1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00
sap-jenkins-library/cmd/tmsUpload.go
gerstneralex 34fc844ac0
Tms upload fixes (#4204)
* Fix proxy usage in tmsUpload

* Fix no default description if custom description is not provided
2023-03-03 18:23:40 +01:00

293 lines
10 KiB
Go

package cmd
import (
"encoding/json"
"fmt"
"net/url"
"sort"
"strconv"
"github.com/SAP/jenkins-library/pkg/command"
piperHttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/SAP/jenkins-library/pkg/tms"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
)
type uaa struct {
Url string `json:"url"`
ClientId string `json:"clientid"`
ClientSecret string `json:"clientsecret"`
}
type serviceKey struct {
Uaa uaa `json:"uaa"`
Uri string `json:"uri"`
}
type tmsUploadUtils interface {
command.ExecRunner
FileExists(filename string) (bool, error)
FileRead(path string) ([]byte, error)
// Add more methods here, or embed additional interfaces, or remove/replace as required.
// The tmsUploadUtils interface should be descriptive of your runtime dependencies,
// i.e. include everything you need to be able to mock in tests.
// Unit tests shall be executable in parallel (not depend on global state), and don't (re-)test dependencies.
}
type tmsUploadUtilsBundle struct {
*command.Command
*piperutils.Files
// Embed more structs as necessary to implement methods or interfaces you add to tmsUploadUtils.
// Structs embedded in this way must each have a unique set of methods attached.
// If there is no struct which implements the method you need, attach the method to
// tmsUploadUtilsBundle and forward to the implementation of the dependency.
}
func newTmsUploadUtils() tmsUploadUtils {
utils := tmsUploadUtilsBundle{
Command: &command.Command{},
Files: &piperutils.Files{},
}
// Reroute command output to logging framework
utils.Stdout(log.Writer())
utils.Stderr(log.Writer())
return &utils
}
func tmsUpload(config tmsUploadOptions, telemetryData *telemetry.CustomData, influx *tmsUploadInflux) {
// Utils can be used wherever the command.ExecRunner interface is expected.
// It can also be used for example as a mavenExecRunner.
utils := newTmsUploadUtils()
client := &piperHttp.Client{}
proxy := config.Proxy
options := piperHttp.ClientOptions{}
if proxy != "" {
transportProxy, err := url.Parse(proxy)
if err != nil {
log.Entry().WithError(err).Fatalf("Failed to parse proxy string %v into a URL structure", proxy)
}
options = piperHttp.ClientOptions{TransportProxy: transportProxy}
client.SetOptions(options)
if GeneralConfig.Verbose {
log.Entry().Infof("HTTP client instructed to use %v proxy", proxy)
}
}
serviceKey, err := unmarshalServiceKey(config.TmsServiceKey)
if err != nil {
log.Entry().WithError(err).Fatal("Failed to unmarshal TMS service key")
}
if GeneralConfig.Verbose {
log.Entry().Info("Will be used for communication:")
log.Entry().Infof("- client id: %v", serviceKey.Uaa.ClientId)
log.Entry().Infof("- TMS URL: %v", serviceKey.Uri)
log.Entry().Infof("- UAA URL: %v", serviceKey.Uaa.Url)
}
communicationInstance, err := tms.NewCommunicationInstance(client, serviceKey.Uri, serviceKey.Uaa.Url, serviceKey.Uaa.ClientId, serviceKey.Uaa.ClientSecret, GeneralConfig.Verbose, options)
if err != nil {
log.Entry().WithError(err).Fatal("Failed to prepare client for talking with TMS")
}
if err := runTmsUpload(config, communicationInstance, utils); err != nil {
log.Entry().WithError(err).Fatal("Failed to run tmsUpload step")
}
}
func runTmsUpload(config tmsUploadOptions, communicationInstance tms.CommunicationInterface, utils tmsUploadUtils) error {
mtaPath := config.MtaPath
exists, _ := utils.FileExists(mtaPath)
if !exists {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("mta file %s not found", mtaPath)
}
description := tms.DEFAULT_TR_DESCRIPTION
if config.CustomDescription != "" {
description = config.CustomDescription
}
namedUser := config.NamedUser
nodeName := config.NodeName
mtaVersion := config.MtaVersion
nodeNameExtDescriptorMapping := config.NodeExtDescriptorMapping
if GeneralConfig.Verbose {
log.Entry().Info("The step will use the following values:")
log.Entry().Infof("- description: %v", description)
if len(nodeNameExtDescriptorMapping) > 0 {
log.Entry().Infof("- mapping between node names and MTA extension descriptor file paths: %v", nodeNameExtDescriptorMapping)
}
log.Entry().Infof("- MTA path: %v", mtaPath)
log.Entry().Infof("- MTA version: %v", mtaVersion)
if namedUser != "" {
log.Entry().Infof("- named user: %v", namedUser)
}
log.Entry().Infof("- node name: %v", nodeName)
}
if len(nodeNameExtDescriptorMapping) > 0 {
nodes, errGetNodes := communicationInstance.GetNodes()
if errGetNodes != nil {
log.SetErrorCategory(log.ErrorService)
return fmt.Errorf("failed to get nodes: %w", errGetNodes)
}
mtaYamlMap, errGetMtaYamlAsMap := getYamlAsMap(utils, "mta.yaml")
if errGetMtaYamlAsMap != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("failed to get mta.yaml as map: %w", errGetMtaYamlAsMap)
}
_, isIdParameterInMap := mtaYamlMap["ID"]
_, isVersionParameterInMap := mtaYamlMap["version"]
if !isIdParameterInMap || !isVersionParameterInMap {
var errorMessage string
if !isIdParameterInMap {
errorMessage += "parameter 'ID' is not found in mta.yaml\n"
}
if !isVersionParameterInMap {
errorMessage += "parameter 'version' is not found in mta.yaml\n"
}
log.SetErrorCategory(log.ErrorConfiguration)
return errors.New(errorMessage)
}
// validate the whole mapping and then throw errors together, so that user can get them after a single pipeline run
nodeIdExtDescriptorMapping, errGetNodeIdExtDescriptorMapping := formNodeIdExtDescriptorMappingWithValidation(utils, nodeNameExtDescriptorMapping, nodes, mtaYamlMap, mtaVersion)
if errGetNodeIdExtDescriptorMapping != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errGetNodeIdExtDescriptorMapping
}
for nodeId, mtaExtDescriptorPath := range nodeIdExtDescriptorMapping {
obtainedMtaExtDescriptor, errGetMtaExtDescriptor := communicationInstance.GetMtaExtDescriptor(nodeId, fmt.Sprintf("%v", mtaYamlMap["ID"]), mtaVersion)
if errGetMtaExtDescriptor != nil {
log.SetErrorCategory(log.ErrorService)
return fmt.Errorf("failed to get MTA extension descriptor: %w", errGetMtaExtDescriptor)
}
if obtainedMtaExtDescriptor != (tms.MtaExtDescriptor{}) {
_, errUpdateMtaExtDescriptor := communicationInstance.UpdateMtaExtDescriptor(nodeId, obtainedMtaExtDescriptor.Id, mtaExtDescriptorPath, mtaVersion, description, namedUser)
if errUpdateMtaExtDescriptor != nil {
log.SetErrorCategory(log.ErrorService)
return fmt.Errorf("failed to update MTA extension descriptor: %w", errUpdateMtaExtDescriptor)
}
} else {
_, errUploadMtaExtDescriptor := communicationInstance.UploadMtaExtDescriptorToNode(nodeId, mtaExtDescriptorPath, mtaVersion, description, namedUser)
if errUploadMtaExtDescriptor != nil {
log.SetErrorCategory(log.ErrorService)
return fmt.Errorf("failed to upload MTA extension descriptor to node: %w", errUploadMtaExtDescriptor)
}
}
}
}
fileInfo, errUploadFile := communicationInstance.UploadFile(mtaPath, namedUser)
if errUploadFile != nil {
log.SetErrorCategory(log.ErrorService)
return fmt.Errorf("failed to upload file: %w", errUploadFile)
}
_, errUploadFileToNode := communicationInstance.UploadFileToNode(nodeName, strconv.FormatInt(fileInfo.Id, 10), description, namedUser)
if errUploadFileToNode != nil {
log.SetErrorCategory(log.ErrorService)
return fmt.Errorf("failed to upload file to node: %w", errUploadFileToNode)
}
return nil
}
func formNodeIdExtDescriptorMappingWithValidation(utils tmsUploadUtils, nodeNameExtDescriptorMapping map[string]interface{}, nodes []tms.Node, mtaYamlMap map[string]interface{}, mtaVersion string) (map[int64]string, error) {
var wrongMtaIdExtDescriptors []string
var wrongExtDescriptorPaths []string
var wrongNodeNames []string
var errorMessage string
nodeIdExtDescriptorMapping := make(map[int64]string)
for nodeName, mappedValue := range nodeNameExtDescriptorMapping {
mappedValueString := fmt.Sprintf("%v", mappedValue)
exists, _ := utils.FileExists(mappedValueString)
if exists {
extDescriptorMap, errGetYamlAsMap := getYamlAsMap(utils, mappedValueString)
if errGetYamlAsMap == nil {
if fmt.Sprintf("%v", mtaYamlMap["ID"]) != fmt.Sprintf("%v", extDescriptorMap["extends"]) {
wrongMtaIdExtDescriptors = append(wrongMtaIdExtDescriptors, mappedValueString)
}
} else {
wrappedErr := errors.Wrapf(errGetYamlAsMap, "tried to parse %v as yaml, but got an error", mappedValueString)
errorMessage += fmt.Sprintf("%v\n", wrappedErr)
}
} else {
wrongExtDescriptorPaths = append(wrongExtDescriptorPaths, mappedValueString)
}
isNodeFound := false
for _, node := range nodes {
if node.Name == nodeName {
nodeIdExtDescriptorMapping[node.Id] = mappedValueString
isNodeFound = true
break
}
}
if !isNodeFound {
wrongNodeNames = append(wrongNodeNames, nodeName)
}
}
if mtaVersion != "*" && mtaVersion != mtaYamlMap["version"] {
errorMessage += "parameter 'mtaVersion' does not match the MTA version in mta.yaml\n"
}
if len(wrongMtaIdExtDescriptors) > 0 || len(wrongExtDescriptorPaths) > 0 || len(wrongNodeNames) > 0 {
if len(wrongMtaIdExtDescriptors) > 0 {
sort.Strings(wrongMtaIdExtDescriptors)
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)
}
if len(wrongExtDescriptorPaths) > 0 {
sort.Strings(wrongExtDescriptorPaths)
errorMessage += fmt.Sprintf("MTA extension descriptor files %v do not exist\n", wrongExtDescriptorPaths)
}
if len(wrongNodeNames) > 0 {
sort.Strings(wrongNodeNames)
errorMessage += fmt.Sprintf("nodes %v do not exist. Please check node names provided in 'nodeExtDescriptorMapping' parameter or create these nodes\n", wrongNodeNames)
}
}
if errorMessage == "" {
return nodeIdExtDescriptorMapping, nil
} else {
return nil, errors.New(errorMessage)
}
}
func getYamlAsMap(utils tmsUploadUtils, yamlPath string) (map[string]interface{}, error) {
var result map[string]interface{}
bytes, err := utils.FileRead(yamlPath)
if err != nil {
return result, err
}
err = yaml.Unmarshal(bytes, &result)
if err != nil {
return result, err
}
return result, nil
}
func unmarshalServiceKey(serviceKeyJson string) (serviceKey serviceKey, err error) {
err = json.Unmarshal([]byte(serviceKeyJson), &serviceKey)
if err != nil {
return
}
return
}