1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-30 05:59:39 +02:00
sap-jenkins-library/pkg/tms/tmsClient.go
Artem Bannikov e11896c5b2
[tmsUpload, tmsExport] Minor: remove confusing comments (#4640)
* Remove confusing comments in tmsClient.go
2023-10-24 11:43:37 +02:00

370 lines
15 KiB
Go

package tms
import (
"bytes"
b64 "encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
piperHttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/xsuaa"
"github.com/pkg/errors"
)
// NewCommunicationInstance returns CommunicationInstance structure with http client prepared for communication with TMS backend
func NewCommunicationInstance(httpClient piperHttp.Uploader, tmsUrl, uaaUrl, clientId, clientSecret string, isVerbose bool, clientOptions piperHttp.ClientOptions) (*CommunicationInstance, error) {
logger := log.Entry().WithField("package", "SAP/jenkins-library/pkg/tms")
communicationInstance := &CommunicationInstance{
tmsUrl: tmsUrl,
uaaUrl: uaaUrl,
clientId: clientId,
clientSecret: clientSecret,
httpClient: httpClient,
logger: logger,
isVerbose: isVerbose,
}
token, err := communicationInstance.getOAuthToken()
if err != nil {
return communicationInstance, errors.Wrap(err, "Error fetching OAuth token")
}
clientOptions.Token = token
communicationInstance.httpClient.SetOptions(clientOptions)
return communicationInstance, nil
}
func (communicationInstance *CommunicationInstance) getOAuthToken() (string, error) {
if communicationInstance.isVerbose {
communicationInstance.logger.Info("OAuth token retrieval started")
communicationInstance.logger.Infof("uaaUrl: %v, clientId: %v", communicationInstance.uaaUrl, communicationInstance.clientId)
}
encodedUsernameColonPassword := b64.StdEncoding.EncodeToString([]byte(communicationInstance.clientId + ":" + communicationInstance.clientSecret))
log.RegisterSecret(encodedUsernameColonPassword)
header := http.Header{}
header.Add("Content-Type", "application/x-www-form-urlencoded")
header.Add("Authorization", "Basic "+encodedUsernameColonPassword)
urlFormData := url.Values{
"username": {communicationInstance.clientId},
"password": {communicationInstance.clientSecret},
"grant_type": {"password"},
}
data, err := sendRequest(communicationInstance, http.MethodPost, "/oauth/token/?grant_type=client_credentials&response_type=token", strings.NewReader(urlFormData.Encode()), header, http.StatusOK, true)
if err != nil {
return "", err
}
var token xsuaa.AuthToken
json.Unmarshal(data, &token)
if communicationInstance.isVerbose {
communicationInstance.logger.Info("OAuth Token retrieved successfully")
}
log.RegisterSecret(token.AccessToken)
return token.TokenType + " " + token.AccessToken, nil
}
func sendRequest(communicationInstance *CommunicationInstance, method, urlPathAndQuery string, body io.Reader, header http.Header, expectedStatusCode int, isTowardsUaa bool) ([]byte, error) {
var requestBody io.Reader
if body != nil {
closer := io.NopCloser(body)
bodyBytes, _ := io.ReadAll(closer)
requestBody = bytes.NewBuffer(bodyBytes)
defer closer.Close()
}
url := communicationInstance.tmsUrl
if isTowardsUaa {
url = communicationInstance.uaaUrl
}
url = strings.TrimSuffix(url, "/")
response, err := communicationInstance.httpClient.SendRequest(method, fmt.Sprintf("%v%v", url, urlPathAndQuery), requestBody, header, nil)
// err is not nil for HTTP status codes >= 300
if err != nil {
communicationInstance.logger.Errorf("HTTP request failed with error: %s", err)
communicationInstance.logResponseBody(response)
return nil, err
}
if response.StatusCode != expectedStatusCode {
return nil, fmt.Errorf("unexpected positive HTTP status code %v, while it was expected %v", response.StatusCode, expectedStatusCode)
}
data, _ := io.ReadAll(response.Body)
if !isTowardsUaa && communicationInstance.isVerbose {
communicationInstance.logger.Debugf("Valid response body: %v", string(data))
}
defer response.Body.Close()
return data, nil
}
func (communicationInstance *CommunicationInstance) logResponseBody(response *http.Response) {
if response != nil && response.Body != nil {
data, _ := io.ReadAll(response.Body)
communicationInstance.logger.Errorf("Response body: %s", data)
response.Body.Close()
}
}
func (communicationInstance *CommunicationInstance) GetNodes() ([]Node, error) {
if communicationInstance.isVerbose {
communicationInstance.logger.Info("Obtaining nodes started")
communicationInstance.logger.Infof("tmsUrl: %v", communicationInstance.tmsUrl)
}
header := http.Header{}
header.Add("Content-Type", "application/json")
var aNodes []Node
var data []byte
data, err := sendRequest(communicationInstance, http.MethodGet, "/v2/nodes", nil, header, http.StatusOK, false)
if err != nil {
return aNodes, err
}
var getNodesResponse nodes
json.Unmarshal(data, &getNodesResponse)
aNodes = getNodesResponse.Nodes
if communicationInstance.isVerbose {
communicationInstance.logger.Info("Nodes obtained successfully")
}
return aNodes, nil
}
func (communicationInstance *CommunicationInstance) GetMtaExtDescriptor(nodeId int64, mtaId, mtaVersion string) (MtaExtDescriptor, error) {
if communicationInstance.isVerbose {
communicationInstance.logger.Info("Get MTA extension descriptor started")
communicationInstance.logger.Infof("tmsUrl: %v, nodeId: %v, mtaId: %v, mtaVersion: %v", communicationInstance.tmsUrl, nodeId, mtaId, mtaVersion)
}
header := http.Header{}
header.Add("Content-Type", "application/json")
var mtaExtDescriptor MtaExtDescriptor
var data []byte
data, err := sendRequest(communicationInstance, http.MethodGet, fmt.Sprintf("/v2/nodes/%v/mtaExtDescriptors?mtaId=%v&mtaVersion=%v", nodeId, mtaId, mtaVersion), nil, header, http.StatusOK, false)
if err != nil {
return mtaExtDescriptor, err
}
var getMtaExtDescriptorsResponse mtaExtDescriptors
json.Unmarshal(data, &getMtaExtDescriptorsResponse)
if len(getMtaExtDescriptorsResponse.MtaExtDescriptors) > 0 {
mtaExtDescriptor = getMtaExtDescriptorsResponse.MtaExtDescriptors[0]
}
if communicationInstance.isVerbose {
if mtaExtDescriptor != (MtaExtDescriptor{}) {
communicationInstance.logger.Info("MTA extension descriptor obtained successfully")
} else {
communicationInstance.logger.Warn("No MTA extension descriptor found")
}
}
return mtaExtDescriptor, nil
}
func (communicationInstance *CommunicationInstance) UploadFileToNode(fileInfo FileInfo, nodeName, description, namedUser string) (NodeUploadResponseEntity, error) {
fileId := strconv.FormatInt(fileInfo.Id, 10)
if communicationInstance.isVerbose {
communicationInstance.logger.Info("Node upload started")
communicationInstance.logger.Infof("tmsUrl: %v, nodeName: %v, fileId: %v, description: %v, namedUser: %v", communicationInstance.tmsUrl, nodeName, fileId, description, namedUser)
}
header := http.Header{}
header.Add("Content-Type", "application/json")
var nodeUploadResponseEntity NodeUploadResponseEntity
entry := Entry{Uri: fileId}
body := NodeUploadRequestEntity{ContentType: "MTA", StorageType: "FILE", NodeName: nodeName, Description: description, NamedUser: namedUser, Entries: []Entry{entry}}
bodyBytes, errMarshaling := json.Marshal(body)
if errMarshaling != nil {
return nodeUploadResponseEntity, errors.Wrapf(errMarshaling, "unable to marshal request body %v", body)
}
data, errSendRequest := sendRequest(communicationInstance, http.MethodPost, "/v2/nodes/upload", bytes.NewReader(bodyBytes), header, http.StatusOK, false)
if errSendRequest != nil {
return nodeUploadResponseEntity, errSendRequest
}
json.Unmarshal(data, &nodeUploadResponseEntity)
communicationInstance.logger.Info("Node upload executed successfully")
communicationInstance.logger.Infof("nodeName: %v, nodeId: %v, uploadedFile: %v, createdTransportRequestDescription: %v, createdTransportRequestId: %v", nodeUploadResponseEntity.QueueEntries[0].NodeName, nodeUploadResponseEntity.QueueEntries[0].NodeId, fileInfo.Name, nodeUploadResponseEntity.TransportRequestDescription, nodeUploadResponseEntity.TransportRequestId)
return nodeUploadResponseEntity, nil
}
func (communicationInstance *CommunicationInstance) ExportFileToNode(fileInfo FileInfo, nodeName, description, namedUser string) (NodeUploadResponseEntity, error) {
fileId := strconv.FormatInt(fileInfo.Id, 10)
if communicationInstance.isVerbose {
communicationInstance.logger.Info("Node export started")
communicationInstance.logger.Infof("tmsUrl: %v, nodeName: %v, fileId: %v, description: %v, namedUser: %v", communicationInstance.tmsUrl, nodeName, fileId, description, namedUser)
}
header := http.Header{}
header.Add("Content-Type", "application/json")
var nodeUploadResponseEntity NodeUploadResponseEntity
entry := Entry{Uri: fileId}
body := NodeUploadRequestEntity{ContentType: "MTA", StorageType: "FILE", NodeName: nodeName, Description: description, NamedUser: namedUser, Entries: []Entry{entry}}
bodyBytes, errMarshaling := json.Marshal(body)
if errMarshaling != nil {
return nodeUploadResponseEntity, errors.Wrapf(errMarshaling, "unable to marshal request body %v", body)
}
data, errSendRequest := sendRequest(communicationInstance, http.MethodPost, "/v2/nodes/export", bytes.NewReader(bodyBytes), header, http.StatusOK, false)
if errSendRequest != nil {
return nodeUploadResponseEntity, errSendRequest
}
json.Unmarshal(data, &nodeUploadResponseEntity)
communicationInstance.logger.Info("Node export executed successfully")
communicationInstance.logger.Infof("nodeName: %v, nodeId: %v, uploadedFile: %v, createdTransportRequestDescription: %v, createdTransportRequestId: %v", nodeUploadResponseEntity.QueueEntries[0].NodeName, nodeUploadResponseEntity.QueueEntries[0].NodeId, fileInfo.Name, nodeUploadResponseEntity.TransportRequestDescription, nodeUploadResponseEntity.TransportRequestId)
return nodeUploadResponseEntity, nil
}
func (communicationInstance *CommunicationInstance) UpdateMtaExtDescriptor(nodeId, idOfMtaExtDescriptor int64, file, mtaVersion, description, namedUser string) (MtaExtDescriptor, error) {
if communicationInstance.isVerbose {
communicationInstance.logger.Info("Update of MTA extension descriptor started")
communicationInstance.logger.Infof("tmsUrl: %v, nodeId: %v, mtaExtDescriptorId: %v, file: %v, mtaVersion: %v, description: %v, namedUser: %v", communicationInstance.tmsUrl, nodeId, idOfMtaExtDescriptor, file, mtaVersion, description, namedUser)
}
header := http.Header{}
header.Add("tms-named-user", namedUser)
tmsUrl := strings.TrimSuffix(communicationInstance.tmsUrl, "/")
url := fmt.Sprintf("%v/v2/nodes/%v/mtaExtDescriptors/%v", tmsUrl, nodeId, idOfMtaExtDescriptor)
formFields := map[string]string{"mtaVersion": mtaVersion, "description": description}
var mtaExtDescriptor MtaExtDescriptor
fileHandle, errOpenFile := os.Open(file)
if errOpenFile != nil {
return mtaExtDescriptor, errors.Wrapf(errOpenFile, "unable to locate file %v", file)
}
defer fileHandle.Close()
uploadRequestData := piperHttp.UploadRequestData{Method: http.MethodPut, URL: url, File: file, FileFieldName: "file", FormFields: formFields, FileContent: fileHandle, Header: header, Cookies: nil}
var data []byte
data, errUpload := upload(communicationInstance, uploadRequestData, http.StatusOK)
if errUpload != nil {
return mtaExtDescriptor, errUpload
}
json.Unmarshal(data, &mtaExtDescriptor)
if communicationInstance.isVerbose {
communicationInstance.logger.Info("MTA extension descriptor updated successfully")
}
return mtaExtDescriptor, nil
}
func (communicationInstance *CommunicationInstance) UploadMtaExtDescriptorToNode(nodeId int64, file, mtaVersion, description, namedUser string) (MtaExtDescriptor, error) {
if communicationInstance.isVerbose {
communicationInstance.logger.Info("Upload of MTA extension descriptor started")
communicationInstance.logger.Infof("tmsUrl: %v, nodeId: %v, file: %v, mtaVersion: %v, description: %v, namedUser: %v", communicationInstance.tmsUrl, nodeId, file, mtaVersion, description, namedUser)
}
header := http.Header{}
header.Add("tms-named-user", namedUser)
tmsUrl := strings.TrimSuffix(communicationInstance.tmsUrl, "/")
url := fmt.Sprintf("%v/v2/nodes/%v/mtaExtDescriptors", tmsUrl, nodeId)
formFields := map[string]string{"mtaVersion": mtaVersion, "description": description}
var mtaExtDescriptor MtaExtDescriptor
fileHandle, errOpenFile := os.Open(file)
if errOpenFile != nil {
return mtaExtDescriptor, errors.Wrapf(errOpenFile, "unable to locate file %v", file)
}
defer fileHandle.Close()
uploadRequestData := piperHttp.UploadRequestData{Method: http.MethodPost, URL: url, File: file, FileFieldName: "file", FormFields: formFields, FileContent: fileHandle, Header: header, Cookies: nil}
var data []byte
data, errUpload := upload(communicationInstance, uploadRequestData, http.StatusCreated)
if errUpload != nil {
return mtaExtDescriptor, errUpload
}
json.Unmarshal(data, &mtaExtDescriptor)
if communicationInstance.isVerbose {
communicationInstance.logger.Info("MTA extension descriptor uploaded successfully")
}
return mtaExtDescriptor, nil
}
func (communicationInstance *CommunicationInstance) UploadFile(file, namedUser string) (FileInfo, error) {
if communicationInstance.isVerbose {
communicationInstance.logger.Info("Upload of file started")
communicationInstance.logger.Infof("tmsUrl: %v, file: %v, namedUser: %v", communicationInstance.tmsUrl, file, namedUser)
}
tmsUrl := strings.TrimSuffix(communicationInstance.tmsUrl, "/")
url := fmt.Sprintf("%v/v2/files/upload", tmsUrl)
formFields := map[string]string{"namedUser": namedUser}
var fileInfo FileInfo
fileHandle, errOpenFile := os.Open(file)
if errOpenFile != nil {
return fileInfo, errors.Wrapf(errOpenFile, "unable to locate file %v", file)
}
defer fileHandle.Close()
uploadRequestData := piperHttp.UploadRequestData{Method: http.MethodPost, URL: url, File: file, FileFieldName: "file", FormFields: formFields, FileContent: fileHandle, Header: http.Header{}, Cookies: nil}
var data []byte
data, errUpload := upload(communicationInstance, uploadRequestData, http.StatusCreated)
if errUpload != nil {
return fileInfo, errUpload
}
json.Unmarshal(data, &fileInfo)
if communicationInstance.isVerbose {
communicationInstance.logger.Info("File uploaded successfully")
}
return fileInfo, nil
}
func upload(communicationInstance *CommunicationInstance, uploadRequestData piperHttp.UploadRequestData, expectedStatusCode int) ([]byte, error) {
response, err := communicationInstance.httpClient.Upload(uploadRequestData)
// err is not nil for HTTP status codes >= 300
if err != nil {
communicationInstance.logger.Errorf("HTTP request failed with error: %s", err)
communicationInstance.logResponseBody(response)
return nil, err
}
if response.StatusCode != expectedStatusCode {
return nil, fmt.Errorf("unexpected positive HTTP status code %v, while it was expected %v", response.StatusCode, expectedStatusCode)
}
data, _ := io.ReadAll(response.Body)
if communicationInstance.isVerbose {
communicationInstance.logger.Debugf("Valid response body: %v", string(data))
}
defer response.Body.Close()
return data, nil
}