Add k8s-upload-profile node type and logic.
[ccsdk/cds.git] / ms / blueprintsprocessor / functions / k8s-profile-upload / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / functions / k8s / profile / upload / K8sProfileUploadComponent.kt
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Modifications Copyright © 2019 IBM.
4  * Modifications Copyright © 2020 Orange.
5  * Modifications Copyright © 2020 Deutsche Telekom AG.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 package org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.profile.upload
21
22 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
23 import org.springframework.beans.factory.config.ConfigurableBeanFactory
24 import org.springframework.context.annotation.Scope
25 import org.springframework.stereotype.Component
26 import java.util.ArrayList
27 import java.nio.file.Paths
28 import java.io.File
29 import com.fasterxml.jackson.databind.JsonNode
30 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode
31 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
32 import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService
33 import org.slf4j.LoggerFactory
34 import java.nio.file.Path
35 import org.onap.ccsdk.cds.controllerblueprints.core.returnNullIfMissing
36 import com.fasterxml.jackson.databind.node.ObjectNode
37 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
38
39 @Component("component-k8s-profile-upload")
40 @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
41 open class K8sProfileUploadComponent(private var bluePrintPropertiesService: BluePrintPropertiesService) :
42
43     AbstractComponentFunction() {
44
45     companion object {
46         const val INPUT_K8S_PROFILE_NAME = "k8s-rb-profile-name"
47         const val INPUT_K8S_DEFINITION_NAME = "k8s-rb-definition-name"
48         const val INPUT_K8S_DEFINITION_VERSION = "k8s-rb-definition-version"
49         const val INPUT_K8S_PROFILE_NAMESPACE = "k8s-rb-profile-namespace"
50         const val INPUT_K8S_PROFILE_SOURCE = "k8s-rb-profile-source"
51         const val INPUT_RESOURCE_ASSIGNMENT_MAP = "resource-assignment-map"
52
53         const val OUTPUT_STATUSES = "statuses"
54         const val OUTPUT_SKIPPED = "skipped"
55         const val OUTPUT_UPLOADED = "uploaded"
56         const val OUTPUT_ERROR = "error"
57     }
58
59     private val log = LoggerFactory.getLogger(K8sProfileUploadComponent::class.java)!!
60
61     override suspend fun processNB(executionRequest: ExecutionServiceInput) {
62         log.info("Triggering K8s Profile Upload component logic.")
63
64         val inputParameterNames = arrayOf(
65             INPUT_K8S_PROFILE_NAME,
66             INPUT_K8S_DEFINITION_NAME,
67             INPUT_K8S_DEFINITION_VERSION,
68             INPUT_K8S_PROFILE_NAMESPACE,
69             INPUT_K8S_PROFILE_SOURCE
70         )
71         var outputPrefixStatuses = mutableMapOf<String, String>()
72         var inputParamsMap = mutableMapOf<String, JsonNode?>()
73
74         inputParameterNames.forEach {
75             inputParamsMap[it] = getOptionalOperationInput(it)?.returnNullIfMissing()
76         }
77
78         log.info("Getting the template prefixes")
79         val prefixList: ArrayList<String> = getTemplatePrefixList(executionRequest)
80
81         log.info("Iterating over prefixes in resource assignment map.")
82         for (prefix in prefixList) {
83             // Prefilling prefix sucess status
84             outputPrefixStatuses.put(prefix, OUTPUT_SKIPPED)
85             // Resource assignment map is organized by prefixes, in each iteraton we work only
86             // on one section of resource assignment map
87             val prefixNode: JsonNode = operationInputs[INPUT_RESOURCE_ASSIGNMENT_MAP]?.get(prefix) ?: continue
88             val assignmentMapPrefix = JacksonUtils.jsonNode(prefixNode.toPrettyString()) as ObjectNode
89
90             // We are copying the map because for each prefix it might be completed with a different data
91             var prefixInputParamsMap = inputParamsMap.toMutableMap()
92             prefixInputParamsMap.forEach { (inputParamName, value) ->
93                 if (value == null) {
94                     val mapValue = assignmentMapPrefix?.get(inputParamName)
95                     log.debug("$inputParamName value was $value so we fetch $mapValue")
96                     prefixInputParamsMap[inputParamName] = mapValue
97                 }
98             }
99
100             // For clarity we pull out the required fields
101             val profileName = prefixInputParamsMap[INPUT_K8S_PROFILE_NAME]?.returnNullIfMissing()?.textValue()
102             val definitionName = prefixInputParamsMap[INPUT_K8S_DEFINITION_NAME]?.returnNullIfMissing()?.textValue()
103             val definitionVersion = prefixInputParamsMap[INPUT_K8S_DEFINITION_VERSION]?.returnNullIfMissing()?.textValue()
104
105             val k8sProfileUploadConfiguration = K8sProfileUploadConfiguration(bluePrintPropertiesService)
106
107             // Creating API connector
108             var api = K8sPluginApi(
109                 k8sProfileUploadConfiguration.getProperties().username,
110                 k8sProfileUploadConfiguration.getProperties().password,
111                 k8sProfileUploadConfiguration.getProperties().url,
112                 definitionName,
113                 definitionVersion
114             )
115
116             if ((profileName == null) || (definitionName == null) || (definitionVersion == null)) {
117                 log.warn("Prefix $prefix does not have required data for us to continue.")
118             } else if (!api.hasDefinition()) {
119                 log.warn("K8s RB Definition ($definitionName/$definitionVersion) not found ")
120             } else if (profileName == "") {
121                 log.warn("K8s rb profile name is empty! Either define profile name to use or choose default")
122             } else if (api.hasProfile(profileName)) {
123                 log.info("Profile Already Existing - skipping upload")
124             } else {
125                 log.info("Uploading K8s Profile..")
126                 outputPrefixStatuses.put(prefix, OUTPUT_ERROR)
127
128                 var profile = K8sProfile()
129                 profile.profileName = profileName
130                 profile.rbName = definitionName
131                 profile.rbVersion = definitionVersion
132                 profile.namespace = prefixInputParamsMap[INPUT_K8S_PROFILE_NAMESPACE]?.textValue()
133                 api.createProfile(profile)
134                 val profileFilePath: Path = prepareProfileFile(profileName)
135                 api.uploadProfileContent(profile, profileFilePath)
136
137                 log.info("K8s Profile Upload Completed")
138                 outputPrefixStatuses.put(prefix, OUTPUT_UPLOADED)
139             }
140         }
141         bluePrintRuntimeService.setNodeTemplateAttributeValue(
142             nodeTemplateName,
143             OUTPUT_STATUSES,
144             outputPrefixStatuses.asJsonNode()
145         )
146     }
147
148     override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
149         bluePrintRuntimeService.getBluePrintError().addError(runtimeException.message!!)
150     }
151
152     fun getTemplatePrefixList(executionRequest: ExecutionServiceInput): ArrayList<String> {
153         val result = ArrayList<String>()
154         for (prefix in executionRequest.payload.get("resource-assignment-request").get("template-prefix").elements())
155             result.add(prefix.asText())
156         return result
157     }
158
159     fun prepareProfileFile(k8sRbProfileName: String): Path {
160         val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
161         val bluePrintBasePath: String = bluePrintContext.rootPath
162         return Paths.get(
163             bluePrintBasePath.plus(File.separator)
164                 .plus("Templates")
165                 .plus(File.separator)
166                 .plus("k8s-profiles")
167                 .plus(File.separator)
168                 .plus("$k8sRbProfileName.tar.gz")
169         )
170     }
171 }