1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-02-19 19:44:27 +02:00
sap-jenkins-library/pkg/abaputils/manageGitRepositoryUtils.go
Simon Dold 3225a7ddc0
Adding log download as zip archive (#5121)
* adding LogOutput to clone step

* delete comments

* adding stepName

* change step name

* adding multiple log archive outputs files

* changing file name

* change filename

* change time format

* adding second file for testing

* adding second file

* change structure for PersistReportsAndLinks

* change to pointer

* change pointer

* cleanup

* changing file name

* adding logArchive for pull action

* adding logArchive for checkoutBranch

* refactor zip archive log

* change structure

* adding PersistArchiveLogsForPiperStep function

* adding persist structure to checkout

* adding FileNameStep

* adding unit tests

* correct name

* change whitespace in yaml

* fixing unit tests

* fixing createTag unit test

* fixing unit test

* fixing unit test

* rename ArchiveOutputLogs to LogOutputManager

* refactor pointer structure

* adopt tests to pointer structure

* fixing / error in repo name

* adding log overview also after archive log

* change log output structure

* adding always execution log

* update unit tests

---------

Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com>
2024-10-10 13:42:09 +02:00

336 lines
11 KiB
Go

package abaputils
import (
"fmt"
"os"
"reflect"
"sort"
"strings"
"time"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/pkg/errors"
)
const numberOfEntriesPerPage = 100000
const logOutputStatusLength = 10
const logOutputTimestampLength = 29
// 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.")
}
}
// PollEntity periodically polls the action entity to get the status. Check if the import is still running
func PollEntity(api SoftwareComponentApiInterface, pollIntervall time.Duration, logOutputManager *LogOutputManager) (string, error) {
log.Entry().Info("Start polling the status...")
var statusCode string = "R"
var err error
api.initialRequest()
for {
// pullEntity, responseStatus, err := api.GetStatus(failureMessageClonePull+repositoryName, connectionDetails, client)
statusCode, err = api.GetAction()
if err != nil {
return statusCode, err
}
if statusCode != "R" && statusCode != "Q" {
PrintLogs(api, logOutputManager)
break
}
time.Sleep(pollIntervall)
}
return statusCode, nil
}
func PrintLogs(api SoftwareComponentApiInterface, logOutputManager *LogOutputManager) {
// Get Execution Logs
executionLogs, err := api.GetExecutionLog()
if err == nil {
printExecutionLogs(executionLogs)
}
results, _ := api.GetLogOverview()
// Sort logs
sort.SliceStable(results, func(i, j int) bool {
return results[i].Index < results[j].Index
})
printOverview(results, api)
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, 0o644)
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)
}
}
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)
}
func printOverview(results []LogResultsV2, api SoftwareComponentApiInterface) {
if len(results) == 0 {
return
}
logOutputPhaseLength, logOutputLineLength := calculateLenghts(results)
log.Entry().Infof("\n")
printDashedLine(logOutputLineLength)
log.Entry().Infof("| %-"+fmt.Sprint(logOutputPhaseLength)+"s | %"+fmt.Sprint(logOutputStatusLength)+"s | %-"+fmt.Sprint(logOutputTimestampLength)+"s |", "Phase", "Status", "Timestamp")
printDashedLine(logOutputLineLength)
for _, logEntry := range results {
log.Entry().Infof("| %-"+fmt.Sprint(logOutputPhaseLength)+"s | %"+fmt.Sprint(logOutputStatusLength)+"s | %-"+fmt.Sprint(logOutputTimestampLength)+"s |", logEntry.Name, logEntry.Status, api.ConvertTime(logEntry.Timestamp))
}
printDashedLine(logOutputLineLength)
}
func calculateLenghts(results []LogResultsV2) (int, int) {
phaseLength := 22
for _, logEntry := range results {
if l := len(logEntry.Name); l > phaseLength {
phaseLength = l
}
}
lineLength := 10 + phaseLength + logOutputStatusLength + logOutputTimestampLength
return phaseLength, lineLength
}
func printDashedLine(i int) {
log.Entry().Infof(strings.Repeat("-", i))
}
func printLog(logOverviewEntry LogResultsV2, api SoftwareComponentApiInterface) {
page := 0
printHeader(logOverviewEntry, api)
for {
logProtocols, count, err := api.GetLogProtocol(logOverviewEntry, page)
printLogProtocolEntries(logOverviewEntry, logProtocols)
page += 1
if allLogsHaveBeenPrinted(logProtocols, page, count, err) {
break
}
}
}
func printLogProtocolEntries(logEntry LogResultsV2, logProtocols []LogProtocol) {
sort.SliceStable(logProtocols, func(i, j int) bool {
return logProtocols[i].ProtocolLine < logProtocols[j].ProtocolLine
})
if logEntry.Status == `Error` {
for _, entry := range logProtocols {
log.Entry().Info(entry.Description)
}
} else {
for _, entry := range logProtocols {
log.Entry().Debug(entry.Description)
}
}
}
func allLogsHaveBeenPrinted(protocols []LogProtocol, page int, count int, err error) bool {
allPagesHaveBeenRead := count <= page*numberOfEntriesPerPage
return (err != nil || allPagesHaveBeenRead || reflect.DeepEqual(protocols, []LogProtocol{}))
}
func printHeader(logEntry LogResultsV2, api SoftwareComponentApiInterface) {
if logEntry.Status == `Error` {
log.Entry().Infof("\n")
AddDefaultDashedLine(1)
log.Entry().Infof("%s (%v)", logEntry.Name, api.ConvertTime(logEntry.Timestamp))
AddDefaultDashedLine(1)
} else {
log.Entry().Debugf("\n")
AddDebugDashedLine()
log.Entry().Debugf("%s (%v)", logEntry.Name, api.ConvertTime(logEntry.Timestamp))
AddDebugDashedLine()
}
}
// GetRepositories for parsing one or multiple branches and repositories from repositories file or branchName and repositoryName configuration
func GetRepositories(config *RepositoriesConfig, branchRequired bool) ([]Repository, error) {
var repositories = make([]Repository, 0)
if reflect.DeepEqual(RepositoriesConfig{}, config) {
log.SetErrorCategory(log.ErrorConfiguration)
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 {
log.SetErrorCategory(log.ErrorConfiguration)
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 {
log.SetErrorCategory(log.ErrorConfiguration)
return repositories, err
}
err = CheckAddonDescriptorForRepositories(descriptor)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
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})
}
if config.RepositoryName != "" && !branchRequired {
repositories = append(repositories, Repository{Name: config.RepositoryName, CommitID: config.CommitID})
}
if len(config.RepositoryNames) > 0 {
for _, repository := range config.RepositoryNames {
repositories = append(repositories, Repository{Name: repository})
}
}
return repositories, nil
}
func (repo *Repository) GetRequestBodyForCommitOrTag() (requestBodyString string) {
if repo.CommitID != "" {
requestBodyString = `, "commit_id":"` + repo.CommitID + `"`
} else if repo.Tag != "" {
requestBodyString = `, "tag_name":"` + repo.Tag + `"`
}
return requestBodyString
}
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
}
func (repo *Repository) GetLogStringForCommitOrTag() (logString string) {
if repo.CommitID != "" {
logString = ", commit '" + repo.CommitID + "'"
} else if repo.Tag != "" {
logString = ", tag '" + repo.Tag + "'"
}
return logString
}
func (repo *Repository) GetCloneRequestBodyWithSWC() (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 + `", "branch_name":"` + repo.Branch + `"` + requestBodyString + `}`
return body
}
func (repo *Repository) GetCloneRequestBody() (body string, err error) {
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()
var byogBodyString = ""
if repo.IsByog {
byogBodyString, err = repo.GetRequestBodyForBYOGCredentials()
if err != nil {
return "", err
}
}
body = `{"branch_name":"` + repo.Branch + `"` + requestBodyString + byogBodyString + `}`
return body, nil
}
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
}
func (repo *Repository) GetPullActionRequestBody() (body string) {
return `{` + `"commit_id":"` + repo.CommitID + `", ` + `"tag_name":"` + repo.Tag + `"` + `}`
}
func (repo *Repository) GetPullLogString() (logString string) {
commitOrTag := repo.GetLogStringForCommitOrTag()
logString = "repository / software component '" + repo.Name + "'" + commitOrTag
return logString
}