github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/tms/tmsUtils.go (about) 1 package tms 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/url" 7 "sort" 8 9 "github.com/SAP/jenkins-library/pkg/command" 10 piperHttp "github.com/SAP/jenkins-library/pkg/http" 11 "github.com/SAP/jenkins-library/pkg/log" 12 "github.com/SAP/jenkins-library/pkg/piperutils" 13 "github.com/ghodss/yaml" 14 "github.com/pkg/errors" 15 "github.com/sirupsen/logrus" 16 ) 17 18 type TmsUtils interface { 19 command.ExecRunner 20 FileExists(filename string) (bool, error) 21 FileRead(path string) ([]byte, error) 22 } 23 24 type uaa struct { 25 Url string `json:"url"` 26 ClientId string `json:"clientid"` 27 ClientSecret string `json:"clientsecret"` 28 } 29 30 type serviceKey struct { 31 Uaa uaa `json:"uaa"` 32 Uri string `json:"uri"` 33 } 34 35 type CommunicationInstance struct { 36 tmsUrl string 37 uaaUrl string 38 clientId string 39 clientSecret string 40 httpClient piperHttp.Uploader 41 logger *logrus.Entry 42 isVerbose bool 43 } 44 45 type Node struct { 46 Id int64 `json:"id"` 47 Name string `json:"name"` 48 } 49 50 type nodes struct { 51 Nodes []Node `json:"nodes"` 52 } 53 54 type MtaExtDescriptor struct { 55 Id int64 `json:"id"` 56 Description string `json:"description"` 57 MtaId string `json:"mtaId"` 58 MtaExtId string `json:"mtaExtId"` 59 MtaVersion string `json:"mtaVersion"` 60 LastChangedAt string `json:"lastChangedAt"` 61 } 62 63 type mtaExtDescriptors struct { 64 MtaExtDescriptors []MtaExtDescriptor `json:"mtaExtDescriptors"` 65 } 66 67 type FileInfo struct { 68 Id int64 `json:"fileId"` 69 Name string `json:"fileName"` 70 } 71 72 type NodeUploadResponseEntity struct { 73 TransportRequestId int64 `json:"transportRequestId"` 74 TransportRequestDescription string `json:"transportRequestDescription"` 75 QueueEntries []QueueEntry `json:"queueEntries"` 76 } 77 78 type QueueEntry struct { 79 Id int64 `json:"queueId"` 80 NodeId int64 `json:"nodeId"` 81 NodeName string `json:"nodeName"` 82 } 83 84 type NodeUploadRequestEntity struct { 85 ContentType string `json:"contentType"` 86 StorageType string `json:"storageType"` 87 NodeName string `json:"nodeName"` 88 Description string `json:"description"` 89 NamedUser string `json:"namedUser"` 90 Entries []Entry `json:"entries"` 91 } 92 93 type Entry struct { 94 Uri string `json:"uri"` 95 } 96 97 type CommunicationInterface interface { 98 GetNodes() ([]Node, error) 99 GetMtaExtDescriptor(nodeId int64, mtaId, mtaVersion string) (MtaExtDescriptor, error) 100 UpdateMtaExtDescriptor(nodeId, idOfMtaExtDescriptor int64, file, mtaVersion, description, namedUser string) (MtaExtDescriptor, error) 101 UploadMtaExtDescriptorToNode(nodeId int64, file, mtaVersion, description, namedUser string) (MtaExtDescriptor, error) 102 UploadFile(file, namedUser string) (FileInfo, error) 103 UploadFileToNode(fileInfo FileInfo, nodeName, description, namedUser string) (NodeUploadResponseEntity, error) 104 ExportFileToNode(fileInfo FileInfo, nodeName, description, namedUser string) (NodeUploadResponseEntity, error) 105 } 106 107 type Options struct { 108 TmsServiceKey string `json:"tmsServiceKey,omitempty"` 109 CustomDescription string `json:"customDescription,omitempty"` 110 NamedUser string `json:"namedUser,omitempty"` 111 NodeName string `json:"nodeName,omitempty"` 112 MtaPath string `json:"mtaPath,omitempty"` 113 MtaVersion string `json:"mtaVersion,omitempty"` 114 NodeExtDescriptorMapping map[string]interface{} `json:"nodeExtDescriptorMapping,omitempty"` 115 Proxy string `json:"proxy,omitempty"` 116 StashContent []string `json:"stashContent,omitempty"` 117 Verbose bool 118 } 119 120 type tmsUtilsBundle struct { 121 *command.Command 122 *piperutils.Files 123 } 124 125 const DEFAULT_TR_DESCRIPTION = "Created by Piper" 126 127 func NewTmsUtils() TmsUtils { 128 utils := tmsUtilsBundle{ 129 Command: &command.Command{}, 130 Files: &piperutils.Files{}, 131 } 132 // Reroute command output to logging framework 133 utils.Stdout(log.Writer()) 134 utils.Stderr(log.Writer()) 135 return &utils 136 } 137 138 func unmarshalServiceKey(serviceKeyJson string) (serviceKey serviceKey, err error) { 139 err = json.Unmarshal([]byte(serviceKeyJson), &serviceKey) 140 if err != nil { 141 return 142 } 143 return 144 } 145 146 func FormNodeIdExtDescriptorMappingWithValidation(utils TmsUtils, nodeNameExtDescriptorMapping map[string]interface{}, nodes []Node, mtaYamlMap map[string]interface{}, mtaVersion string) (map[int64]string, error) { 147 var wrongMtaIdExtDescriptors []string 148 var wrongExtDescriptorPaths []string 149 var wrongNodeNames []string 150 var errorMessage string 151 152 nodeIdExtDescriptorMapping := make(map[int64]string) 153 for nodeName, mappedValue := range nodeNameExtDescriptorMapping { 154 mappedValueString := fmt.Sprintf("%v", mappedValue) 155 exists, _ := utils.FileExists(mappedValueString) 156 if exists { 157 extDescriptorMap, errGetYamlAsMap := GetYamlAsMap(utils, mappedValueString) 158 if errGetYamlAsMap == nil { 159 if fmt.Sprintf("%v", mtaYamlMap["ID"]) != fmt.Sprintf("%v", extDescriptorMap["extends"]) { 160 wrongMtaIdExtDescriptors = append(wrongMtaIdExtDescriptors, mappedValueString) 161 } 162 } else { 163 wrappedErr := errors.Wrapf(errGetYamlAsMap, "tried to parse %v as yaml, but got an error", mappedValueString) 164 errorMessage += fmt.Sprintf("%v\n", wrappedErr) 165 } 166 } else { 167 wrongExtDescriptorPaths = append(wrongExtDescriptorPaths, mappedValueString) 168 } 169 170 isNodeFound := false 171 for _, node := range nodes { 172 if node.Name == nodeName { 173 nodeIdExtDescriptorMapping[node.Id] = mappedValueString 174 isNodeFound = true 175 break 176 } 177 } 178 if !isNodeFound { 179 wrongNodeNames = append(wrongNodeNames, nodeName) 180 } 181 } 182 183 if mtaVersion != "*" && mtaVersion != mtaYamlMap["version"] { 184 errorMessage += "parameter 'mtaVersion' does not match the MTA version in mta.yaml\n" 185 } 186 187 if len(wrongMtaIdExtDescriptors) > 0 || len(wrongExtDescriptorPaths) > 0 || len(wrongNodeNames) > 0 { 188 if len(wrongMtaIdExtDescriptors) > 0 { 189 sort.Strings(wrongMtaIdExtDescriptors) 190 errorMessage += fmt.Sprintf("parameter 'extends' in MTA extension descriptor files %v is not the same as MTA ID or is missing at all\n", wrongMtaIdExtDescriptors) 191 } 192 if len(wrongExtDescriptorPaths) > 0 { 193 sort.Strings(wrongExtDescriptorPaths) 194 errorMessage += fmt.Sprintf("MTA extension descriptor files %v do not exist\n", wrongExtDescriptorPaths) 195 } 196 if len(wrongNodeNames) > 0 { 197 sort.Strings(wrongNodeNames) 198 errorMessage += fmt.Sprintf("nodes %v do not exist. Please check node names provided in 'nodeExtDescriptorMapping' parameter or create these nodes\n", wrongNodeNames) 199 } 200 } 201 202 if errorMessage == "" { 203 return nodeIdExtDescriptorMapping, nil 204 } else { 205 return nil, errors.New(errorMessage) 206 } 207 } 208 209 func GetYamlAsMap(utils TmsUtils, yamlPath string) (map[string]interface{}, error) { 210 var result map[string]interface{} 211 bytes, err := utils.FileRead(yamlPath) 212 if err != nil { 213 return result, err 214 } 215 err = yaml.Unmarshal(bytes, &result) 216 if err != nil { 217 return result, err 218 } 219 220 return result, nil 221 } 222 223 func SetupCommunication(config Options) (communicationInstance CommunicationInterface) { 224 client := &piperHttp.Client{} 225 proxy := config.Proxy 226 options := piperHttp.ClientOptions{} 227 if proxy != "" { 228 transportProxy, err := url.Parse(proxy) 229 if err != nil { 230 log.Entry().WithError(err).Fatalf("Failed to parse proxy string %v into a URL structure", proxy) 231 } 232 233 options = piperHttp.ClientOptions{TransportProxy: transportProxy} 234 client.SetOptions(options) 235 if config.Verbose { 236 log.Entry().Infof("HTTP client instructed to use %v proxy", proxy) 237 } 238 } 239 240 serviceKey, err := unmarshalServiceKey(config.TmsServiceKey) 241 if err != nil { 242 log.Entry().WithError(err).Fatal("Failed to unmarshal TMS service key") 243 } 244 log.RegisterSecret(serviceKey.Uaa.ClientSecret) 245 246 if config.Verbose { 247 log.Entry().Info("Will be used for communication:") 248 log.Entry().Infof("- client id: %v", serviceKey.Uaa.ClientId) 249 log.Entry().Infof("- TMS URL: %v", serviceKey.Uri) 250 log.Entry().Infof("- UAA URL: %v", serviceKey.Uaa.Url) 251 } 252 253 commuInstance, err := NewCommunicationInstance(client, serviceKey.Uri, serviceKey.Uaa.Url, serviceKey.Uaa.ClientId, serviceKey.Uaa.ClientSecret, config.Verbose, options) 254 if err != nil { 255 log.Entry().WithError(err).Fatal("Failed to prepare client for talking with TMS") 256 } 257 return commuInstance 258 } 259 260 func UploadDescriptors(config Options, communicationInstance CommunicationInterface, utils TmsUtils) error { 261 description := config.CustomDescription 262 namedUser := config.NamedUser 263 nodeName := config.NodeName 264 mtaVersion := config.MtaVersion 265 nodeNameExtDescriptorMapping := config.NodeExtDescriptorMapping 266 mtaPath := config.MtaPath 267 268 if config.Verbose { 269 log.Entry().Info("The step will use the following values:") 270 log.Entry().Infof("- description: %v", config.CustomDescription) 271 272 if len(nodeNameExtDescriptorMapping) > 0 { 273 log.Entry().Infof("- mapping between node names and MTA extension descriptor file paths: %v", nodeNameExtDescriptorMapping) 274 } 275 log.Entry().Infof("- MTA path: %v", mtaPath) 276 log.Entry().Infof("- MTA version: %v", mtaVersion) 277 if namedUser != "" { 278 log.Entry().Infof("- named user: %v", namedUser) 279 } 280 log.Entry().Infof("- node name: %v", nodeName) 281 } 282 283 if len(nodeNameExtDescriptorMapping) > 0 { 284 nodes, errGetNodes := communicationInstance.GetNodes() 285 if errGetNodes != nil { 286 log.SetErrorCategory(log.ErrorService) 287 return fmt.Errorf("failed to get nodes: %w", errGetNodes) 288 } 289 290 mtaYamlMap, errGetMtaYamlAsMap := GetYamlAsMap(utils, "mta.yaml") 291 if errGetMtaYamlAsMap != nil { 292 log.SetErrorCategory(log.ErrorConfiguration) 293 return fmt.Errorf("failed to get mta.yaml as map: %w", errGetMtaYamlAsMap) 294 } 295 _, isIdParameterInMap := mtaYamlMap["ID"] 296 _, isVersionParameterInMap := mtaYamlMap["version"] 297 if !isIdParameterInMap || !isVersionParameterInMap { 298 var errorMessage string 299 if !isIdParameterInMap { 300 errorMessage += "parameter 'ID' is not found in mta.yaml\n" 301 } 302 if !isVersionParameterInMap { 303 errorMessage += "parameter 'version' is not found in mta.yaml\n" 304 } 305 log.SetErrorCategory(log.ErrorConfiguration) 306 return errors.New(errorMessage) 307 } 308 309 // validate the whole mapping and then throw errors together, so that user can get them after a single pipeline run 310 nodeIdExtDescriptorMapping, errGetNodeIdExtDescriptorMapping := FormNodeIdExtDescriptorMappingWithValidation(utils, nodeNameExtDescriptorMapping, nodes, mtaYamlMap, mtaVersion) 311 if errGetNodeIdExtDescriptorMapping != nil { 312 log.SetErrorCategory(log.ErrorConfiguration) 313 return errGetNodeIdExtDescriptorMapping 314 } 315 316 for nodeId, mtaExtDescriptorPath := range nodeIdExtDescriptorMapping { 317 obtainedMtaExtDescriptor, errGetMtaExtDescriptor := communicationInstance.GetMtaExtDescriptor(nodeId, fmt.Sprintf("%v", mtaYamlMap["ID"]), mtaVersion) 318 if errGetMtaExtDescriptor != nil { 319 log.SetErrorCategory(log.ErrorService) 320 return fmt.Errorf("failed to get MTA extension descriptor: %w", errGetMtaExtDescriptor) 321 } 322 323 if obtainedMtaExtDescriptor != (MtaExtDescriptor{}) { 324 _, errUpdateMtaExtDescriptor := communicationInstance.UpdateMtaExtDescriptor(nodeId, obtainedMtaExtDescriptor.Id, mtaExtDescriptorPath, mtaVersion, description, namedUser) 325 if errUpdateMtaExtDescriptor != nil { 326 log.SetErrorCategory(log.ErrorService) 327 return fmt.Errorf("failed to update MTA extension descriptor: %w", errUpdateMtaExtDescriptor) 328 } 329 } else { 330 _, errUploadMtaExtDescriptor := communicationInstance.UploadMtaExtDescriptorToNode(nodeId, mtaExtDescriptorPath, mtaVersion, description, namedUser) 331 if errUploadMtaExtDescriptor != nil { 332 log.SetErrorCategory(log.ErrorService) 333 return fmt.Errorf("failed to upload MTA extension descriptor to node: %w", errUploadMtaExtDescriptor) 334 } 335 } 336 } 337 } 338 return nil 339 } 340 341 func UploadFile(config Options, communicationInstance CommunicationInterface, utils TmsUtils) (FileInfo, error) { 342 var fileInfo FileInfo 343 344 mtaPath := config.MtaPath 345 exists, _ := utils.FileExists(mtaPath) 346 if !exists { 347 log.SetErrorCategory(log.ErrorConfiguration) 348 return fileInfo, fmt.Errorf("mta file %s not found", mtaPath) 349 } 350 351 fileInfo, errUploadFile := communicationInstance.UploadFile(mtaPath, config.NamedUser) 352 if errUploadFile != nil { 353 log.SetErrorCategory(log.ErrorService) 354 return fileInfo, fmt.Errorf("failed to upload file: %w", errUploadFile) 355 } 356 357 return fileInfo, nil 358 }