mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-01-04 04:07:16 +02:00
fix(orchestrator) usage of correct env variables (#3650)
* Reorders getApiInformation, changes variables to get start time, adjusts and adds test cases * Changes the way to get apiInformation and reduces number of requests * Changes getting pipeline start time from correct env variable * Refactors getApiInformation functionality * Adds GetBuildReason() for Azure and Jenkins * Updates JobURL for ADO
This commit is contained in:
parent
5926aa7f77
commit
ccc1c976ee
@ -202,7 +202,7 @@ func addRootFlags(rootCmd *cobra.Command) {
|
|||||||
provider = &orchestrator.UnknownOrchestratorConfigProvider{}
|
provider = &orchestrator.UnknownOrchestratorConfigProvider{}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVar(&GeneralConfig.CorrelationID, "correlationID", provider.GetBuildUrl(), "ID for unique identification of a pipeline run")
|
rootCmd.PersistentFlags().StringVar(&GeneralConfig.CorrelationID, "correlationID", provider.GetBuildURL(), "ID for unique identification of a pipeline run")
|
||||||
rootCmd.PersistentFlags().StringVar(&GeneralConfig.CustomConfig, "customConfig", ".pipeline/config.yml", "Path to the pipeline configuration file")
|
rootCmd.PersistentFlags().StringVar(&GeneralConfig.CustomConfig, "customConfig", ".pipeline/config.yml", "Path to the pipeline configuration file")
|
||||||
rootCmd.PersistentFlags().StringSliceVar(&GeneralConfig.GitHubTokens, "gitHubTokens", AccessTokensFromEnvJSON(os.Getenv("PIPER_gitHubTokens")), "List of entries in form of <hostname>:<token> to allow GitHub token authentication for downloading config / defaults")
|
rootCmd.PersistentFlags().StringSliceVar(&GeneralConfig.GitHubTokens, "gitHubTokens", AccessTokensFromEnvJSON(os.Getenv("PIPER_gitHubTokens")), "List of entries in form of <hostname>:<token> to allow GitHub token authentication for downloading config / defaults")
|
||||||
rootCmd.PersistentFlags().StringSliceVar(&GeneralConfig.DefaultConfig, "defaultConfig", []string{".pipeline/defaults.yaml"}, "Default configurations, passed as path to yaml file")
|
rootCmd.PersistentFlags().StringSliceVar(&GeneralConfig.DefaultConfig, "defaultConfig", []string{".pipeline/defaults.yaml"}, "Default configurations, passed as path to yaml file")
|
||||||
|
@ -67,7 +67,7 @@ type ClientOptions struct {
|
|||||||
TrustedCerts []string
|
TrustedCerts []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransportWrapper is a wrapper for central roundtrip capabilities
|
// TransportWrapper is a wrapper for central round trip capabilities
|
||||||
type TransportWrapper struct {
|
type TransportWrapper struct {
|
||||||
Transport http.RoundTripper
|
Transport http.RoundTripper
|
||||||
doLogRequestBodyOnDebug bool
|
doLogRequestBodyOnDebug bool
|
||||||
@ -206,7 +206,7 @@ func (c *Client) Upload(data UploadRequestData) (*http.Response, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRequest sends an http request with a defined method
|
// SendRequest sends a http request with a defined method
|
||||||
//
|
//
|
||||||
// On error, any Response can be ignored and the Response.Body
|
// On error, any Response can be ignored and the Response.Body
|
||||||
// does not need to be closed.
|
// does not need to be closed.
|
||||||
@ -219,7 +219,7 @@ func (c *Client) SendRequest(method, url string, body io.Reader, header http.Hea
|
|||||||
return c.Send(request)
|
return c.Send(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send sends an http request
|
// Send sends a http request
|
||||||
func (c *Client) Send(request *http.Request) (*http.Response, error) {
|
func (c *Client) Send(request *http.Request) (*http.Response, error) {
|
||||||
httpClient := c.initialize()
|
httpClient := c.initialize()
|
||||||
response, err := httpClient.Do(request)
|
response, err := httpClient.Do(request)
|
||||||
@ -355,7 +355,7 @@ var contextKeyRequestStart = &contextKey{"RequestStart"}
|
|||||||
var authHeaderKey = "Authorization"
|
var authHeaderKey = "Authorization"
|
||||||
|
|
||||||
// RoundTrip is the core part of this module and implements http.RoundTripper.
|
// RoundTrip is the core part of this module and implements http.RoundTripper.
|
||||||
// Executes HTTP request with request/response logging.
|
// Executes HTTP requests with request/response logging.
|
||||||
func (t *TransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
|
func (t *TransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
ctx := context.WithValue(req.Context(), contextKeyRequestStart, time.Now())
|
ctx := context.WithValue(req.Context(), contextKeyRequestStart, time.Now())
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
@ -372,7 +372,7 @@ func (t *TransportWrapper) RoundTrip(req *http.Request) (*http.Response, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleAuthentication(req *http.Request, username, password, token string) {
|
func handleAuthentication(req *http.Request, username, password, token string) {
|
||||||
// Handle authenticaion if not done already
|
// Handle authentication if not done already
|
||||||
if (len(username) > 0 || len(password) > 0) && len(req.Header.Get(authHeaderKey)) == 0 {
|
if (len(username) > 0 || len(password) > 0) && len(req.Header.Get(authHeaderKey)) == 0 {
|
||||||
req.SetBasicAuth(username, password)
|
req.SetBasicAuth(username, password)
|
||||||
log.Entry().Debug("Using Basic Authentication ****/****")
|
log.Entry().Debug("Using Basic Authentication ****/****")
|
||||||
@ -432,9 +432,9 @@ func transformHeaders(header http.Header) http.Header {
|
|||||||
// Since
|
// Since
|
||||||
// 1.) The auth header type itself might serve as a vector for an
|
// 1.) The auth header type itself might serve as a vector for an
|
||||||
// intrusion
|
// intrusion
|
||||||
// 2.) We cannot make assumtions about the structure of the auth
|
// 2.) We cannot make assumptions about the structure of the auth
|
||||||
// header value since that depends on the type, e.g. several tokens
|
// header value since that depends on the type, e.g. several tokens
|
||||||
// where only some of the tokens define the secret
|
// where only some tokens define the secret
|
||||||
// we hide the full auth header value anyway in order to be on the
|
// we hide the full auth header value anyway in order to be on the
|
||||||
// save side.
|
// save side.
|
||||||
value = []string{"<set>"}
|
value = []string{"<set>"}
|
||||||
@ -623,7 +623,7 @@ func (c *Client) configureTLSToTrustCertificates(transport *TransportWrapper) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// default truststore location
|
// TrustStoreDirectory default truststore location
|
||||||
const TrustStoreDirectory = ".pipeline/trustStore"
|
const TrustStoreDirectory = ".pipeline/trustStore"
|
||||||
|
|
||||||
func getWorkingDirForTrustStore() (string, error) {
|
func getWorkingDirForTrustStore() (string, error) {
|
||||||
@ -637,7 +637,7 @@ func getWorkingDirForTrustStore() (string, error) {
|
|||||||
return TrustStoreDirectory, nil
|
return TrustStoreDirectory, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseHTTPResponseBodyXML parses a XML http response into a given interface
|
// ParseHTTPResponseBodyXML parses an XML http response into a given interface
|
||||||
func ParseHTTPResponseBodyXML(resp *http.Response, response interface{}) error {
|
func ParseHTTPResponseBodyXML(resp *http.Response, response interface{}) error {
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
return errors.Errorf("cannot parse HTTP response with value <nil>")
|
return errors.Errorf("cannot parse HTTP response with value <nil>")
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
package orchestrator
|
package orchestrator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
piperHttp "github.com/SAP/jenkins-library/pkg/http"
|
piperHttp "github.com/SAP/jenkins-library/pkg/http"
|
||||||
"github.com/SAP/jenkins-library/pkg/log"
|
"github.com/SAP/jenkins-library/pkg/log"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AzureDevOpsConfigProvider struct {
|
type AzureDevOpsConfigProvider struct {
|
||||||
client piperHttp.Client
|
client piperHttp.Client
|
||||||
options piperHttp.ClientOptions
|
options piperHttp.ClientOptions
|
||||||
|
apiInformation map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
//InitOrchestratorProvider initializes http client for AzureDevopsConfigProvider
|
// InitOrchestratorProvider initializes http client for AzureDevopsConfigProvider
|
||||||
func (a *AzureDevOpsConfigProvider) InitOrchestratorProvider(settings *OrchestratorSettings) {
|
func (a *AzureDevOpsConfigProvider) InitOrchestratorProvider(settings *OrchestratorSettings) {
|
||||||
a.client = piperHttp.Client{}
|
a.client = piperHttp.Client{}
|
||||||
a.options = piperHttp.ClientOptions{
|
a.options = piperHttp.ClientOptions{
|
||||||
@ -29,6 +28,58 @@ func (a *AzureDevOpsConfigProvider) InitOrchestratorProvider(settings *Orchestra
|
|||||||
log.Entry().Debug("Successfully initialized Azure config provider")
|
log.Entry().Debug("Successfully initialized Azure config provider")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fetchAPIInformation fetches Azure API information of current build
|
||||||
|
func (a *AzureDevOpsConfigProvider) fetchAPIInformation() {
|
||||||
|
// if apiInformation is empty fill it otherwise do nothing
|
||||||
|
if len(a.apiInformation) == 0 {
|
||||||
|
log.Entry().Debugf("apiInformation is empty, getting infos from API")
|
||||||
|
URL := a.getSystemCollectionURI() + a.getTeamProjectID() + "/_apis/build/builds/" + a.getAzureBuildID() + "/"
|
||||||
|
log.Entry().Debugf("API URL: %s", URL)
|
||||||
|
response, err := a.client.GetRequest(URL, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().Error("failed to get HTTP response, returning empty API information", err)
|
||||||
|
a.apiInformation = map[string]interface{}{}
|
||||||
|
return
|
||||||
|
} else if response.StatusCode != 200 { //http.StatusNoContent
|
||||||
|
log.Entry().Errorf("response code is %v, could not get API information from AzureDevOps. Returning with empty interface.", response.StatusCode)
|
||||||
|
a.apiInformation = map[string]interface{}{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = piperHttp.ParseHTTPResponseBodyJSON(response, &a.apiInformation)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().Error("failed to parse HTTP response, returning with empty interface", err)
|
||||||
|
a.apiInformation = map[string]interface{}{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Entry().Debugf("successfully retrieved apiInformation")
|
||||||
|
} else {
|
||||||
|
log.Entry().Debugf("apiInformation already set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSystemCollectionURI returns the URI of the TFS collection or Azure DevOps organization e.g. https://dev.azure.com/fabrikamfiber/
|
||||||
|
func (a *AzureDevOpsConfigProvider) getSystemCollectionURI() string {
|
||||||
|
return getEnv("SYSTEM_COLLECTIONURI", "n/a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTeamProjectID is the name of the project that contains this build e.g. 123a4567-ab1c-12a1-1234-123456ab7890
|
||||||
|
func (a *AzureDevOpsConfigProvider) getTeamProjectID() string {
|
||||||
|
return getEnv("SYSTEM_TEAMPROJECTID", "n/a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAzureBuildID returns the id of the build, e.g. 1234
|
||||||
|
func (a *AzureDevOpsConfigProvider) getAzureBuildID() string {
|
||||||
|
// INFO: Private function only used for API requests, buildId for e.g. reporting
|
||||||
|
// is GetBuildNumber to align with the UI of ADO
|
||||||
|
return getEnv("BUILD_BUILDID", "n/a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJobName returns the pipeline job name, currently org/repo
|
||||||
|
func (a *AzureDevOpsConfigProvider) GetJobName() string {
|
||||||
|
return getEnv("BUILD_REPOSITORY_NAME", "n/a")
|
||||||
|
}
|
||||||
|
|
||||||
// OrchestratorVersion returns the agent version on ADO
|
// OrchestratorVersion returns the agent version on ADO
|
||||||
func (a *AzureDevOpsConfigProvider) OrchestratorVersion() string {
|
func (a *AzureDevOpsConfigProvider) OrchestratorVersion() string {
|
||||||
return getEnv("AGENT_VERSION", "n/a")
|
return getEnv("AGENT_VERSION", "n/a")
|
||||||
@ -39,145 +90,134 @@ func (a *AzureDevOpsConfigProvider) OrchestratorType() string {
|
|||||||
return "Azure"
|
return "Azure"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GetBuildStatus returns status of the build. Return variables are aligned with Jenkins build statuses.
|
||||||
func (a *AzureDevOpsConfigProvider) GetBuildStatus() string {
|
func (a *AzureDevOpsConfigProvider) GetBuildStatus() string {
|
||||||
responseInterface := a.getAPIInformation()
|
// cases to align with Jenkins: SUCCESS, FAILURE, NOT_BUILD, ABORTED
|
||||||
if _, ok := responseInterface["result"]; ok {
|
switch buildStatus := getEnv("AGENT_JOBSTATUS", "FAILURE"); buildStatus {
|
||||||
// cases in Jenkins: SUCCESS, FAILURE, NOT_BUILD, ABORTED
|
case "Succeeded":
|
||||||
switch result := responseInterface["result"]; result {
|
return "SUCCESS"
|
||||||
case "SUCCESS":
|
case "Canceled":
|
||||||
return "SUCCESS"
|
return "ABORTED"
|
||||||
case "ABORTED":
|
default:
|
||||||
return "ABORTED"
|
// Failed, SucceededWithIssues
|
||||||
default:
|
return "FAILURE"
|
||||||
// FAILURE, NOT_BUILT
|
|
||||||
return "FAILURE"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "FAILURE"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AzureDevOpsConfigProvider) getAPIInformation() map[string]interface{} {
|
// GetLog returns the whole logfile for the current pipeline run
|
||||||
URL := a.GetSystemCollectionURI() + a.GetTeamProjectId() + "/_apis/build/builds/" + a.GetBuildId() + "/"
|
|
||||||
response, err := a.client.GetRequest(URL, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Entry().Error("failed to get http response, using default values", err)
|
|
||||||
return map[string]interface{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.StatusCode != 200 { //http.StatusNoContent
|
|
||||||
log.Entry().Errorf("Response-Code is %v . \n Could not get API information from AzureDevOps. Returning with empty interface.", response.StatusCode)
|
|
||||||
return map[string]interface{}{}
|
|
||||||
}
|
|
||||||
var responseInterface map[string]interface{}
|
|
||||||
err = piperHttp.ParseHTTPResponseBodyJSON(response, &responseInterface)
|
|
||||||
if err != nil {
|
|
||||||
log.Entry().Error("failed to parse http response, returning with empty interface", err)
|
|
||||||
return map[string]interface{}{}
|
|
||||||
}
|
|
||||||
return responseInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetJobName returns the pipeline job name
|
|
||||||
func (a *AzureDevOpsConfigProvider) GetJobName() string {
|
|
||||||
responseInterface := a.getAPIInformation()
|
|
||||||
if val, ok := responseInterface["repository"]; ok {
|
|
||||||
return val.(map[string]interface{})["id"].(string)
|
|
||||||
}
|
|
||||||
return "n/a"
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLog returns the logfile of the pipeline run so far
|
|
||||||
func (a *AzureDevOpsConfigProvider) GetLog() ([]byte, error) {
|
func (a *AzureDevOpsConfigProvider) GetLog() ([]byte, error) {
|
||||||
// ToDo: How to get step specific logs, not only whole log?
|
URL := a.getSystemCollectionURI() + a.getTeamProjectID() + "/_apis/build/builds/" + a.getAzureBuildID() + "/logs"
|
||||||
URL := a.GetSystemCollectionURI() + a.GetTeamProjectId() + "/_apis/build/builds/" + a.GetBuildId() + "/logs"
|
|
||||||
|
|
||||||
response, err := a.client.GetRequest(URL, nil, nil)
|
response, err := a.client.GetRequest(URL, nil, nil)
|
||||||
logs := []byte{}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Entry().Error("failed to get http response", err)
|
log.Entry().Error("failed to get HTTP response: ", err)
|
||||||
return logs, nil
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
if response.StatusCode != 200 { //http.StatusNoContent -> also empty log!
|
if response.StatusCode != 200 { //http.StatusNoContent -> also empty log!
|
||||||
log.Entry().Errorf("Response-Code is %v . \n Could not get log information from AzureDevOps. Returning with empty log.", response.StatusCode)
|
log.Entry().Errorf("response-Code is %v, could not get log information from AzureDevOps, returning with empty log.", response.StatusCode)
|
||||||
return logs, nil
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
var responseInterface map[string]interface{}
|
var responseInterface map[string]interface{}
|
||||||
err = piperHttp.ParseHTTPResponseBodyJSON(response, &responseInterface)
|
err = piperHttp.ParseHTTPResponseBodyJSON(response, &responseInterface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Entry().Error("failed to parse http response", err)
|
log.Entry().Error("failed to parse http response: ", err)
|
||||||
return logs, nil
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
// check if response interface is empty or non-existent
|
// check if response interface is empty or non-existent
|
||||||
logCount := int(responseInterface["count"].(float64))
|
var logCount int
|
||||||
|
if val, ok := responseInterface["count"]; ok {
|
||||||
|
logCount = int(val.(float64))
|
||||||
|
} else {
|
||||||
|
log.Entry().Error("log count variable not found, returning empty log")
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
var logs []byte
|
||||||
for i := 1; i <= logCount; i++ {
|
for i := 1; i <= logCount; i++ {
|
||||||
counter := strconv.Itoa(i)
|
counter := strconv.Itoa(i)
|
||||||
logURL := URL + "/" + counter
|
logURL := URL + "/" + counter
|
||||||
fmt.Println("logURL: ", logURL)
|
|
||||||
log.Entry().Debugf("Getting log no.: %d from %v", i, logURL)
|
log.Entry().Debugf("Getting log no.: %d from %v", i, logURL)
|
||||||
response, err := a.client.GetRequest(logURL, nil, nil)
|
response, err := a.client.GetRequest(logURL, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
log.Entry().Error("failed to get log", err)
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
if response.StatusCode != 200 { //http.StatusNoContent -> also empty log!
|
||||||
|
log.Entry().Errorf("response code is %v, could not get log information from AzureDevOps ", response.StatusCode)
|
||||||
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
content, err := ioutil.ReadAll(response.Body)
|
content, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().Error("failed to parse http response", err)
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
logs = append(logs, content...)
|
logs = append(logs, content...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return logs, nil
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPipelineStartTime returns the pipeline start time
|
// GetPipelineStartTime returns the pipeline start time in UTC
|
||||||
func (a *AzureDevOpsConfigProvider) GetPipelineStartTime() time.Time {
|
func (a *AzureDevOpsConfigProvider) GetPipelineStartTime() time.Time {
|
||||||
// "2021-10-11 13:49:09+00:00"
|
//"2022-03-18T07:30:31.1915758Z"
|
||||||
timestamp := getEnv("SYSTEM_PIPELINESTARTTIME", "n/a")
|
a.fetchAPIInformation()
|
||||||
replaced := strings.Replace(timestamp, " ", "T", 1)
|
if val, ok := a.apiInformation["startTime"]; ok {
|
||||||
parsed, err := time.Parse(time.RFC3339, replaced)
|
parsed, err := time.Parse(time.RFC3339, val.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Entry().Errorf("Could not parse timestamp. %v", err)
|
log.Entry().Errorf("could not parse timestamp, %v", err)
|
||||||
// Return 1970 in case parsing goes wrong
|
parsed = time.Time{}
|
||||||
parsed = time.Date(1970, time.January, 01, 0, 0, 0, 0, time.UTC)
|
}
|
||||||
|
return parsed.UTC()
|
||||||
}
|
}
|
||||||
return parsed
|
return time.Time{}.UTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AzureDevOpsConfigProvider) GetSystemCollectionURI() string {
|
// GetBuildID returns the BuildNumber displayed in the ADO UI
|
||||||
return getEnv("SYSTEM_COLLECTIONURI", "n/a")
|
func (a *AzureDevOpsConfigProvider) GetBuildID() string {
|
||||||
}
|
// INFO: ADO has BUILD_ID and buildNumber, as buildNumber is used in the UI we return this value
|
||||||
|
// for the buildID used only for API requests we have a private method getAzureBuildID
|
||||||
func (a *AzureDevOpsConfigProvider) GetTeamProjectId() string {
|
// example: buildNumber: 20220318.16 buildId: 76443
|
||||||
return getEnv("SYSTEM_TEAMPROJECTID", "n/a")
|
return getEnv("BUILD_BUILDNUMBER", "n/a")
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AzureDevOpsConfigProvider) GetBuildId() string {
|
|
||||||
return getEnv("BUILD_BUILDID", "n/a")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStageName returns the human-readable name given to a stage. e.g. "Promote" or "Init"
|
||||||
func (a *AzureDevOpsConfigProvider) GetStageName() string {
|
func (a *AzureDevOpsConfigProvider) GetStageName() string {
|
||||||
return getEnv("SYSTEM_STAGEDISPLAYNAME", "n/a")
|
return getEnv("SYSTEM_STAGEDISPLAYNAME", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBranch returns the source branch name, e.g. main
|
||||||
func (a *AzureDevOpsConfigProvider) GetBranch() string {
|
func (a *AzureDevOpsConfigProvider) GetBranch() string {
|
||||||
tmp := getEnv("BUILD_SOURCEBRANCH", "n/a")
|
return getEnv("BUILD_SOURCEBRANCHNAME", "n/a")
|
||||||
return strings.TrimPrefix(tmp, "refs/heads/")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AzureDevOpsConfigProvider) GetBuildUrl() string {
|
// GetBuildURL returns the builds URL e.g. https://dev.azure.com/fabrikamfiber/your-repo-name/_build/results?buildId=1234
|
||||||
return os.Getenv("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI") + os.Getenv("SYSTEM_TEAMPROJECT") + "/_build/results?buildId=" + os.Getenv("BUILD_BUILDID")
|
func (a *AzureDevOpsConfigProvider) GetBuildURL() string {
|
||||||
|
return os.Getenv("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI") + os.Getenv("SYSTEM_TEAMPROJECT") + "/" + os.Getenv("SYSTEM_DEFINITIONNAME") + "/_build/results?buildId=" + a.getAzureBuildID()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AzureDevOpsConfigProvider) GetJobUrl() string {
|
// GetJobURL returns tje current job url e.g. https://dev.azure.com/fabrikamfiber/your-repo-name/_build?definitionId=1234
|
||||||
// TODO: Check if thi is the correct URL
|
func (a *AzureDevOpsConfigProvider) GetJobURL() string {
|
||||||
return os.Getenv("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI") + os.Getenv("SYSTEM_TEAMPROJECT")
|
// TODO: Check if this is the correct URL
|
||||||
|
return os.Getenv("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI") + os.Getenv("SYSTEM_TEAMPROJECT") + "/" + os.Getenv("SYSTEM_DEFINITIONNAME") + "/_build?definitionId=" + os.Getenv("SYSTEM_DEFINITIONID")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCommit returns commit SHA of current build
|
||||||
func (a *AzureDevOpsConfigProvider) GetCommit() string {
|
func (a *AzureDevOpsConfigProvider) GetCommit() string {
|
||||||
return getEnv("BUILD_SOURCEVERSION", "n/a")
|
return getEnv("BUILD_SOURCEVERSION", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AzureDevOpsConfigProvider) GetRepoUrl() string {
|
// GetRepoURL returns current repo URL e.g. https://github.com/SAP/jenkins-library
|
||||||
|
func (a *AzureDevOpsConfigProvider) GetRepoURL() string {
|
||||||
return getEnv("BUILD_REPOSITORY_URI", "n/a")
|
return getEnv("BUILD_REPOSITORY_URI", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBuildReason returns the build reason
|
||||||
|
func (a *AzureDevOpsConfigProvider) GetBuildReason() string {
|
||||||
|
// https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#build-variables-devops-services
|
||||||
|
return getEnv("BUILD_REASON", "n/a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPullRequestConfig returns pull request configuration
|
||||||
func (a *AzureDevOpsConfigProvider) GetPullRequestConfig() PullRequestConfig {
|
func (a *AzureDevOpsConfigProvider) GetPullRequestConfig() PullRequestConfig {
|
||||||
prKey := getEnv("SYSTEM_PULLREQUEST_PULLREQUESTID", "n/a")
|
prKey := getEnv("SYSTEM_PULLREQUEST_PULLREQUESTID", "n/a")
|
||||||
|
|
||||||
@ -196,6 +236,7 @@ func (a *AzureDevOpsConfigProvider) GetPullRequestConfig() PullRequestConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPullRequest indicates whether the current build is a PR
|
||||||
func (a *AzureDevOpsConfigProvider) IsPullRequest() bool {
|
func (a *AzureDevOpsConfigProvider) IsPullRequest() bool {
|
||||||
return getEnv("BUILD_REASON", "n/a") == "PullRequest"
|
return getEnv("BUILD_REASON", "n/a") == "PullRequest"
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
package orchestrator
|
package orchestrator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||||
|
"github.com/jarcoal/httpmock"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -12,21 +18,23 @@ func TestAzure(t *testing.T) {
|
|||||||
defer resetEnv(os.Environ())
|
defer resetEnv(os.Environ())
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
os.Setenv("AZURE_HTTP_USER_AGENT", "FOO BAR BAZ")
|
os.Setenv("AZURE_HTTP_USER_AGENT", "FOO BAR BAZ")
|
||||||
os.Setenv("BUILD_SOURCEBRANCH", "refs/heads/feat/test-azure")
|
os.Setenv("BUILD_SOURCEBRANCHNAME", "feat/test-azure")
|
||||||
os.Setenv("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://pogo.foo")
|
os.Setenv("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://pogo.sap/")
|
||||||
os.Setenv("SYSTEM_TEAMPROJECT", "bar")
|
os.Setenv("SYSTEM_TEAMPROJECT", "foo")
|
||||||
os.Setenv("BUILD_BUILDID", "42")
|
os.Setenv("BUILD_BUILDID", "42")
|
||||||
os.Setenv("BUILD_SOURCEVERSION", "abcdef42713")
|
os.Setenv("BUILD_SOURCEVERSION", "abcdef42713")
|
||||||
os.Setenv("BUILD_REPOSITORY_URI", "github.com/foo/bar")
|
os.Setenv("BUILD_REPOSITORY_URI", "github.com/foo/bar")
|
||||||
|
os.Setenv("SYSTEM_DEFINITIONNAME", "bar")
|
||||||
|
os.Setenv("SYSTEM_DEFINITIONID", "1234")
|
||||||
p, _ := NewOrchestratorSpecificConfigProvider()
|
p, _ := NewOrchestratorSpecificConfigProvider()
|
||||||
|
|
||||||
assert.False(t, p.IsPullRequest())
|
assert.False(t, p.IsPullRequest())
|
||||||
assert.Equal(t, "feat/test-azure", p.GetBranch())
|
assert.Equal(t, "feat/test-azure", p.GetBranch())
|
||||||
assert.Equal(t, "https://pogo.foobar/_build/results?buildId=42", p.GetBuildUrl())
|
assert.Equal(t, "https://pogo.sap/foo/bar/_build/results?buildId=42", p.GetBuildURL())
|
||||||
assert.Equal(t, "abcdef42713", p.GetCommit())
|
assert.Equal(t, "abcdef42713", p.GetCommit())
|
||||||
assert.Equal(t, "github.com/foo/bar", p.GetRepoUrl())
|
assert.Equal(t, "github.com/foo/bar", p.GetRepoURL())
|
||||||
assert.Equal(t, "Azure", p.OrchestratorType())
|
assert.Equal(t, "Azure", p.OrchestratorType())
|
||||||
|
assert.Equal(t, "https://pogo.sap/foo/bar/_build?definitionId=1234", p.GetJobURL())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("PR", func(t *testing.T) {
|
t.Run("PR", func(t *testing.T) {
|
||||||
@ -74,4 +82,279 @@ func TestAzure(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, Orchestrator(Unknown), o)
|
assert.Equal(t, Orchestrator(Unknown), o)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("env variables", func(t *testing.T) {
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("SYSTEM_COLLECTIONURI", "https://dev.azure.com/fabrikamfiber/")
|
||||||
|
os.Setenv("SYSTEM_TEAMPROJECTID", "123a4567-ab1c-12a1-1234-123456ab7890")
|
||||||
|
os.Setenv("BUILD_BUILDID", "42")
|
||||||
|
os.Setenv("AGENT_VERSION", "2.193.0")
|
||||||
|
os.Setenv("BUILD_BUILDNUMBER", "20220318.16")
|
||||||
|
os.Setenv("BUILD_REPOSITORY_NAME", "repo-org/repo-name")
|
||||||
|
|
||||||
|
p := AzureDevOpsConfigProvider{}
|
||||||
|
|
||||||
|
assert.Equal(t, "https://dev.azure.com/fabrikamfiber/", p.getSystemCollectionURI())
|
||||||
|
assert.Equal(t, "123a4567-ab1c-12a1-1234-123456ab7890", p.getTeamProjectID())
|
||||||
|
assert.Equal(t, "42", p.getAzureBuildID()) // Don't confuse getAzureBuildID and GetBuildID!
|
||||||
|
assert.Equal(t, "20220318.16", p.GetBuildID()) // buildNumber is used in the UI
|
||||||
|
assert.Equal(t, "2.193.0", p.OrchestratorVersion())
|
||||||
|
assert.Equal(t, "repo-org/repo-name", p.GetJobName())
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAzureDevOpsConfigProvider_GetPipelineStartTime(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
apiInformation map[string]interface{}
|
||||||
|
want time.Time
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Retrieve correct time",
|
||||||
|
apiInformation: map[string]interface{}{"startTime": "2022-03-18T12:30:42.0Z"},
|
||||||
|
want: time.Date(2022, time.March, 18, 12, 30, 42, 0, time.UTC),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty apiInformation",
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
want: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "apiInformation does not contain key",
|
||||||
|
apiInformation: map[string]interface{}{"someKey": "someValue"},
|
||||||
|
want: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "apiInformation contains malformed date",
|
||||||
|
apiInformation: map[string]interface{}{"startTime": "2022-03/18 12:30:42.0Z"},
|
||||||
|
want: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
a := &AzureDevOpsConfigProvider{}
|
||||||
|
a.apiInformation = tt.apiInformation
|
||||||
|
pipelineStartTime := a.GetPipelineStartTime()
|
||||||
|
assert.Equalf(t, tt.want, pipelineStartTime, "GetPipelineStartTime()")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAzureDevOpsConfigProvider_GetBuildStatus(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
want string
|
||||||
|
envVar string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Success",
|
||||||
|
envVar: "Succeeded",
|
||||||
|
want: "SUCCESS",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aborted",
|
||||||
|
envVar: "Canceled",
|
||||||
|
want: "ABORTED",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failure",
|
||||||
|
envVar: "failed",
|
||||||
|
want: "FAILURE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "other",
|
||||||
|
envVar: "some other status",
|
||||||
|
want: "FAILURE",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("AGENT_JOBSTATUS", tt.envVar)
|
||||||
|
a := &AzureDevOpsConfigProvider{}
|
||||||
|
|
||||||
|
assert.Equalf(t, tt.want, a.GetBuildStatus(), "GetBuildStatus()")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAzureDevOpsConfigProvider_getAPIInformation(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
wantHTTPErr bool
|
||||||
|
wantHTTPStatusCodeError bool
|
||||||
|
wantHTTPJSONParseError bool
|
||||||
|
apiInformation map[string]interface{}
|
||||||
|
wantAPIInformation map[string]interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success case",
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
wantAPIInformation: map[string]interface{}{"Success": "Case"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "apiInformation already set",
|
||||||
|
apiInformation: map[string]interface{}{"API info": "set"},
|
||||||
|
wantAPIInformation: map[string]interface{}{"API info": "set"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failed to get response",
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
wantHTTPErr: true,
|
||||||
|
wantAPIInformation: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "response code != 200 http.StatusNoContent",
|
||||||
|
wantHTTPStatusCodeError: true,
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
wantAPIInformation: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parseResponseBodyJson fails",
|
||||||
|
wantHTTPJSONParseError: true,
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
wantAPIInformation: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
a := &AzureDevOpsConfigProvider{
|
||||||
|
apiInformation: tt.apiInformation,
|
||||||
|
}
|
||||||
|
|
||||||
|
a.client.SetOptions(piperhttp.ClientOptions{
|
||||||
|
MaxRequestDuration: 5 * time.Second,
|
||||||
|
Token: "TOKEN",
|
||||||
|
TransportSkipVerification: true,
|
||||||
|
UseDefaultTransport: true, // need to use default transport for http mock
|
||||||
|
MaxRetries: -1,
|
||||||
|
})
|
||||||
|
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("SYSTEM_COLLECTIONURI", "https://dev.azure.com/fabrikamfiber/")
|
||||||
|
os.Setenv("SYSTEM_TEAMPROJECTID", "123a4567-ab1c-12a1-1234-123456ab7890")
|
||||||
|
os.Setenv("BUILD_BUILDID", "1234")
|
||||||
|
|
||||||
|
fakeUrl := "https://dev.azure.com/fabrikamfiber/123a4567-ab1c-12a1-1234-123456ab7890/_apis/build/builds/1234/"
|
||||||
|
httpmock.Activate()
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
httpmock.RegisterResponder("GET", fakeUrl,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
if tt.wantHTTPErr {
|
||||||
|
return nil, errors.New("this error shows up")
|
||||||
|
}
|
||||||
|
if tt.wantHTTPStatusCodeError {
|
||||||
|
return &http.Response{
|
||||||
|
Status: "204",
|
||||||
|
StatusCode: http.StatusNoContent,
|
||||||
|
Request: req,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if tt.wantHTTPJSONParseError {
|
||||||
|
// Intentionally malformed JSON response
|
||||||
|
return httpmock.NewJsonResponse(200, "timestamp:broken")
|
||||||
|
}
|
||||||
|
return httpmock.NewStringResponse(200, "{\"Success\":\"Case\"}"), nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
a.fetchAPIInformation()
|
||||||
|
assert.Equal(t, tt.wantAPIInformation, a.apiInformation)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAzureDevOpsConfigProvider_GetLog(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
want []byte
|
||||||
|
wantErr assert.ErrorAssertionFunc
|
||||||
|
wantHTTPErr bool
|
||||||
|
wantHTTPStatusCodeError bool
|
||||||
|
wantLogCountError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Successfully got log file",
|
||||||
|
want: []byte("Success"),
|
||||||
|
wantErr: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Log count variable not available",
|
||||||
|
want: []byte(""),
|
||||||
|
wantErr: assert.NoError,
|
||||||
|
wantLogCountError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTTP error",
|
||||||
|
want: []byte(""),
|
||||||
|
wantErr: assert.Error,
|
||||||
|
wantHTTPErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Status code error",
|
||||||
|
want: []byte(""),
|
||||||
|
wantErr: assert.NoError,
|
||||||
|
wantHTTPStatusCodeError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
a := &AzureDevOpsConfigProvider{}
|
||||||
|
a.client.SetOptions(piperhttp.ClientOptions{
|
||||||
|
MaxRequestDuration: 5 * time.Second,
|
||||||
|
Token: "TOKEN",
|
||||||
|
TransportSkipVerification: true,
|
||||||
|
UseDefaultTransport: true, // need to use default transport for http mock
|
||||||
|
MaxRetries: -1,
|
||||||
|
})
|
||||||
|
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("SYSTEM_COLLECTIONURI", "https://dev.azure.com/fabrikamfiber/")
|
||||||
|
os.Setenv("SYSTEM_TEAMPROJECTID", "123a4567-ab1c-12a1-1234-123456ab7890")
|
||||||
|
os.Setenv("BUILD_BUILDID", "1234")
|
||||||
|
|
||||||
|
fakeUrl := "https://dev.azure.com/fabrikamfiber/123a4567-ab1c-12a1-1234-123456ab7890/_apis/build/builds/1234/logs"
|
||||||
|
httpmock.Activate()
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
httpmock.RegisterResponder("GET", fakeUrl+"/1",
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
return httpmock.NewStringResponse(200, "Success"), nil
|
||||||
|
})
|
||||||
|
httpmock.RegisterResponder("GET", fakeUrl,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
if tt.wantHTTPErr {
|
||||||
|
return nil, errors.New("this error shows up")
|
||||||
|
}
|
||||||
|
if tt.wantHTTPStatusCodeError {
|
||||||
|
return &http.Response{
|
||||||
|
Status: "204",
|
||||||
|
StatusCode: http.StatusNoContent,
|
||||||
|
Request: req,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if tt.wantLogCountError {
|
||||||
|
return httpmock.NewJsonResponse(200, map[string]interface{}{
|
||||||
|
"some": "value",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return httpmock.NewJsonResponse(200, map[string]interface{}{
|
||||||
|
"count": 1,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
got, err := a.GetLog()
|
||||||
|
if !tt.wantErr(t, err, fmt.Sprintf("GetLog()")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equalf(t, tt.want, got, "GetLog()")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type GitHubActionsConfigProvider struct{}
|
type GitHubActionsConfigProvider struct{}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) InitOrchestratorProvider(settings *OrchestratorSettings) {
|
func (g *GitHubActionsConfigProvider) InitOrchestratorProvider(settings *OrchestratorSettings) {
|
||||||
log.Entry().Debug("Successfully initalized GitHubActions config provider")
|
log.Entry().Debug("Successfully initialized GitHubActions config provider")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) OrchestratorVersion() string {
|
func (g *GitHubActionsConfigProvider) OrchestratorVersion() string {
|
||||||
@ -23,51 +23,55 @@ func (g *GitHubActionsConfigProvider) OrchestratorType() string {
|
|||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetBuildStatus() string {
|
func (g *GitHubActionsConfigProvider) GetBuildStatus() string {
|
||||||
log.Entry().Infof("GetBuildStatus() for GitHub Actions not yet implemented.")
|
log.Entry().Infof("GetBuildStatus() for GitHub Actions not yet implemented.")
|
||||||
return "SUCCESS"
|
return "FAILURE"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetLog() ([]byte, error) {
|
func (g *GitHubActionsConfigProvider) GetLog() ([]byte, error) {
|
||||||
log.Entry().Infof("GetLog() for GitHub Actions not yet implemented.")
|
log.Entry().Infof("GetLog() for GitHub Actions not yet implemented.")
|
||||||
return nil, nil
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetBuildId() string {
|
func (g *GitHubActionsConfigProvider) GetBuildID() string {
|
||||||
log.Entry().Infof("GetBuildId() for GitHub Actions not yet implemented.")
|
log.Entry().Infof("GetBuildID() for GitHub Actions not yet implemented.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetPipelineStartTime() time.Time {
|
func (g *GitHubActionsConfigProvider) GetPipelineStartTime() time.Time {
|
||||||
log.Entry().Infof("GetPipelineStartTime() for GitHub Actions not yet implemented.")
|
log.Entry().Infof("GetPipelineStartTime() for GitHub Actions not yet implemented.")
|
||||||
timestamp, _ := time.Parse(time.UnixDate, "Wed Feb 25 11:06:39 PST 1970")
|
return time.Time{}.UTC()
|
||||||
return timestamp
|
|
||||||
}
|
}
|
||||||
func (g *GitHubActionsConfigProvider) GetStageName() string {
|
func (g *GitHubActionsConfigProvider) GetStageName() string {
|
||||||
return "GITHUB_WORKFLOW" //TODO: is there something like is "stage" in GH Actions?
|
return "GITHUB_WORKFLOW" //TODO: is there something like is "stage" in GH Actions?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GitHubActionsConfigProvider) GetBuildReason() string {
|
||||||
|
log.Entry().Infof("GetBuildReason() for GitHub Actions not yet implemented.")
|
||||||
|
return "n/a"
|
||||||
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetBranch() string {
|
func (g *GitHubActionsConfigProvider) GetBranch() string {
|
||||||
return strings.TrimPrefix(getEnv("GITHUB_REF", "n/a"), "refs/heads/")
|
return strings.TrimPrefix(getEnv("GITHUB_REF", "n/a"), "refs/heads/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetBuildUrl() string {
|
func (g *GitHubActionsConfigProvider) GetBuildURL() string {
|
||||||
return g.GetRepoUrl() + "/actions/runs/" + getEnv("GITHUB_RUN_ID", "n/a")
|
return g.GetRepoURL() + "/actions/runs/" + getEnv("GITHUB_RUN_ID", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetJobUrl() string {
|
func (g *GitHubActionsConfigProvider) GetJobURL() string {
|
||||||
log.Entry().Debugf("Not yet implemented.")
|
log.Entry().Debugf("Not yet implemented.")
|
||||||
return g.GetRepoUrl() + "/actions/runs/" + getEnv("GITHUB_RUN_ID", "n/a")
|
return g.GetRepoURL() + "/actions/runs/" + getEnv("GITHUB_RUN_ID", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetJobName() string {
|
func (g *GitHubActionsConfigProvider) GetJobName() string {
|
||||||
log.Entry().Debugf("GetJobName() for GitHubActions not yet implemented.")
|
log.Entry().Debugf("GetJobName() for GitHubActions not yet implemented.")
|
||||||
return "N/A"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetCommit() string {
|
func (g *GitHubActionsConfigProvider) GetCommit() string {
|
||||||
return getEnv("GITHUB_SHA", "n/a")
|
return getEnv("GITHUB_SHA", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubActionsConfigProvider) GetRepoUrl() string {
|
func (g *GitHubActionsConfigProvider) GetRepoURL() string {
|
||||||
return getEnv("GITHUB_SERVER_URL", "n/a") + getEnv("GITHUB_REPOSITORY", "n/a")
|
return getEnv("GITHUB_SERVER_URL", "n/a") + getEnv("GITHUB_REPOSITORY", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,10 +22,10 @@ func TestGitHubActions(t *testing.T) {
|
|||||||
p, _ := NewOrchestratorSpecificConfigProvider()
|
p, _ := NewOrchestratorSpecificConfigProvider()
|
||||||
|
|
||||||
assert.False(t, p.IsPullRequest())
|
assert.False(t, p.IsPullRequest())
|
||||||
assert.Equal(t, "github.com/foo/bar/actions/runs/42", p.GetBuildUrl())
|
assert.Equal(t, "github.com/foo/bar/actions/runs/42", p.GetBuildURL())
|
||||||
assert.Equal(t, "feat/test-gh-actions", p.GetBranch())
|
assert.Equal(t, "feat/test-gh-actions", p.GetBranch())
|
||||||
assert.Equal(t, "abcdef42713", p.GetCommit())
|
assert.Equal(t, "abcdef42713", p.GetCommit())
|
||||||
assert.Equal(t, "github.com/foo/bar", p.GetRepoUrl())
|
assert.Equal(t, "github.com/foo/bar", p.GetRepoURL())
|
||||||
assert.Equal(t, "GitHubActions", p.OrchestratorType())
|
assert.Equal(t, "GitHubActions", p.OrchestratorType())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package orchestrator
|
package orchestrator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/Jeffail/gabs/v2"
|
||||||
piperHttp "github.com/SAP/jenkins-library/pkg/http"
|
piperHttp "github.com/SAP/jenkins-library/pkg/http"
|
||||||
"github.com/SAP/jenkins-library/pkg/log"
|
"github.com/SAP/jenkins-library/pkg/log"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -9,10 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type JenkinsConfigProvider struct {
|
type JenkinsConfigProvider struct {
|
||||||
client piperHttp.Client
|
client piperHttp.Client
|
||||||
options piperHttp.ClientOptions
|
options piperHttp.ClientOptions
|
||||||
|
apiInformation map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitOrchestratorProvider initializes the Jenkins orchestrator with credentials
|
||||||
func (j *JenkinsConfigProvider) InitOrchestratorProvider(settings *OrchestratorSettings) {
|
func (j *JenkinsConfigProvider) InitOrchestratorProvider(settings *OrchestratorSettings) {
|
||||||
j.client = piperHttp.Client{}
|
j.client = piperHttp.Client{}
|
||||||
j.options = piperHttp.ClientOptions{
|
j.options = piperHttp.ClientOptions{
|
||||||
@ -25,43 +29,51 @@ func (j *JenkinsConfigProvider) InitOrchestratorProvider(settings *OrchestratorS
|
|||||||
log.Entry().Debug("Successfully initialized Jenkins config provider")
|
log.Entry().Debug("Successfully initialized Jenkins config provider")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrchestratorVersion returns the orchestrator version currently running on
|
||||||
func (j *JenkinsConfigProvider) OrchestratorVersion() string {
|
func (j *JenkinsConfigProvider) OrchestratorVersion() string {
|
||||||
return getEnv("JENKINS_VERSION", "n/a")
|
return getEnv("JENKINS_VERSION", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrchestratorType returns the orchestrator type Jenkins
|
||||||
func (j *JenkinsConfigProvider) OrchestratorType() string {
|
func (j *JenkinsConfigProvider) OrchestratorType() string {
|
||||||
return "Jenkins"
|
return "Jenkins"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JenkinsConfigProvider) getAPIInformation() map[string]interface{} {
|
func (j *JenkinsConfigProvider) fetchAPIInformation() {
|
||||||
URL := j.GetBuildUrl() + "api/json"
|
if len(j.apiInformation) == 0 {
|
||||||
|
log.Entry().Debugf("apiInformation is empty, getting infos from API")
|
||||||
|
URL := j.GetBuildURL() + "api/json"
|
||||||
|
log.Entry().Debugf("API URL: %s", URL)
|
||||||
|
response, err := j.client.GetRequest(URL, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().WithError(err).Error("could not get API information from Jenkins")
|
||||||
|
j.apiInformation = map[string]interface{}{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
response, err := j.client.GetRequest(URL, nil, nil)
|
if response.StatusCode != 200 { //http.StatusNoContent
|
||||||
if err != nil {
|
log.Entry().Errorf("Response-Code is %v, could not get timestamp from Jenkins. Setting timestamp to 1970.", response.StatusCode)
|
||||||
log.Entry().WithError(err).Error("could not get api information from Jenkins")
|
j.apiInformation = map[string]interface{}{}
|
||||||
return map[string]interface{}{}
|
return
|
||||||
|
}
|
||||||
|
err = piperHttp.ParseHTTPResponseBodyJSON(response, &j.apiInformation)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().WithError(err).Errorf("could not parse HTTP response body")
|
||||||
|
j.apiInformation = map[string]interface{}{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Entry().Debugf("successfully retrieved apiInformation")
|
||||||
|
} else {
|
||||||
|
log.Entry().Debugf("apiInformation already set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.StatusCode != 200 { //http.StatusNoContent
|
|
||||||
log.Entry().Errorf("Response-Code is %v . \n Could not get timestamp from Jenkins. Setting timestamp to 1970.", response.StatusCode)
|
|
||||||
return map[string]interface{}{}
|
|
||||||
}
|
|
||||||
var responseInterface map[string]interface{}
|
|
||||||
err = piperHttp.ParseHTTPResponseBodyJSON(response, &responseInterface)
|
|
||||||
if err != nil {
|
|
||||||
log.Entry().Error(err)
|
|
||||||
return map[string]interface{}{}
|
|
||||||
}
|
|
||||||
return responseInterface
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBuildInformation
|
// GetBuildStatus returns build status of the current job
|
||||||
func (j *JenkinsConfigProvider) GetBuildStatus() string {
|
func (j *JenkinsConfigProvider) GetBuildStatus() string {
|
||||||
responseInterface := j.getAPIInformation()
|
j.fetchAPIInformation()
|
||||||
|
if val, ok := j.apiInformation["result"]; ok {
|
||||||
if val, ok := responseInterface["result"]; ok {
|
|
||||||
// cases in ADO: succeeded, failed, canceled, none, partiallySucceeded
|
// cases in ADO: succeeded, failed, canceled, none, partiallySucceeded
|
||||||
switch result := responseInterface["result"]; result {
|
switch result := val; result {
|
||||||
case "SUCCESS":
|
case "SUCCESS":
|
||||||
return "SUCCESS"
|
return "SUCCESS"
|
||||||
case "ABORTED":
|
case "ABORTED":
|
||||||
@ -70,50 +82,47 @@ func (j *JenkinsConfigProvider) GetBuildStatus() string {
|
|||||||
// FAILURE, NOT_BUILT
|
// FAILURE, NOT_BUILT
|
||||||
return "FAILURE"
|
return "FAILURE"
|
||||||
}
|
}
|
||||||
return val.(string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "FAILURE"
|
return "FAILURE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLog returns the logfile from the current job as byte object
|
||||||
func (j *JenkinsConfigProvider) GetLog() ([]byte, error) {
|
func (j *JenkinsConfigProvider) GetLog() ([]byte, error) {
|
||||||
URL := j.GetBuildUrl() + "consoleText"
|
URL := j.GetBuildURL() + "consoleText"
|
||||||
|
|
||||||
response, err := j.client.GetRequest(URL, nil, nil)
|
response, err := j.client.GetRequest(URL, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, errors.Wrapf(err, "Could not read Jenkins logfile. %v", err)
|
return []byte{}, errors.Wrapf(err, "could not GET Jenkins log file %v", err)
|
||||||
}
|
} else if response.StatusCode != 200 {
|
||||||
if response.StatusCode != 200 {
|
log.Entry().Error("response code !=200 could not get log information from Jenkins, returning with empty log.")
|
||||||
log.Entry().Error("Could not get log information from Jenkins. Returning with empty log.")
|
|
||||||
return []byte{}, nil
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
logFile, err := ioutil.ReadAll(response.Body)
|
logFile, err := ioutil.ReadAll(response.Body)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, errors.Wrapf(err, "could not read Jenkins logfile from request. %v", err)
|
return []byte{}, errors.Wrapf(err, "could not read Jenkins log file from request %v", err)
|
||||||
}
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
return logFile, nil
|
return logFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPipelineStartTime returns the pipeline start time in UTC
|
||||||
func (j *JenkinsConfigProvider) GetPipelineStartTime() time.Time {
|
func (j *JenkinsConfigProvider) GetPipelineStartTime() time.Time {
|
||||||
URL := j.GetBuildUrl() + "api/json"
|
URL := j.GetBuildURL() + "api/json"
|
||||||
|
|
||||||
response, err := j.client.GetRequest(URL, nil, nil)
|
response, err := j.client.GetRequest(URL, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Entry().Error(err)
|
log.Entry().WithError(err).Errorf("could not getRequest to URL %s", URL)
|
||||||
|
return time.Time{}.UTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.StatusCode != 200 { //http.StatusNoContent -> also empty log!
|
if response.StatusCode != 200 { //http.StatusNoContent -> also empty log!
|
||||||
log.Entry().Errorf("Response-Code is %v . \n Could not get timestamp from Jenkins. Setting timestamp to 1970.", response.StatusCode)
|
log.Entry().Errorf("response code is %v . \n Could not get timestamp from Jenkins. Setting timestamp to 1970.", response.StatusCode)
|
||||||
return time.Unix(1, 0)
|
return time.Time{}.UTC()
|
||||||
}
|
}
|
||||||
var responseInterface map[string]interface{}
|
var responseInterface map[string]interface{}
|
||||||
err = piperHttp.ParseHTTPResponseBodyJSON(response, &responseInterface)
|
err = piperHttp.ParseHTTPResponseBodyJSON(response, &responseInterface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Entry().Error(err)
|
log.Entry().WithError(err).Infof("could not parse http response, returning 1970")
|
||||||
|
return time.Time{}.UTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
rawTimeStamp := responseInterface["timestamp"].(float64)
|
rawTimeStamp := responseInterface["timestamp"].(float64)
|
||||||
@ -121,45 +130,88 @@ func (j *JenkinsConfigProvider) GetPipelineStartTime() time.Time {
|
|||||||
|
|
||||||
log.Entry().Debugf("Pipeline start time: %v", timeStamp.String())
|
log.Entry().Debugf("Pipeline start time: %v", timeStamp.String())
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
return timeStamp
|
return timeStamp.UTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetJobName returns the job name of the current job e.g. foo/bar/BRANCH
|
||||||
func (j *JenkinsConfigProvider) GetJobName() string {
|
func (j *JenkinsConfigProvider) GetJobName() string {
|
||||||
return getEnv("JOB_NAME", "n/a")
|
return getEnv("JOB_NAME", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JenkinsConfigProvider) GetJobUrl() string {
|
// GetJobURL returns the current job URL e.g. https://jaas.url/job/foo/job/bar/job/main
|
||||||
|
func (j *JenkinsConfigProvider) GetJobURL() string {
|
||||||
return getEnv("JOB_URL", "n/a")
|
return getEnv("JOB_URL", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getJenkinsHome returns the jenkins home e.g. /var/lib/jenkins
|
||||||
func (j *JenkinsConfigProvider) getJenkinsHome() string {
|
func (j *JenkinsConfigProvider) getJenkinsHome() string {
|
||||||
return getEnv("JENKINS_HOME", "n/a")
|
return getEnv("JENKINS_HOME", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JenkinsConfigProvider) GetBuildId() string {
|
// GetBuildID returns the build ID of the current job, e.g. 1234
|
||||||
|
func (j *JenkinsConfigProvider) GetBuildID() string {
|
||||||
return getEnv("BUILD_ID", "n/a")
|
return getEnv("BUILD_ID", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *JenkinsConfigProvider) GetStageName() string {
|
// GetStageName returns the stage name the job is currently in, e.g. Promote
|
||||||
|
func (j *JenkinsConfigProvider) GetStageName() string {
|
||||||
return getEnv("STAGE_NAME", "n/a")
|
return getEnv("STAGE_NAME", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GetBuildReason returns the build reason of the current build
|
||||||
|
func (j *JenkinsConfigProvider) GetBuildReason() string {
|
||||||
|
j.fetchAPIInformation()
|
||||||
|
marshal, err := json.Marshal(j.apiInformation)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().WithError(err).Debugf("could not marshal apiInformation")
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
jsonParsed, err := gabs.ParseJSON(marshal)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().WithError(err).Debugf("could not parse apiInformation")
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
for _, child := range jsonParsed.S("actions").Children() {
|
||||||
|
class := child.S("_class")
|
||||||
|
if class.String() == "\"hudson.model.CauseAction\"" {
|
||||||
|
for _, val := range child.S("causes").Children() {
|
||||||
|
subclass := val.S("_class")
|
||||||
|
if subclass.String() == "\"hudson.model.Cause$UserIdCause\"" {
|
||||||
|
return "Manual"
|
||||||
|
} else if subclass.String() == "\"hudson.triggers.TimerTrigger$TimerTriggerCause\"" {
|
||||||
|
return "Schedule"
|
||||||
|
} else {
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBranch returns the branch name, only works with the git plugin enabled
|
||||||
func (j *JenkinsConfigProvider) GetBranch() string {
|
func (j *JenkinsConfigProvider) GetBranch() string {
|
||||||
return getEnv("BRANCH_NAME", "n/a")
|
return getEnv("BRANCH_NAME", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JenkinsConfigProvider) GetBuildUrl() string {
|
// GetBuildURL returns the build url, e.g. https://jaas.url/job/foo/job/bar/job/main/1234/
|
||||||
|
func (j *JenkinsConfigProvider) GetBuildURL() string {
|
||||||
return getEnv("BUILD_URL", "n/a")
|
return getEnv("BUILD_URL", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCommit returns the commit SHA from the current build, only works with the git plugin enabled
|
||||||
func (j *JenkinsConfigProvider) GetCommit() string {
|
func (j *JenkinsConfigProvider) GetCommit() string {
|
||||||
return getEnv("GIT_COMMIT", "n/a")
|
return getEnv("GIT_COMMIT", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JenkinsConfigProvider) GetRepoUrl() string {
|
// GetRepoURL returns the repo URL of the current build, only works with the git plugin enabled
|
||||||
|
func (j *JenkinsConfigProvider) GetRepoURL() string {
|
||||||
return getEnv("GIT_URL", "n/a")
|
return getEnv("GIT_URL", "n/a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPullRequestConfig returns the pull request config
|
||||||
func (j *JenkinsConfigProvider) GetPullRequestConfig() PullRequestConfig {
|
func (j *JenkinsConfigProvider) GetPullRequestConfig() PullRequestConfig {
|
||||||
return PullRequestConfig{
|
return PullRequestConfig{
|
||||||
Branch: getEnv("CHANGE_BRANCH", "n/a"),
|
Branch: getEnv("CHANGE_BRANCH", "n/a"),
|
||||||
@ -168,6 +220,7 @@ func (j *JenkinsConfigProvider) GetPullRequestConfig() PullRequestConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPullRequest returns boolean indicating if current job is a PR
|
||||||
func (j *JenkinsConfigProvider) IsPullRequest() bool {
|
func (j *JenkinsConfigProvider) IsPullRequest() bool {
|
||||||
return truthy("CHANGE_ID")
|
return truthy("CHANGE_ID")
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
package orchestrator
|
package orchestrator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||||
|
"github.com/jarcoal/httpmock"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestJenkins(t *testing.T) {
|
func TestJenkins(t *testing.T) {
|
||||||
@ -12,7 +19,7 @@ func TestJenkins(t *testing.T) {
|
|||||||
defer resetEnv(os.Environ())
|
defer resetEnv(os.Environ())
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
os.Setenv("JENKINS_URL", "FOO BAR BAZ")
|
os.Setenv("JENKINS_URL", "FOO BAR BAZ")
|
||||||
os.Setenv("BUILD_URL", "jaas.com/foo/bar/main/42")
|
os.Setenv("BUILD_URL", "https://jaas.url/job/foo/job/bar/job/main/1234/")
|
||||||
os.Setenv("BRANCH_NAME", "main")
|
os.Setenv("BRANCH_NAME", "main")
|
||||||
os.Setenv("GIT_COMMIT", "abcdef42713")
|
os.Setenv("GIT_COMMIT", "abcdef42713")
|
||||||
os.Setenv("GIT_URL", "github.com/foo/bar")
|
os.Setenv("GIT_URL", "github.com/foo/bar")
|
||||||
@ -20,10 +27,10 @@ func TestJenkins(t *testing.T) {
|
|||||||
p, _ := NewOrchestratorSpecificConfigProvider()
|
p, _ := NewOrchestratorSpecificConfigProvider()
|
||||||
|
|
||||||
assert.False(t, p.IsPullRequest())
|
assert.False(t, p.IsPullRequest())
|
||||||
assert.Equal(t, "jaas.com/foo/bar/main/42", p.GetBuildUrl())
|
assert.Equal(t, "https://jaas.url/job/foo/job/bar/job/main/1234/", p.GetBuildURL())
|
||||||
assert.Equal(t, "main", p.GetBranch())
|
assert.Equal(t, "main", p.GetBranch())
|
||||||
assert.Equal(t, "abcdef42713", p.GetCommit())
|
assert.Equal(t, "abcdef42713", p.GetCommit())
|
||||||
assert.Equal(t, "github.com/foo/bar", p.GetRepoUrl())
|
assert.Equal(t, "github.com/foo/bar", p.GetRepoURL())
|
||||||
assert.Equal(t, "Jenkins", p.OrchestratorType())
|
assert.Equal(t, "Jenkins", p.OrchestratorType())
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -43,4 +50,449 @@ func TestJenkins(t *testing.T) {
|
|||||||
assert.Equal(t, "main", c.Base)
|
assert.Equal(t, "main", c.Base)
|
||||||
assert.Equal(t, "42", c.Key)
|
assert.Equal(t, "42", c.Key)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("env variables", func(t *testing.T) {
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("JENKINS_HOME", "/var/lib/jenkins")
|
||||||
|
os.Setenv("BUILD_ID", "1234")
|
||||||
|
os.Setenv("JOB_URL", "https://jaas.url/job/foo/job/bar/job/main")
|
||||||
|
os.Setenv("JENKINS_VERSION", "42")
|
||||||
|
os.Setenv("JOB_NAME", "foo/bar/BRANCH")
|
||||||
|
os.Setenv("STAGE_NAME", "Promote")
|
||||||
|
os.Setenv("BUILD_URL", "https://jaas.url/job/foo/job/bar/job/main/1234/")
|
||||||
|
os.Setenv("STAGE_NAME", "Promote")
|
||||||
|
|
||||||
|
p := JenkinsConfigProvider{}
|
||||||
|
|
||||||
|
assert.Equal(t, "/var/lib/jenkins", p.getJenkinsHome())
|
||||||
|
assert.Equal(t, "1234", p.GetBuildID())
|
||||||
|
assert.Equal(t, "https://jaas.url/job/foo/job/bar/job/main", p.GetJobURL())
|
||||||
|
assert.Equal(t, "42", p.OrchestratorVersion())
|
||||||
|
assert.Equal(t, "Jenkins", p.OrchestratorType())
|
||||||
|
assert.Equal(t, "foo/bar/BRANCH", p.GetJobName())
|
||||||
|
assert.Equal(t, "Promote", p.GetStageName())
|
||||||
|
assert.Equal(t, "https://jaas.url/job/foo/job/bar/job/main/1234/", p.GetBuildURL())
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJenkinsConfigProvider_GetPipelineStartTime(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
client piperhttp.Client
|
||||||
|
options piperhttp.ClientOptions
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want time.Time
|
||||||
|
wantHTTPErr bool
|
||||||
|
wantHTTPStatusCodeError bool
|
||||||
|
wantHTTPJSONParseError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Retrieve correct time",
|
||||||
|
want: time.Date(2022, time.March, 21, 22, 30, 0, 0, time.UTC),
|
||||||
|
wantHTTPErr: false,
|
||||||
|
wantHTTPStatusCodeError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ParseHTTPResponseBodyJSON error",
|
||||||
|
want: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
wantHTTPErr: false,
|
||||||
|
wantHTTPStatusCodeError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GetRequest fails",
|
||||||
|
want: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
wantHTTPErr: true,
|
||||||
|
wantHTTPStatusCodeError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "response code != 200 http.StatusNoContent",
|
||||||
|
want: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
wantHTTPErr: false,
|
||||||
|
wantHTTPStatusCodeError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parseResponseBodyJson fails",
|
||||||
|
want: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
wantHTTPErr: false,
|
||||||
|
wantHTTPStatusCodeError: false,
|
||||||
|
wantHTTPJSONParseError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
j := &JenkinsConfigProvider{
|
||||||
|
client: piperhttp.Client{},
|
||||||
|
}
|
||||||
|
j.client.SetOptions(piperhttp.ClientOptions{
|
||||||
|
MaxRequestDuration: 5 * time.Second,
|
||||||
|
Token: "TOKEN",
|
||||||
|
TransportSkipVerification: true,
|
||||||
|
UseDefaultTransport: true,
|
||||||
|
MaxRetries: -1,
|
||||||
|
})
|
||||||
|
httpmock.Activate()
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
buildURl := "https://jaas.url/job/foo/job/bar/job/main/1234/"
|
||||||
|
os.Setenv("BUILD_URL", buildURl)
|
||||||
|
|
||||||
|
fakeUrl := buildURl + "api/json"
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
httpmock.RegisterResponder("GET", fakeUrl,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
if tt.wantHTTPErr {
|
||||||
|
return nil, errors.New("this error shows up")
|
||||||
|
}
|
||||||
|
if tt.wantHTTPStatusCodeError {
|
||||||
|
return &http.Response{
|
||||||
|
Status: "204",
|
||||||
|
StatusCode: http.StatusNoContent,
|
||||||
|
Request: req,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if tt.wantHTTPJSONParseError {
|
||||||
|
// Intentionally malformed JSON response
|
||||||
|
return httpmock.NewJsonResponse(200, "timestamp:asdffd")
|
||||||
|
}
|
||||||
|
return httpmock.NewStringResponse(200, "{\"timestamp\":1647901800932,\"url\":\"https://jaas.url/view/piperpipelines/job/foo/job/bar/job/main/3731/\"}"), nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equalf(t, tt.want, j.GetPipelineStartTime(), "GetPipelineStartTime()")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJenkinsConfigProvider_GetBuildStatus(t *testing.T) {
|
||||||
|
apiSuccess := []byte(`{ "queueId":376475,
|
||||||
|
"result":"SUCCESS",
|
||||||
|
"timestamp":1647946800925
|
||||||
|
}`)
|
||||||
|
apiFailure := []byte(`{ "queueId":376475,
|
||||||
|
"result":"FAILURE",
|
||||||
|
"timestamp":1647946800925
|
||||||
|
}`)
|
||||||
|
apiAborted := []byte(`{ "queueId":376475,
|
||||||
|
"result":"ABORTED",
|
||||||
|
"timestamp":1647946800925
|
||||||
|
}`)
|
||||||
|
|
||||||
|
apiOTHER := []byte(`{ "queueId":376475,
|
||||||
|
"result":"SOMETHING",
|
||||||
|
"timestamp":1647946800925
|
||||||
|
}`)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
want string
|
||||||
|
apiInformation []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "SUCCESS",
|
||||||
|
apiInformation: apiSuccess,
|
||||||
|
want: "SUCCESS",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ABORTED",
|
||||||
|
apiInformation: apiAborted,
|
||||||
|
want: "ABORTED",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FAILURE",
|
||||||
|
apiInformation: apiFailure,
|
||||||
|
want: "FAILURE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Unknown FAILURE",
|
||||||
|
apiInformation: apiOTHER,
|
||||||
|
want: "FAILURE",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var apiInformation map[string]interface{}
|
||||||
|
err := json.Unmarshal(tt.apiInformation, &apiInformation)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not parse json:", err)
|
||||||
|
}
|
||||||
|
j := &JenkinsConfigProvider{
|
||||||
|
apiInformation: apiInformation,
|
||||||
|
}
|
||||||
|
assert.Equalf(t, tt.want, j.GetBuildStatus(), "GetBuildStatus()")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJenkinsConfigProvider_GetBuildReason(t *testing.T) {
|
||||||
|
apiJsonSchedule := []byte(`{
|
||||||
|
"_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun",
|
||||||
|
"actions": [{
|
||||||
|
"_class": "hudson.model.CauseAction",
|
||||||
|
"causes": [{
|
||||||
|
"_class": "hudson.triggers.TimerTrigger$TimerTriggerCause",
|
||||||
|
"shortDescription": "Started by timer"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_class": "jenkins.metrics.impl.TimeInQueueAction",
|
||||||
|
"blockedDurationMillis": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
apiJSONManual := []byte(`{
|
||||||
|
"_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun",
|
||||||
|
"actions": [{
|
||||||
|
"_class": "hudson.model.CauseAction",
|
||||||
|
"causes": [{
|
||||||
|
"_class": "hudson.model.Cause$UserIdCause",
|
||||||
|
"shortDescription": "Started by user John Doe",
|
||||||
|
"userId": "i12345",
|
||||||
|
"userName": "John Doe"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_class": "jenkins.metrics.impl.TimeInQueueAction",
|
||||||
|
"blockedDurationMillis": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
apiJSONUnknown := []byte(`{
|
||||||
|
"_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun",
|
||||||
|
"actions": [{
|
||||||
|
"_class": "hudson.model.CauseAction",
|
||||||
|
"causes": [{
|
||||||
|
"_class": "hudson.model.RandomThingHere",
|
||||||
|
"shortDescription": "Something"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_class": "jenkins.metrics.impl.TimeInQueueAction",
|
||||||
|
"blockedDurationMillis": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
apiInformation []byte
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Manual trigger",
|
||||||
|
apiInformation: apiJSONManual,
|
||||||
|
want: "Manual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Scheduled trigger",
|
||||||
|
apiInformation: apiJsonSchedule,
|
||||||
|
want: "Schedule",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Unknown",
|
||||||
|
apiInformation: apiJSONUnknown,
|
||||||
|
want: "Unknown",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty api",
|
||||||
|
apiInformation: []byte(`{}`),
|
||||||
|
want: "Unknown",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
||||||
|
var apiInformation map[string]interface{}
|
||||||
|
err := json.Unmarshal(tt.apiInformation, &apiInformation)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not parse json:", err)
|
||||||
|
}
|
||||||
|
j := &JenkinsConfigProvider{apiInformation: apiInformation}
|
||||||
|
|
||||||
|
assert.Equalf(t, tt.want, j.GetBuildReason(), "GetBuildReason()")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJenkinsConfigProvider_getAPIInformation(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
wantHTTPErr bool
|
||||||
|
wantHTTPStatusCodeError bool
|
||||||
|
wantHTTPJSONParseError bool
|
||||||
|
apiInformation map[string]interface{}
|
||||||
|
wantAPIInformation map[string]interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success case",
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
wantAPIInformation: map[string]interface{}{"Success": "Case"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "apiInformation already set",
|
||||||
|
apiInformation: map[string]interface{}{"API info": "set"},
|
||||||
|
wantAPIInformation: map[string]interface{}{"API info": "set"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failed to get response",
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
wantHTTPErr: true,
|
||||||
|
wantAPIInformation: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "response code != 200 http.StatusNoContent",
|
||||||
|
wantHTTPStatusCodeError: true,
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
wantAPIInformation: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parseResponseBodyJson fails",
|
||||||
|
wantHTTPJSONParseError: true,
|
||||||
|
apiInformation: map[string]interface{}{},
|
||||||
|
wantAPIInformation: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
j := &JenkinsConfigProvider{
|
||||||
|
apiInformation: tt.apiInformation,
|
||||||
|
}
|
||||||
|
j.client.SetOptions(piperhttp.ClientOptions{
|
||||||
|
MaxRequestDuration: 5 * time.Second,
|
||||||
|
Token: "TOKEN",
|
||||||
|
TransportSkipVerification: true,
|
||||||
|
UseDefaultTransport: true, // need to use default transport for http mock
|
||||||
|
MaxRetries: -1,
|
||||||
|
})
|
||||||
|
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("BUILD_URL", "https://jaas.url/job/foo/job/bar/job/main/1234/")
|
||||||
|
|
||||||
|
fakeUrl := "https://jaas.url/job/foo/job/bar/job/main/1234/api/json"
|
||||||
|
httpmock.Activate()
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
httpmock.RegisterResponder("GET", fakeUrl,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
if tt.wantHTTPErr {
|
||||||
|
return nil, errors.New("this error shows up")
|
||||||
|
}
|
||||||
|
if tt.wantHTTPStatusCodeError {
|
||||||
|
return &http.Response{
|
||||||
|
Status: "204",
|
||||||
|
StatusCode: http.StatusNoContent,
|
||||||
|
Request: req,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if tt.wantHTTPJSONParseError {
|
||||||
|
// Intentionally malformed JSON response
|
||||||
|
return httpmock.NewJsonResponse(200, "timestamp:broken")
|
||||||
|
}
|
||||||
|
return httpmock.NewStringResponse(200, "{\"Success\":\"Case\"}"), nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
j.fetchAPIInformation()
|
||||||
|
assert.Equal(t, tt.wantAPIInformation, j.apiInformation)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJenkinsConfigProvider_GetLog(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
want []byte
|
||||||
|
wantErr assert.ErrorAssertionFunc
|
||||||
|
wantHTTPErr bool
|
||||||
|
wantHTTPStatusCodeError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Successfully got log file",
|
||||||
|
want: []byte("Success!"),
|
||||||
|
wantErr: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTTP error",
|
||||||
|
want: []byte(""),
|
||||||
|
wantErr: assert.Error,
|
||||||
|
wantHTTPErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Status code error",
|
||||||
|
want: []byte(""),
|
||||||
|
wantErr: assert.NoError,
|
||||||
|
wantHTTPStatusCodeError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
j := &JenkinsConfigProvider{}
|
||||||
|
j.client.SetOptions(piperhttp.ClientOptions{
|
||||||
|
MaxRequestDuration: 5 * time.Second,
|
||||||
|
Token: "TOKEN",
|
||||||
|
TransportSkipVerification: true,
|
||||||
|
UseDefaultTransport: true, // need to use default transport for http mock
|
||||||
|
MaxRetries: -1,
|
||||||
|
})
|
||||||
|
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("BUILD_URL", "https://jaas.url/job/foo/job/bar/job/main/1234/")
|
||||||
|
|
||||||
|
fakeUrl := "https://jaas.url/job/foo/job/bar/job/main/1234/consoleText"
|
||||||
|
httpmock.Activate()
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
httpmock.RegisterResponder("GET", fakeUrl,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
if tt.wantHTTPErr {
|
||||||
|
return nil, errors.New("this error shows up")
|
||||||
|
}
|
||||||
|
if tt.wantHTTPStatusCodeError {
|
||||||
|
return &http.Response{
|
||||||
|
Status: "204",
|
||||||
|
StatusCode: http.StatusNoContent,
|
||||||
|
Request: req,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return httpmock.NewStringResponse(200, "Success!"), nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
got, err := j.GetLog()
|
||||||
|
if !tt.wantErr(t, err, fmt.Sprintf("GetLog()")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equalf(t, tt.want, got, "GetLog()")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJenkinsConfigProvider_InitOrchestratorProvider(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
settings *OrchestratorSettings
|
||||||
|
apiInformation map[string]interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Init, test empty apiInformation",
|
||||||
|
settings: &OrchestratorSettings{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
j := &JenkinsConfigProvider{}
|
||||||
|
j.InitOrchestratorProvider(tt.settings)
|
||||||
|
var expected map[string]interface{}
|
||||||
|
assert.Equal(t, j.apiInformation, expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,17 +22,18 @@ type OrchestratorSpecificConfigProviding interface {
|
|||||||
OrchestratorVersion() string
|
OrchestratorVersion() string
|
||||||
GetStageName() string
|
GetStageName() string
|
||||||
GetBranch() string
|
GetBranch() string
|
||||||
GetBuildUrl() string
|
GetBuildURL() string
|
||||||
GetBuildId() string
|
GetBuildID() string
|
||||||
GetJobUrl() string
|
GetJobURL() string
|
||||||
GetJobName() string
|
GetJobName() string
|
||||||
GetCommit() string
|
GetCommit() string
|
||||||
GetPullRequestConfig() PullRequestConfig
|
GetPullRequestConfig() PullRequestConfig
|
||||||
GetRepoUrl() string
|
GetRepoURL() string
|
||||||
IsPullRequest() bool
|
IsPullRequest() bool
|
||||||
GetLog() ([]byte, error)
|
GetLog() ([]byte, error)
|
||||||
GetPipelineStartTime() time.Time
|
GetPipelineStartTime() time.Time
|
||||||
GetBuildStatus() string
|
GetBuildStatus() string
|
||||||
|
GetBuildReason() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PullRequestConfig struct {
|
type PullRequestConfig struct {
|
||||||
@ -41,6 +42,7 @@ type PullRequestConfig struct {
|
|||||||
Key string
|
Key string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrchestratorSettings struct to set orchestrator specific settings e.g. Jenkins credentials
|
||||||
type OrchestratorSettings struct {
|
type OrchestratorSettings struct {
|
||||||
JenkinsUser string
|
JenkinsUser string
|
||||||
JenkinsToken string
|
JenkinsToken string
|
||||||
@ -60,6 +62,7 @@ func NewOrchestratorSpecificConfigProvider() (OrchestratorSpecificConfigProvidin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DetectOrchestrator returns the name of the current orchestrator e.g. Jenkins, Azure, Unknown
|
||||||
func DetectOrchestrator() Orchestrator {
|
func DetectOrchestrator() Orchestrator {
|
||||||
if isAzure() {
|
if isAzure() {
|
||||||
return Orchestrator(AzureDevOps)
|
return Orchestrator(AzureDevOps)
|
||||||
|
@ -7,75 +7,96 @@ import (
|
|||||||
|
|
||||||
type UnknownOrchestratorConfigProvider struct{}
|
type UnknownOrchestratorConfigProvider struct{}
|
||||||
|
|
||||||
|
// InitOrchestratorProvider returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) InitOrchestratorProvider(settings *OrchestratorSettings) {
|
func (u *UnknownOrchestratorConfigProvider) InitOrchestratorProvider(settings *OrchestratorSettings) {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrchestratorVersion returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) OrchestratorVersion() string {
|
func (u *UnknownOrchestratorConfigProvider) OrchestratorVersion() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
|
||||||
return "N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetBuildStatus() string {
|
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
|
||||||
return "SUCCESS"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetBuildId() string {
|
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBuildStatus returns n/a for the unknownOrchestrator
|
||||||
|
func (u *UnknownOrchestratorConfigProvider) GetBuildStatus() string {
|
||||||
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
|
return "FAILURE"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBuildReason returns n/a for the unknownOrchestrator
|
||||||
|
func (u *UnknownOrchestratorConfigProvider) GetBuildReason() string {
|
||||||
|
log.Entry().Infof("Unknown orchestrator - returning default values.")
|
||||||
|
return "n/a"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBuildID returns n/a for the unknownOrchestrator
|
||||||
|
func (u *UnknownOrchestratorConfigProvider) GetBuildID() string {
|
||||||
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
|
return "n/a"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJobName returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetJobName() string {
|
func (u *UnknownOrchestratorConfigProvider) GetJobName() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrchestratorType returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) OrchestratorType() string {
|
func (u *UnknownOrchestratorConfigProvider) OrchestratorType() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLog returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetLog() ([]byte, error) {
|
func (u *UnknownOrchestratorConfigProvider) GetLog() ([]byte, error) {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return nil, nil
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPipelineStartTime returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetPipelineStartTime() time.Time {
|
func (u *UnknownOrchestratorConfigProvider) GetPipelineStartTime() time.Time {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
timestamp, _ := time.Parse(time.UnixDate, "Wed Feb 25 11:06:39 PST 1970")
|
return time.Time{}.UTC()
|
||||||
return timestamp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStageName returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetStageName() string {
|
func (u *UnknownOrchestratorConfigProvider) GetStageName() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBranch returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetBranch() string {
|
func (u *UnknownOrchestratorConfigProvider) GetBranch() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetBuildUrl() string {
|
// GetBuildURL returns n/a for the unknownOrchestrator
|
||||||
|
func (u *UnknownOrchestratorConfigProvider) GetBuildURL() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetJobUrl() string {
|
// GetJobURL returns n/a for the unknownOrchestrator
|
||||||
|
func (u *UnknownOrchestratorConfigProvider) GetJobURL() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCommit returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetCommit() string {
|
func (u *UnknownOrchestratorConfigProvider) GetCommit() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetRepoUrl() string {
|
// GetRepoURL returns n/a for the unknownOrchestrator
|
||||||
|
func (u *UnknownOrchestratorConfigProvider) GetRepoURL() string {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return "n/a"
|
return "n/a"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPullRequestConfig returns n/a for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) GetPullRequestConfig() PullRequestConfig {
|
func (u *UnknownOrchestratorConfigProvider) GetPullRequestConfig() PullRequestConfig {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return PullRequestConfig{
|
return PullRequestConfig{
|
||||||
@ -85,6 +106,7 @@ func (u *UnknownOrchestratorConfigProvider) GetPullRequestConfig() PullRequestCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPullRequest returns false for the unknownOrchestrator
|
||||||
func (u *UnknownOrchestratorConfigProvider) IsPullRequest() bool {
|
func (u *UnknownOrchestratorConfigProvider) IsPullRequest() bool {
|
||||||
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
log.Entry().Warning("Unknown orchestrator - returning default values.")
|
||||||
return false
|
return false
|
||||||
|
@ -3,6 +3,7 @@ package orchestrator
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -15,10 +16,10 @@ func TestUnknownOrchestrator(t *testing.T) {
|
|||||||
p, _ := NewOrchestratorSpecificConfigProvider()
|
p, _ := NewOrchestratorSpecificConfigProvider()
|
||||||
|
|
||||||
assert.False(t, p.IsPullRequest())
|
assert.False(t, p.IsPullRequest())
|
||||||
assert.Equal(t, "n/a", p.GetBuildUrl())
|
assert.Equal(t, "n/a", p.GetBuildURL())
|
||||||
assert.Equal(t, "n/a", p.GetBranch())
|
assert.Equal(t, "n/a", p.GetBranch())
|
||||||
assert.Equal(t, "n/a", p.GetCommit())
|
assert.Equal(t, "n/a", p.GetCommit())
|
||||||
assert.Equal(t, "n/a", p.GetRepoUrl())
|
assert.Equal(t, "n/a", p.GetRepoURL())
|
||||||
assert.Equal(t, "Unknown", p.OrchestratorType())
|
assert.Equal(t, "Unknown", p.OrchestratorType())
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -34,4 +35,24 @@ func TestUnknownOrchestrator(t *testing.T) {
|
|||||||
assert.Equal(t, "n/a", c.Base)
|
assert.Equal(t, "n/a", c.Base)
|
||||||
assert.Equal(t, "n/a", c.Key)
|
assert.Equal(t, "n/a", c.Key)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("env variables", func(t *testing.T) {
|
||||||
|
defer resetEnv(os.Environ())
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
p := UnknownOrchestratorConfigProvider{}
|
||||||
|
|
||||||
|
assert.Equal(t, "n/a", p.OrchestratorVersion())
|
||||||
|
assert.Equal(t, "n/a", p.GetBuildID())
|
||||||
|
assert.Equal(t, "n/a", p.GetJobName())
|
||||||
|
assert.Equal(t, "Unknown", p.OrchestratorType())
|
||||||
|
assert.Equal(t, time.Time{}.UTC(), p.GetPipelineStartTime())
|
||||||
|
assert.Equal(t, "FAILURE", p.GetBuildStatus())
|
||||||
|
assert.Equal(t, "n/a", p.GetRepoURL())
|
||||||
|
assert.Equal(t, "n/a", p.GetBuildURL())
|
||||||
|
assert.Equal(t, "n/a", p.GetStageName())
|
||||||
|
log, err := p.GetLog()
|
||||||
|
assert.Equal(t, []byte{}, log)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ type BaseData struct {
|
|||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
StepName string `json:"e_3"` // set by step generator
|
StepName string `json:"e_3"` // set by step generator
|
||||||
StageName string `json:"e_10"`
|
StageName string `json:"e_10"`
|
||||||
PipelineURLHash string `json:"e_4"` // defaults to sha1 of provider.GetBuildUrl()
|
PipelineURLHash string `json:"e_4"` // defaults to sha1 of provider.GetBuildURL()
|
||||||
BuildURLHash string `json:"e_5"` // defaults to sha1 of provider.GetJobUrl()
|
BuildURLHash string `json:"e_5"` // defaults to sha1 of provider.GetJobURL()
|
||||||
Orchestrator string `json:"e_14"` // defaults to provider.OrchestratorType()
|
Orchestrator string `json:"e_14"` // defaults to provider.OrchestratorType()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,13 +88,13 @@ func (t *Telemetry) Initialize(telemetryDisabled bool, stepName string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telemetry) getPipelineURLHash() string {
|
func (t *Telemetry) getPipelineURLHash() string {
|
||||||
jobUrl := t.provider.GetJobUrl()
|
jobURL := t.provider.GetJobURL()
|
||||||
return t.toSha1OrNA(jobUrl)
|
return t.toSha1OrNA(jobURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telemetry) getBuildURLHash() string {
|
func (t *Telemetry) getBuildURLHash() string {
|
||||||
buildUrl := t.provider.GetBuildUrl()
|
buildURL := t.provider.GetBuildURL()
|
||||||
return t.toSha1OrNA(buildUrl)
|
return t.toSha1OrNA(buildURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telemetry) toSha1OrNA(input string) string {
|
func (t *Telemetry) toSha1OrNA(input string) string {
|
||||||
@ -161,7 +161,7 @@ func (t *Telemetry) logStepTelemetryData() {
|
|||||||
StepDuration: t.data.CustomData.Duration,
|
StepDuration: t.data.CustomData.Duration,
|
||||||
ErrorCategory: t.data.CustomData.ErrorCategory,
|
ErrorCategory: t.data.CustomData.ErrorCategory,
|
||||||
ErrorDetail: fatalError,
|
ErrorDetail: fatalError,
|
||||||
CorrelationID: t.provider.GetBuildUrl(),
|
CorrelationID: t.provider.GetBuildURL(),
|
||||||
PiperCommitHash: t.data.CustomData.PiperCommitHash,
|
PiperCommitHash: t.data.CustomData.PiperCommitHash,
|
||||||
}
|
}
|
||||||
stepTelemetryJSON, err := json.Marshal(stepTelemetryData)
|
stepTelemetryJSON, err := json.Marshal(stepTelemetryData)
|
||||||
|
Loading…
Reference in New Issue
Block a user