2020-08-04 17:52:28 +02:00
package abaputils
import (
2020-10-05 14:38:35 +02:00
"fmt"
2024-10-10 13:42:09 +02:00
"os"
2020-08-04 17:52:28 +02:00
"reflect"
"sort"
2022-05-19 16:59:37 +02:00
"strings"
2020-08-04 17:52:28 +02:00
"time"
"github.com/SAP/jenkins-library/pkg/log"
2024-10-10 13:42:09 +02:00
"github.com/SAP/jenkins-library/pkg/piperutils"
2020-08-04 17:52:28 +02:00
"github.com/pkg/errors"
)
2023-04-04 12:46:03 +02:00
const numberOfEntriesPerPage = 100000
2023-03-31 15:26:38 +02:00
const logOutputStatusLength = 10
const logOutputTimestampLength = 29
2022-05-23 15:15:22 +02:00
2024-10-10 13:42:09 +02:00
// Specifies which output option is used for logs
type LogOutputManager struct {
LogOutput string
PiperStep string
FileNameStep string
StepReports [ ] piperutils . Path
}
func PersistArchiveLogsForPiperStep ( logOutputManager * LogOutputManager ) {
fileUtils := piperutils . Files { }
switch logOutputManager . PiperStep {
case "clone" :
piperutils . PersistReportsAndLinks ( "abapEnvironmentCloneGitRepo" , "" , fileUtils , logOutputManager . StepReports , nil )
case "pull" :
piperutils . PersistReportsAndLinks ( "abapEnvironmentPullGitRepo" , "" , fileUtils , logOutputManager . StepReports , nil )
case "checkoutBranch" :
piperutils . PersistReportsAndLinks ( "abapEnvironmentCheckoutBranch" , "" , fileUtils , logOutputManager . StepReports , nil )
default :
log . Entry ( ) . Info ( "Cannot save log archive because no piper step was defined." )
}
}
2023-11-28 13:26:31 +01:00
// PollEntity periodically polls the action entity to get the status. Check if the import is still running
2024-10-10 13:42:09 +02:00
func PollEntity ( api SoftwareComponentApiInterface , pollIntervall time . Duration , logOutputManager * LogOutputManager ) ( string , error ) {
2020-08-04 17:52:28 +02:00
log . Entry ( ) . Info ( "Start polling the status..." )
2023-11-28 13:26:31 +01:00
var statusCode string = "R"
var err error
2020-08-04 17:52:28 +02:00
2024-10-10 13:42:09 +02:00
api . initialRequest ( )
2020-08-04 17:52:28 +02:00
for {
2023-11-28 13:26:31 +01:00
// pullEntity, responseStatus, err := api.GetStatus(failureMessageClonePull+repositoryName, connectionDetails, client)
statusCode , err = api . GetAction ( )
2020-08-04 17:52:28 +02:00
if err != nil {
2023-11-28 13:26:31 +01:00
return statusCode , err
2020-08-04 17:52:28 +02:00
}
2023-03-31 15:26:38 +02:00
2023-11-28 13:26:31 +01:00
if statusCode != "R" && statusCode != "Q" {
2024-10-10 13:42:09 +02:00
PrintLogs ( api , logOutputManager )
2020-08-04 17:52:28 +02:00
break
}
time . Sleep ( pollIntervall )
}
2023-11-28 13:26:31 +01:00
return statusCode , nil
2020-08-04 17:52:28 +02:00
}
2024-10-10 13:42:09 +02:00
func PrintLogs ( api SoftwareComponentApiInterface , logOutputManager * LogOutputManager ) {
2024-04-24 16:01:34 +02:00
// Get Execution Logs
executionLogs , err := api . GetExecutionLog ( )
if err == nil {
printExecutionLogs ( executionLogs )
}
2024-10-10 13:42:09 +02:00
results , _ := api . GetLogOverview ( )
2022-02-11 10:16:40 +01:00
// Sort logs
2023-12-19 15:16:48 +01:00
sort . SliceStable ( results , func ( i , j int ) bool {
return results [ i ] . Index < results [ j ] . Index
2022-02-11 10:16:40 +01:00
} )
2024-05-08 14:25:13 +02:00
printOverview ( results , api )
2022-02-11 10:16:40 +01:00
2024-10-10 13:42:09 +02:00
if logOutputManager . LogOutput == "ZIP" {
// get zip file as byte array
zipfile , err := api . GetLogArchive ( )
// Saving logs in file and adding to piperutils to archive file
if err == nil {
fileName := "LogArchive-" + logOutputManager . FileNameStep + "-" + strings . Replace ( api . getRepositoryName ( ) , "/" , "_" , - 1 ) + "-" + api . getUUID ( ) + "_" + time . Now ( ) . Format ( "2006-01-02T15:04:05" ) + ".zip"
err = os . WriteFile ( fileName , zipfile , 0 o644 )
if err == nil {
log . Entry ( ) . Infof ( "Writing %s file was successful" , fileName )
logOutputManager . StepReports = append ( logOutputManager . StepReports , piperutils . Path { Target : fileName , Name : "Log_Archive_" + api . getUUID ( ) , Mandatory : true } )
}
}
} else {
// Print Details
if len ( results ) != 0 {
for _ , logEntryForDetails := range results {
printLog ( logEntryForDetails , api )
}
}
AddDefaultDashedLine ( 1 )
2022-02-11 10:16:40 +01:00
}
}
2024-04-24 16:01:34 +02:00
func printExecutionLogs ( executionLogs ExecutionLog ) {
log . Entry ( ) . Infof ( "\n" )
AddDefaultDashedLine ( 1 )
log . Entry ( ) . Infof ( "Execution Logs" )
AddDefaultDashedLine ( 1 )
for _ , entry := range executionLogs . Value {
log . Entry ( ) . Infof ( "%7s - %s" , entry . Type , entry . Descr )
}
AddDefaultDashedLine ( 1 )
}
2024-05-08 14:25:13 +02:00
func printOverview ( results [ ] LogResultsV2 , api SoftwareComponentApiInterface ) {
2023-04-04 12:46:03 +02:00
2024-10-10 13:42:09 +02:00
if len ( results ) == 0 {
return
}
2023-12-19 15:16:48 +01:00
logOutputPhaseLength , logOutputLineLength := calculateLenghts ( results )
2023-04-04 12:46:03 +02:00
2023-03-31 15:26:38 +02:00
log . Entry ( ) . Infof ( "\n" )
2023-04-04 12:46:03 +02:00
printDashedLine ( logOutputLineLength )
2023-03-31 15:26:38 +02:00
log . Entry ( ) . Infof ( "| %-" + fmt . Sprint ( logOutputPhaseLength ) + "s | %" + fmt . Sprint ( logOutputStatusLength ) + "s | %-" + fmt . Sprint ( logOutputTimestampLength ) + "s |" , "Phase" , "Status" , "Timestamp" )
2023-04-04 12:46:03 +02:00
printDashedLine ( logOutputLineLength )
2023-12-19 15:16:48 +01:00
for _ , logEntry := range results {
2024-05-08 14:25:13 +02:00
log . Entry ( ) . Infof ( "| %-" + fmt . Sprint ( logOutputPhaseLength ) + "s | %" + fmt . Sprint ( logOutputStatusLength ) + "s | %-" + fmt . Sprint ( logOutputTimestampLength ) + "s |" , logEntry . Name , logEntry . Status , api . ConvertTime ( logEntry . Timestamp ) )
2023-03-31 15:26:38 +02:00
}
2023-04-04 12:46:03 +02:00
printDashedLine ( logOutputLineLength )
2023-03-31 15:26:38 +02:00
}
2023-12-19 15:16:48 +01:00
func calculateLenghts ( results [ ] LogResultsV2 ) ( int , int ) {
2023-03-31 15:26:38 +02:00
phaseLength := 22
2023-12-19 15:16:48 +01:00
for _ , logEntry := range results {
2023-03-31 15:26:38 +02:00
if l := len ( logEntry . Name ) ; l > phaseLength {
phaseLength = l
}
}
lineLength := 10 + phaseLength + logOutputStatusLength + logOutputTimestampLength
return phaseLength , lineLength
}
2023-04-04 12:46:03 +02:00
func printDashedLine ( i int ) {
2022-05-19 16:59:37 +02:00
log . Entry ( ) . Infof ( strings . Repeat ( "-" , i ) )
}
2023-11-28 13:26:31 +01:00
func printLog ( logOverviewEntry LogResultsV2 , api SoftwareComponentApiInterface ) {
2022-02-11 10:16:40 +01:00
2023-03-31 15:26:38 +02:00
page := 0
2024-05-08 14:25:13 +02:00
printHeader ( logOverviewEntry , api )
2023-03-31 15:26:38 +02:00
for {
2023-12-19 15:16:48 +01:00
logProtocols , count , err := api . GetLogProtocol ( logOverviewEntry , page )
printLogProtocolEntries ( logOverviewEntry , logProtocols )
2023-03-31 15:26:38 +02:00
page += 1
2023-12-19 15:16:48 +01:00
if allLogsHaveBeenPrinted ( logProtocols , page , count , err ) {
2023-03-31 15:26:38 +02:00
break
}
}
}
2023-12-19 15:16:48 +01:00
func printLogProtocolEntries ( logEntry LogResultsV2 , logProtocols [ ] LogProtocol ) {
2023-04-04 12:46:03 +02:00
2023-12-19 15:16:48 +01:00
sort . SliceStable ( logProtocols , func ( i , j int ) bool {
return logProtocols [ i ] . ProtocolLine < logProtocols [ j ] . ProtocolLine
2023-04-04 12:46:03 +02:00
} )
2024-02-09 10:20:27 +01:00
if logEntry . Status == ` Error ` {
2023-12-19 15:16:48 +01:00
for _ , entry := range logProtocols {
2023-04-04 12:46:03 +02:00
log . Entry ( ) . Info ( entry . Description )
}
} else {
2023-12-19 15:16:48 +01:00
for _ , entry := range logProtocols {
2023-04-04 12:46:03 +02:00
log . Entry ( ) . Debug ( entry . Description )
}
}
}
2023-12-19 15:16:48 +01:00
func allLogsHaveBeenPrinted ( protocols [ ] LogProtocol , page int , count int , err error ) bool {
allPagesHaveBeenRead := count <= page * numberOfEntriesPerPage
return ( err != nil || allPagesHaveBeenRead || reflect . DeepEqual ( protocols , [ ] LogProtocol { } ) )
2023-04-04 12:46:03 +02:00
}
2024-05-08 14:25:13 +02:00
func printHeader ( logEntry LogResultsV2 , api SoftwareComponentApiInterface ) {
2024-05-10 09:54:07 +02:00
if logEntry . Status == ` Error ` {
2022-02-11 10:16:40 +01:00
log . Entry ( ) . Infof ( "\n" )
2023-11-28 13:26:31 +01:00
AddDefaultDashedLine ( 1 )
2024-05-08 14:25:13 +02:00
log . Entry ( ) . Infof ( "%s (%v)" , logEntry . Name , api . ConvertTime ( logEntry . Timestamp ) )
2023-11-28 13:26:31 +01:00
AddDefaultDashedLine ( 1 )
2022-02-11 10:16:40 +01:00
} else {
log . Entry ( ) . Debugf ( "\n" )
2023-07-18 09:05:53 +02:00
AddDebugDashedLine ( )
2024-05-08 14:25:13 +02:00
log . Entry ( ) . Debugf ( "%s (%v)" , logEntry . Name , api . ConvertTime ( logEntry . Timestamp ) )
2023-07-18 09:05:53 +02:00
AddDebugDashedLine ( )
2022-02-11 10:16:40 +01:00
}
}
2024-06-25 10:09:31 +02:00
// GetRepositories for parsing one or multiple branches and repositories from repositories file or branchName and repositoryName configuration
2022-06-28 11:02:15 +02:00
func GetRepositories ( config * RepositoriesConfig , branchRequired bool ) ( [ ] Repository , error ) {
2020-10-05 14:38:35 +02:00
var repositories = make ( [ ] Repository , 0 )
if reflect . DeepEqual ( RepositoriesConfig { } , config ) {
2021-08-04 17:31:16 +02:00
log . SetErrorCategory ( log . ErrorConfiguration )
2020-10-05 14:38:35 +02:00
return repositories , fmt . Errorf ( "Failed to read repository configuration: %w" , errors . New ( "Eror in configuration, most likely you have entered empty or wrong configuration values. Please make sure that you have correctly specified them. For more information please read the User documentation" ) )
}
if config . RepositoryName == "" && config . BranchName == "" && config . Repositories == "" && len ( config . RepositoryNames ) == 0 {
2021-08-04 17:31:16 +02:00
log . SetErrorCategory ( log . ErrorConfiguration )
2020-10-05 14:38:35 +02:00
return repositories , fmt . Errorf ( "Failed to read repository configuration: %w" , errors . New ( "You have not specified any repository configuration. Please make sure that you have correctly specified it. For more information please read the User documentation" ) )
}
if config . Repositories != "" {
descriptor , err := ReadAddonDescriptor ( config . Repositories )
if err != nil {
2021-08-04 17:31:16 +02:00
log . SetErrorCategory ( log . ErrorConfiguration )
2020-10-05 14:38:35 +02:00
return repositories , err
}
err = CheckAddonDescriptorForRepositories ( descriptor )
if err != nil {
2021-08-04 17:31:16 +02:00
log . SetErrorCategory ( log . ErrorConfiguration )
2020-10-05 14:38:35 +02:00
return repositories , fmt . Errorf ( "Error in config file %v, %w" , config . Repositories , err )
}
repositories = descriptor . Repositories
}
if config . RepositoryName != "" && config . BranchName != "" {
repositories = append ( repositories , Repository { Name : config . RepositoryName , Branch : config . BranchName } )
}
2022-06-28 11:02:15 +02:00
if config . RepositoryName != "" && ! branchRequired {
repositories = append ( repositories , Repository { Name : config . RepositoryName , CommitID : config . CommitID } )
}
2024-06-25 10:09:31 +02:00
2020-10-05 14:38:35 +02:00
if len ( config . RepositoryNames ) > 0 {
for _ , repository := range config . RepositoryNames {
repositories = append ( repositories , Repository { Name : repository } )
}
}
return repositories , nil
}
2021-12-20 17:58:58 +01:00
func ( repo * Repository ) GetRequestBodyForCommitOrTag ( ) ( requestBodyString string ) {
if repo . CommitID != "" {
requestBodyString = ` , "commit_id":" ` + repo . CommitID + ` " `
} else if repo . Tag != "" {
requestBodyString = ` , "tag_name":" ` + repo . Tag + ` " `
2020-11-02 14:17:13 +01:00
}
2021-12-20 17:58:58 +01:00
return requestBodyString
}
2024-06-25 10:09:31 +02:00
func ( repo * Repository ) GetRequestBodyForBYOGCredentials ( ) ( string , error ) {
var byogBodyString string
if repo . ByogAuthMethod != "" {
byogBodyString += ` , "auth_method":" ` + repo . ByogAuthMethod + ` " `
}
if repo . ByogUsername != "" {
byogBodyString += ` , "username":" ` + repo . ByogUsername + ` " `
} else {
return "" , fmt . Errorf ( "Failed to get BYOG credentials: %w" , errors . New ( "Username for BYOG is missing, please provide git username to authenticate" ) )
}
if repo . ByogPassword != "" {
byogBodyString += ` , "password":" ` + repo . ByogPassword + ` " `
} else {
return "" , fmt . Errorf ( "Failed to get BYOG credentials: %w" , errors . New ( "Password/Token for BYOG is missing, please provide git password or token to authenticate" ) )
}
return byogBodyString , nil
}
2021-12-20 17:58:58 +01:00
func ( repo * Repository ) GetLogStringForCommitOrTag ( ) ( logString string ) {
if repo . CommitID != "" {
logString = ", commit '" + repo . CommitID + "'"
} else if repo . Tag != "" {
logString = ", tag '" + repo . Tag + "'"
}
return logString
}
2024-03-14 16:30:19 +01:00
func ( repo * Repository ) GetCloneRequestBodyWithSWC ( ) ( body string ) {
2021-12-20 17:58:58 +01:00
if repo . CommitID != "" && repo . Tag != "" {
log . Entry ( ) . WithField ( "Tag" , repo . Tag ) . WithField ( "Commit ID" , repo . CommitID ) . Info ( "The commit ID takes precedence over the tag" )
}
requestBodyString := repo . GetRequestBodyForCommitOrTag ( )
body = ` { "sc_name":" ` + repo . Name + ` ", "branch_name":" ` + repo . Branch + ` " ` + requestBodyString + ` } `
2024-03-14 16:30:19 +01:00
return body
}
2024-06-25 10:09:31 +02:00
func ( repo * Repository ) GetCloneRequestBody ( ) ( body string , err error ) {
2024-03-14 16:30:19 +01:00
if repo . CommitID != "" && repo . Tag != "" {
log . Entry ( ) . WithField ( "Tag" , repo . Tag ) . WithField ( "Commit ID" , repo . CommitID ) . Info ( "The commit ID takes precedence over the tag" )
}
requestBodyString := repo . GetRequestBodyForCommitOrTag ( )
2024-06-25 10:09:31 +02:00
var byogBodyString = ""
if repo . IsByog {
byogBodyString , err = repo . GetRequestBodyForBYOGCredentials ( )
if err != nil {
return "" , err
}
}
body = ` { "branch_name":" ` + repo . Branch + ` " ` + requestBodyString + byogBodyString + ` } `
return body , nil
2021-12-20 17:58:58 +01:00
}
func ( repo * Repository ) GetCloneLogString ( ) ( logString string ) {
commitOrTag := repo . GetLogStringForCommitOrTag ( )
logString = "repository / software component '" + repo . Name + "', branch '" + repo . Branch + "'" + commitOrTag
return logString
}
func ( repo * Repository ) GetPullRequestBody ( ) ( body string ) {
if repo . CommitID != "" && repo . Tag != "" {
log . Entry ( ) . WithField ( "Tag" , repo . Tag ) . WithField ( "Commit ID" , repo . CommitID ) . Info ( "The commit ID takes precedence over the tag" )
}
requestBodyString := repo . GetRequestBodyForCommitOrTag ( )
body = ` { "sc_name":" ` + repo . Name + ` " ` + requestBodyString + ` } `
return body
}
2023-12-19 15:16:48 +01:00
func ( repo * Repository ) GetPullActionRequestBody ( ) ( body string ) {
return ` { ` + ` "commit_id":" ` + repo . CommitID + ` ", ` + ` "tag_name":" ` + repo . Tag + ` " ` + ` } `
}
2021-12-20 17:58:58 +01:00
func ( repo * Repository ) GetPullLogString ( ) ( logString string ) {
commitOrTag := repo . GetLogStringForCommitOrTag ( )
logString = "repository / software component '" + repo . Name + "'" + commitOrTag
return logString
2020-11-02 14:17:13 +01:00
}