forked from runatlantis/atlantis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
e2e.go
204 lines (174 loc) · 6.4 KB
/
e2e.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright 2017 HootSuite Media Inc.
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Modified hereafter by contributors to runatlantis/atlantis.
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"time"
"github.com/google/go-github/github"
)
type E2ETester struct {
githubClient *GithubClient
repoURL string
ownerName string
repoName string
hookID int
cloneDirRoot string
projectType Project
}
type E2EResult struct {
projectType string
githubPullRequestURL string
testResult string
}
var testFileData = `
resource "null_resource" "hello" {
}
`
// nolint: gosec
func (t *E2ETester) Start() (*E2EResult, error) {
cloneDir := fmt.Sprintf("%s/%s-test", t.cloneDirRoot, t.projectType.Name)
branchName := fmt.Sprintf("%s-%s", t.projectType.Name, time.Now().Format("20060102150405"))
testFileName := fmt.Sprintf("%s.tf", t.projectType.Name)
e2eResult := &E2EResult{}
e2eResult.projectType = t.projectType.Name
// create the directory and parents if necessary
log.Printf("creating dir %q", cloneDir)
if err := os.MkdirAll(cloneDir, 0700); err != nil {
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))
}
// checkout a new branch for the project
log.Printf("checking out branch %q", branchName)
checkoutCmd := exec.Command("git", "checkout", "-b", branchName)
checkoutCmd.Dir = cloneDir
if err := checkoutCmd.Run(); err != nil {
return e2eResult, fmt.Errorf("failed to git checkout branch %q: %v", branchName, err)
}
// write a file for running the tests
randomData := []byte(testFileData)
filePath := fmt.Sprintf("%s/%s/%s", cloneDir, t.projectType.Name, testFileName)
log.Printf("creating file to commit %q", filePath)
err := ioutil.WriteFile(filePath, randomData, 0644)
if err != nil {
return e2eResult, fmt.Errorf("couldn't write file %s: %v", filePath, err)
}
// add the file
log.Printf("git add file %q", filePath)
addCmd := exec.Command("git", "add", filePath)
addCmd.Dir = cloneDir
if err = addCmd.Run(); err != nil {
return e2eResult, fmt.Errorf("failed to git add file %q: %v", filePath, err)
}
// commit the file
log.Printf("git commit file %q", filePath)
commitCmd := exec.Command("git", "commit", "-am", "test commit")
commitCmd.Dir = cloneDir
var output []byte
if output, err = commitCmd.CombinedOutput(); err != nil {
return e2eResult, fmt.Errorf("failed to run git commit in %q: %v: %v", cloneDir, err, string(output))
}
// push the branch to remote
log.Printf("git push branch %q", branchName)
pushCmd := exec.Command("git", "push", "origin", branchName)
pushCmd.Dir = cloneDir
if err = pushCmd.Run(); err != nil {
return e2eResult, fmt.Errorf("failed to git push branch %q: %v", branchName, err)
}
// 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 := "master"
newPullRequest := &github.NewPullRequest{Title: &title, Head: &head, Body: &body, Base: &base}
pull, _, err := t.githubClient.client.PullRequests.Create(t.githubClient.ctx, t.ownerName, t.repoName, newPullRequest)
if err != nil {
return e2eResult, fmt.Errorf("error while creating new pull request: %v", err)
}
// set pull request url
e2eResult.githubPullRequestURL = pull.GetHTMLURL()
log.Printf("created pull request %s", pull.GetHTMLURL())
// defer closing pull request and delete remote branch
defer cleanUp(t, pull.GetNumber(), branchName) // nolint: errcheck
// wait for atlantis to respond to webhook and autoplan.
time.Sleep(2 * time.Second)
state := "not started"
// waiting for atlantis run and finish
maxLoops := 20
i := 0
for ; i < maxLoops && checkStatus(state); i++ {
time.Sleep(2 * time.Second)
state, _ = getAtlantisStatus(t, branchName)
if state == "" {
log.Println("atlantis run hasn't started")
continue
}
log.Printf("atlantis run is in %s state", state)
}
if i == maxLoops {
state = "timed out"
}
log.Printf("atlantis run finished with status %q", state)
e2eResult.testResult = state
// check if atlantis run was a success
if state != "success" {
return e2eResult, fmt.Errorf("atlantis run project type %q failed with %q status", t.projectType.Name, state)
}
return e2eResult, nil
}
// nolint: unparam
func getAtlantisStatus(t *E2ETester, branchName string) (string, error) {
// check repo status
combinedStatus, _, err := t.githubClient.client.Repositories.GetCombinedStatus(t.githubClient.ctx, t.ownerName, t.repoName, branchName, nil)
if err != nil {
return "", err
}
for _, status := range combinedStatus.Statuses {
if status.GetContext() == "Atlantis" {
return status.GetState(), nil
}
}
return "", nil
}
func checkStatus(state string) bool {
for _, s := range []string{"success", "error", "failure"} {
if state == s {
return false
}
}
return true
}
// nolint: unparam
func cleanUp(t *E2ETester, pullRequestNumber int, branchName string) error {
// clean up
pullClosed, _, err := t.githubClient.client.PullRequests.Edit(t.githubClient.ctx, t.ownerName, t.repoName, pullRequestNumber, &github.PullRequest{State: github.String("closed")})
if err != nil {
return fmt.Errorf("error while closing new pull request: %v", err)
}
log.Printf("closed pull request %d", pullClosed.GetNumber())
deleteBranchName := fmt.Sprintf("%s/%s", "heads", branchName)
_, err = t.githubClient.client.Git.DeleteRef(t.githubClient.ctx, t.ownerName, t.repoName, deleteBranchName)
if err != nil {
return fmt.Errorf("error while deleting branch %s: %v", deleteBranchName, err)
}
log.Printf("deleted branch %s", deleteBranchName)
return nil
}