Skip to content

Commit

Permalink
chore: make e2e code generic to vcs (#4732)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukemassa authored Jul 7, 2024
1 parent 2341891 commit 4416716
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 113 deletions.
63 changes: 18 additions & 45 deletions e2e/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,19 @@ import (
"os"
"os/exec"
"time"

"github.com/google/go-github/v59/github"
)

type E2ETester struct {
githubClient *GithubClient
repoURL string
ownerName string
repoName string
vcsClient VCSClient
hookID int64
cloneDirRoot string
projectType Project
}

type E2EResult struct {
projectType string
githubPullRequestURL string
testResult string
projectType string
pullRequestURL string
testResult string
}

var testFileData = `
Expand All @@ -59,11 +54,9 @@ func (t *E2ETester) Start(ctx context.Context) (*E2EResult, error) {
return e2eResult, fmt.Errorf("failed to create dir %q prior to cloning, attempting to continue: %v", cloneDir, err)
}

cloneCmd := exec.Command("git", "clone", t.repoURL, cloneDir)
// git clone the repo
log.Printf("git cloning into %q", cloneDir)
if output, err := cloneCmd.CombinedOutput(); err != nil {
return e2eResult, fmt.Errorf("failed to clone repository: %v: %s", err, string(output))
err := t.vcsClient.Clone(cloneDir)
if err != nil {
return e2eResult, err
}

// checkout a new branch for the project
Expand All @@ -78,7 +71,7 @@ func (t *E2ETester) Start(ctx context.Context) (*E2EResult, error) {
randomData := []byte(testFileData)
filePath := fmt.Sprintf("%s/%s/%s", cloneDir, t.projectType.Name, testFileName)
log.Printf("creating file to commit %q", filePath)
err := os.WriteFile(filePath, randomData, 0644)
err = os.WriteFile(filePath, randomData, 0644)
if err != nil {
return e2eResult, fmt.Errorf("couldn't write file %s: %v", filePath, err)
}
Expand Down Expand Up @@ -109,23 +102,19 @@ func (t *E2ETester) Start(ctx context.Context) (*E2EResult, error) {

// create a new pr
title := fmt.Sprintf("This is a test pull request for atlantis e2e test for %s project type", t.projectType.Name)
head := fmt.Sprintf("%s:%s", t.ownerName, branchName)
body := ""
base := "main"
newPullRequest := &github.NewPullRequest{Title: &title, Head: &head, Body: &body, Base: &base}
url, pullId, err := t.vcsClient.CreatePullRequest(ctx, title, branchName)

pull, _, err := t.githubClient.client.PullRequests.Create(ctx, t.ownerName, t.repoName, newPullRequest)
if err != nil {
return e2eResult, fmt.Errorf("error while creating new pull request: %v", err)
return e2eResult, err
}

// set pull request url
e2eResult.githubPullRequestURL = pull.GetHTMLURL()
e2eResult.pullRequestURL = url

log.Printf("created pull request %s", pull.GetHTMLURL())
log.Printf("created pull request %s", url)

// defer closing pull request and delete remote branch
defer cleanUp(ctx, t, pull.GetNumber(), branchName) // nolint: errcheck
defer cleanUp(ctx, t, pullId, branchName) // nolint: errcheck

// wait for atlantis to respond to webhook and autoplan.
time.Sleep(2 * time.Second)
Expand All @@ -136,7 +125,7 @@ func (t *E2ETester) Start(ctx context.Context) (*E2EResult, error) {
i := 0
for ; i < maxLoops && checkStatus(state); i++ {
time.Sleep(2 * time.Second)
state, _ = getAtlantisStatus(ctx, t, branchName)
state, _ = t.vcsClient.GetAtlantisStatus(ctx, branchName)
if state == "" {
log.Println("atlantis run hasn't started")
continue
Expand All @@ -157,22 +146,6 @@ func (t *E2ETester) Start(ctx context.Context) (*E2EResult, error) {
return e2eResult, nil
}

func getAtlantisStatus(ctx context.Context, t *E2ETester, branchName string) (string, error) {
// check repo status
combinedStatus, _, err := t.githubClient.client.Repositories.GetCombinedStatus(ctx, t.ownerName, t.repoName, branchName, nil)
if err != nil {
return "", err
}

for _, status := range combinedStatus.Statuses {
if status.GetContext() == "atlantis/plan" {
return status.GetState(), nil
}
}

return "", nil
}

func checkStatus(state string) bool {
for _, s := range []string{"success", "error", "failure"} {
if state == s {
Expand All @@ -184,14 +157,14 @@ func checkStatus(state string) bool {

func cleanUp(ctx context.Context, t *E2ETester, pullRequestNumber int, branchName string) error {
// clean up
pullClosed, _, err := t.githubClient.client.PullRequests.Edit(ctx, t.ownerName, t.repoName, pullRequestNumber, &github.PullRequest{State: github.String("closed")})
err := t.vcsClient.ClosePullRequest(ctx, pullRequestNumber)
if err != nil {
return fmt.Errorf("error while closing new pull request: %v", err)
return err
}
log.Printf("closed pull request %d", pullClosed.GetNumber())
log.Printf("closed pull request %d", pullRequestNumber)

deleteBranchName := fmt.Sprintf("%s/%s", "heads", branchName)
_, err = t.githubClient.client.Git.DeleteRef(ctx, t.ownerName, t.repoName, deleteBranchName)
err = t.vcsClient.DeleteBranch(ctx, deleteBranchName)
if err != nil {
return fmt.Errorf("error while deleting branch %s: %v", deleteBranchName, err)
}
Expand Down
142 changes: 140 additions & 2 deletions e2e/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,148 @@
package main

import (
"context"
"fmt"
"log"
"os"
"os/exec"
"strings"

"github.com/google/go-github/v59/github"
)

type GithubClient struct {
client *github.Client
username string
client *github.Client
username string
ownerName string
repoName string
token string
}

func NewGithubClient() *GithubClient {

githubUsername := os.Getenv("ATLANTISBOT_GITHUB_USERNAME")
if githubUsername == "" {
log.Fatalf("ATLANTISBOT_GITHUB_USERNAME cannot be empty")
}
githubToken := os.Getenv("ATLANTISBOT_GITHUB_TOKEN")
if githubToken == "" {
log.Fatalf("ATLANTISBOT_GITHUB_TOKEN cannot be empty")
}
ownerName := os.Getenv("GITHUB_REPO_OWNER_NAME")
if ownerName == "" {
ownerName = "runatlantis"
}
repoName := os.Getenv("GITHUB_REPO_NAME")
if repoName == "" {
repoName = "atlantis-tests"
}

// create github client
tp := github.BasicAuthTransport{
Username: strings.TrimSpace(githubUsername),
Password: strings.TrimSpace(githubToken),
}
ghClient := github.NewClient(tp.Client())

return &GithubClient{
client: ghClient,
username: githubUsername,
ownerName: ownerName,
repoName: repoName,
token: githubToken,
}

}

func (g GithubClient) Clone(cloneDir string) error {

repoURL := fmt.Sprintf("https://%s:%s@github.com/%s/%s.git", g.username, g.token, g.ownerName, g.repoName)
cloneCmd := exec.Command("git", "clone", repoURL, cloneDir)
// git clone the repo
log.Printf("git cloning into %q", cloneDir)
if output, err := cloneCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to clone repository: %v: %s", err, string(output))
}
return nil
}

func (g GithubClient) CreateAtlantisWebhook(ctx context.Context, hookURL string) (int64, error) {
// create atlantis hook
atlantisHook := &github.Hook{
Events: []string{"issue_comment", "pull_request", "push"},
Config: map[string]interface{}{
"url": hookURL,
"content_type": "json",
},
Active: github.Bool(true),
}

hook, _, err := g.client.Repositories.CreateHook(ctx, g.ownerName, g.repoName, atlantisHook)
if err != nil {
return 0, err
}
log.Println(hook.GetURL())

return hook.GetID(), nil
}

func (g GithubClient) DeleteAtlantisHook(ctx context.Context, hookID int64) error {
_, err := g.client.Repositories.DeleteHook(ctx, g.ownerName, g.repoName, hookID)
if err != nil {
return err
}
log.Printf("deleted webhook id %d", hookID)

return nil
}

func (g GithubClient) CreatePullRequest(ctx context.Context, title, branchName string) (string, int, error) {
head := fmt.Sprintf("%s:%s", g.ownerName, branchName)
body := ""
base := "main"
newPullRequest := &github.NewPullRequest{Title: &title, Head: &head, Body: &body, Base: &base}

pull, _, err := g.client.PullRequests.Create(ctx, g.ownerName, g.repoName, newPullRequest)
if err != nil {
return "", 0, fmt.Errorf("error while creating new pull request: %v", err)
}

// set pull request url
return pull.GetHTMLURL(), pull.GetNumber(), nil

}

func (g GithubClient) GetAtlantisStatus(ctx context.Context, branchName string) (string, error) {
// check repo status
combinedStatus, _, err := g.client.Repositories.GetCombinedStatus(ctx, g.ownerName, g.repoName, branchName, nil)
if err != nil {
return "", err
}

for _, status := range combinedStatus.Statuses {
if status.GetContext() == "atlantis/plan" {
return status.GetState(), nil
}
}

return "", nil
}

func (g GithubClient) ClosePullRequest(ctx context.Context, pullRequestNumber int) error {
// clean up
_, _, err := g.client.PullRequests.Edit(ctx, g.ownerName, g.repoName, pullRequestNumber, &github.PullRequest{State: github.String("closed")})
if err != nil {
return fmt.Errorf("error while closing new pull request: %v", err)
}
return nil

}
func (g GithubClient) DeleteBranch(ctx context.Context, branchName string) error {

_, err := g.client.Git.DeleteRef(ctx, g.ownerName, g.repoName, branchName)
if err != nil {
return fmt.Errorf("error while deleting branch %s: %v", branchName, err)
}
return nil
}
Loading

0 comments on commit 4416716

Please sign in to comment.