Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PoC] Add support for Jenkins in Image Builder client #12489

Merged
merged 18 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ linters:
disable-all: true
enable:
- unused
- exportloopref
- copyloopvar
- gosimple
- govet
- ineffassign
Expand Down
1 change: 0 additions & 1 deletion cmd/external-plugins/automated-approver/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ func (ac *ApproveCondition) checkChangedFiles(logger *zap.SugaredLogger, changes
defer logger.Sync()
logger.Debugf("Checking if PR changed only allowed files: %v", ac.ChangedFiles)
for _, change := range changes {
change := change
logger.Debugf("Checking file: %s", change.Filename)
matched := slices.ContainsFunc(ac.ChangedFiles, func(allowedFile string) bool {
filesMatcher := regexp.MustCompile(allowedFile)
Expand Down
82 changes: 81 additions & 1 deletion cmd/image-builder/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"regexp"
"slices"
"strconv"

Expand All @@ -21,6 +22,7 @@ const (
Prow CISystem = "Prow"
GithubActions CISystem = "GithubActions"
AzureDevOps CISystem = "AzureDevOps"
Jenkins CISystem = "Jenkins"
)

type Config struct {
Expand Down Expand Up @@ -147,14 +149,17 @@ func (gitState GitStateConfig) IsPullRequest() bool {
return gitState.isPullRequest
}

func LoadGitStateConfig(ciSystem CISystem) (GitStateConfig, error) {
// TODO (dekiel): Add logger parameter to all functions reading a git state.
func LoadGitStateConfig(logger Logger, ciSystem CISystem) (GitStateConfig, error) {
switch ciSystem {
// Load from env specific for Azure DevOps and Prow Jobs
case AzureDevOps, Prow:
return loadADOGitState()
// Load from env specific for Github Actions
case GithubActions:
return loadGithubActionsGitState()
case Jenkins:
return loadJenkinsGitState(logger)
default:
// Unknown CI System, return error and empty git state
return GitStateConfig{}, fmt.Errorf("unknown ci system, got %s", ciSystem)
Expand Down Expand Up @@ -326,6 +331,75 @@ func loadGithubActionsGitState() (GitStateConfig, error) {
}
}

// loadJenkinsGitState loads git state from environment variables specific for Jenkins.
func loadJenkinsGitState(logger Logger) (GitStateConfig, error) {
// Load from env specific for Jenkins Jobs
prID, isPullRequest := os.LookupEnv("CHANGE_ID")
gitURL, present := os.LookupEnv("GIT_URL")
if !present {
return GitStateConfig{}, fmt.Errorf("GIT_URL environment variable is not set, please set it to valid git URL")
}

owner, repo, err := extractOwnerAndRepoFromGitURL(logger, gitURL)
if err != nil {
return GitStateConfig{}, fmt.Errorf("failed to extract owner and repository from git URL %s: %w", gitURL, err)
}

baseCommitSHA, present := os.LookupEnv("GIT_COMMIT")
if !present {
return GitStateConfig{}, fmt.Errorf("GIT_COMMIT environment variable is not set, please set it to valid commit SHA")
}

gitState := GitStateConfig{
RepositoryName: repo,
RepositoryOwner: owner,
JobType: "postsubmit",
BaseCommitSHA: baseCommitSHA,
}

if isPullRequest {
pullNumber, err := strconv.Atoi(prID)
if err != nil {
return GitStateConfig{}, fmt.Errorf("failed to convert prID string variable to integer: %w", err)
}

baseRef, present := os.LookupEnv("CHANGE_BRANCH")
if !present {
return GitStateConfig{}, fmt.Errorf("CHANGE_BRANCH environment variable is not set, please set it to valid base branch name")
}
pullRequestHeadSHA, present := os.LookupEnv("CHANGE_HEAD_SHA")
if !present {
return GitStateConfig{}, fmt.Errorf("CHANGE_HEAD_SHA environment variable is not set, please set it to valid commit SHA")
}
gitState.JobType = "presubmit"
gitState.PullRequestNumber = pullNumber
gitState.BaseCommitRef = baseRef
gitState.PullHeadCommitSHA = pullRequestHeadSHA
gitState.isPullRequest = true
}

return gitState, nil
}

func extractOwnerAndRepoFromGitURL(logger Logger, gitURL string) (string, string, error) {
re := regexp.MustCompile(`.*/(?P<owner>.*)/(?P<repo>.*).git`)
matches := re.FindStringSubmatch(gitURL)

logger.Debugw("Extracted matches from git URL", "matches", matches, "gitURL", gitURL)

if len(matches) != 3 {
return "", "", fmt.Errorf("failed to extract owner and repository from git URL")
}

owner := matches[re.SubexpIndex("owner")]
repo := matches[re.SubexpIndex("repo")]

logger.Debugw("Extracted owner from git URL", "owner", owner)
logger.Debugw("Extracted repository from git URL", "repo", repo)

return owner, repo, nil
}

// DetermineUsedCISystem return CISystem bind to system in which image builder is running or error if unknown
// It is used to avoid getting env variables in multiple parts of image builder
func DetermineUsedCISystem() (CISystem, error) {
Expand Down Expand Up @@ -358,5 +432,11 @@ func determineUsedCISystem(envGetter func(key string) string, envLookup func(key
return AzureDevOps, nil
}

// JENKINS_HOME environment variable is set in Jenkins
_, isJenkins := envLookup("JENKINS_HOME")
if isJenkins {
return Jenkins, nil
}

return "", fmt.Errorf("cannot determine ci system: unknown system")
}
61 changes: 59 additions & 2 deletions cmd/image-builder/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/kyma-project/test-infra/pkg/tags"
"go.uber.org/zap"
)

func Test_ParseConfig(t *testing.T) {
Expand Down Expand Up @@ -379,6 +380,48 @@ func TestLoadGitStateConfig(t *testing.T) {
PullHeadCommitSHA: "e47034172c36d3e5fb407b5ba57adf0f7868599d",
},
},
{
name: "load data from push event for jenkins",
options: options{
ciSystem: Jenkins,
},
env: map[string]string{
"CHANGE_BRANCH": "refs/heads/main",
"JENKINS_HOME": "/some/absolute/path",
"GIT_URL": "github.com/kyma-project/test-infra.git",
"GIT_COMMIT": "1234",
},
gitState: GitStateConfig{
RepositoryName: "test-infra",
RepositoryOwner: "kyma-project",
JobType: "postsubmit",
BaseCommitSHA: "1234",
},
},
{
name: "load data from pull request event for jenkins",
options: options{
ciSystem: Jenkins,
},
env: map[string]string{
"CHANGE_BRANCH": "refs/heads/main",
"JENKINS_HOME": "/some/absolute/path",
"CHANGE_ID": "14",
"GIT_URL": "github.com/kyma-project/test-infra.git",
"GIT_COMMIT": "1234",
"CHANGE_HEAD_SHA": "4321", // Must be explicitly set when calling docker run
},
gitState: GitStateConfig{
RepositoryName: "test-infra",
RepositoryOwner: "kyma-project",
JobType: "presubmit",
BaseCommitSHA: "1234",
BaseCommitRef: "refs/heads/main",
PullRequestNumber: 14,
PullHeadCommitSHA: "4321",
isPullRequest: true,
},
},
}

for _, c := range tc {
Expand All @@ -388,8 +431,15 @@ func TestLoadGitStateConfig(t *testing.T) {
t.Setenv(key, value)
}

// Setup logger
zapLogger, err := zap.NewDevelopment()
if err != nil {
t.Errorf("Failed to initialize logger: %s", err)
}
logger := zapLogger.Sugar()

// Load git state
state, err := LoadGitStateConfig(c.options.ciSystem)
state, err := LoadGitStateConfig(logger, c.options.ciSystem)
if err != nil && !c.expectError {
t.Errorf("unexpected error occured %s", err)
}
Expand Down Expand Up @@ -436,6 +486,13 @@ func Test_determineCISystem(t *testing.T) {
},
ciSystem: GithubActions,
},
{
name: "detect running in jenkins",
env: mockEnv{
"JENKINS_HOME": "/some/absolute/path",
},
ciSystem: Jenkins,
},
{
name: "unknown ci system",
env: mockEnv{
Expand Down Expand Up @@ -463,4 +520,4 @@ func Test_determineCISystem(t *testing.T) {
}
})
}
}
}
15 changes: 9 additions & 6 deletions cmd/image-builder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -869,14 +869,16 @@ func main() {

// If running inside some CI system, determine which system is used
if o.isCI {
ciSystem, err := DetermineUsedCISystem()
o.ciSystem, err = DetermineUsedCISystem()
if err != nil {
log.Fatalf("Failed to determine current ci system: %s", err)
o.logger.Errorw("Failed to determine current ci system", "error", err)
os.Exit(1)
}
o.ciSystem = ciSystem
o.gitState, err = LoadGitStateConfig(ciSystem)

o.gitState, err = LoadGitStateConfig(o.logger, o.ciSystem)
if err != nil {
log.Fatalf("Failed to load current git state: %s", err)
o.logger.Errorw("Failed to load current git state", "error", err)
os.Exit(1)
}

o.logger.Debugw("Git state loaded", "gitState", o.gitState)
Expand Down Expand Up @@ -922,12 +924,13 @@ func main() {
if o.buildInADO {
err = buildInADO(o)
if err != nil {
fmt.Printf("Image build failed with error: %s\n", err)
o.logger.Errorw("Image build failed", "error", err, "JobType", o.gitState.JobType)
os.Exit(1)
}
os.Exit(0)
}

o.logger.Warnw("Local build is deprecated and will be removed soon, the tool will not support local building anymore. Please migrate to the ADO build backend.")
err = buildLocally(o)
if err != nil {
fmt.Println(err)
Expand Down
Loading