github.com/kvattikuti/drone@v0.2.1-0.20140603034306-d400229a327a/pkg/queue/worker.go (about) 1 package queue 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/drone/drone/pkg/build/git" 7 r "github.com/drone/drone/pkg/build/repo" 8 "github.com/drone/drone/pkg/channel" 9 "github.com/drone/drone/pkg/database" 10 . "github.com/drone/drone/pkg/model" 11 "github.com/drone/drone/pkg/plugin/notify" 12 "github.com/drone/go-github/github" 13 "io" 14 //"log" 15 "net/url" 16 "path/filepath" 17 "time" 18 ) 19 20 type worker struct { 21 runner BuildRunner 22 } 23 24 // work is a function that will infinitely 25 // run in the background waiting for tasks that 26 // it can pull off the queue and execute. 27 func (w *worker) work(queue <-chan *BuildTask) { 28 var task *BuildTask 29 for { 30 // get work item (pointer) from the queue 31 task = <-queue 32 if task == nil { 33 continue 34 } 35 36 // execute the task 37 var err = w.execute(task) 38 if err != nil { 39 println(err.Error()) 40 } 41 } 42 } 43 44 // execute will execute the build task and persist 45 // the results to the datastore. 46 func (w *worker) execute(task *BuildTask) error { 47 48 println("build task execute called") 49 // we need to be sure that we can recover 50 // from any sort panic that could occur 51 // to avoid brining down the entire application 52 defer func() { 53 if e := recover(); e != nil { 54 task.Build.Finished = time.Now().UTC() 55 task.Commit.Finished = time.Now().UTC() 56 task.Build.Duration = task.Build.Finished.Unix() - task.Build.Started.Unix() 57 task.Commit.Duration = task.Build.Finished.Unix() - task.Build.Started.Unix() 58 task.Commit.Status = "Error" 59 task.Build.Status = "Error" 60 database.SaveBuild(task.Build) 61 database.SaveCommit(task.Commit) 62 } 63 }() 64 65 println("build task started") 66 // update commit and build status 67 task.Commit.Status = "Started" 68 task.Build.Status = "Started" 69 task.Build.Started = time.Now().UTC() 70 task.Commit.Started = time.Now().UTC() 71 72 // persist the commit to the database 73 if err := database.SaveCommit(task.Commit); err != nil { 74 return err 75 } 76 println("commit saved") 77 78 // persist the build to the database 79 if err := database.SaveBuild(task.Build); err != nil { 80 return err 81 } 82 println("build saved") 83 84 // get settings 85 settings, _ := database.GetSettings() 86 87 // notification context 88 context := ¬ify.Context{ 89 Repo: task.Repo, 90 Commit: task.Commit, 91 Host: settings.URL().String(), 92 } 93 println(context) 94 95 /*// send all "started" notifications 96 if task.Script.Notifications != nil { 97 task.Script.Notifications.Send(context) 98 } 99 100 // Send "started" notification to Github 101 if err := updateGitHubStatus(task.Repo, task.Commit); err != nil { 102 log.Printf("error updating github status: %s\n", err.Error()) 103 } 104 println("github status updated")*/ 105 106 // make sure a channel exists for the repository, 107 // the commit, and the commit output (TODO) 108 reposlug := fmt.Sprintf("%s/%s/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name) 109 commitslug := fmt.Sprintf("%s/%s/%s/commit/%s/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name, task.Commit.Branch, task.Commit.Hash) 110 consoleslug := fmt.Sprintf("%s/%s/%s/commit/%s/%s/builds/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name, task.Commit.Branch, task.Commit.Hash, task.Build.Slug) 111 println(reposlug) 112 println(commitslug) 113 println(consoleslug) 114 channel.Create(reposlug) 115 channel.Create(commitslug) 116 channel.CreateStream(consoleslug) 117 118 // notify the channels that the commit and build started 119 channel.SendJSON(reposlug, task.Commit) 120 channel.SendJSON(commitslug, task.Build) 121 122 var buf = &bufferWrapper{channel: consoleslug} 123 124 // append private parameters to the environment 125 // variable section of the .drone.yml file, iff 126 // this is not a pull request (for security purposes) 127 if task.Repo.Params != nil && len(task.Commit.PullRequest) == 0 { 128 for k, v := range task.Repo.Params { 129 task.Script.Env = append(task.Script.Env, k+"="+v) 130 } 131 } 132 133 /*defer func() { 134 // update the status of the commit using the 135 // GitHub status API. 136 if err := updateGitHubStatus(task.Repo, task.Commit); err != nil { 137 log.Printf("error updating github status: %s\n", err.Error()) 138 } 139 }()*/ 140 141 println("running build") 142 // execute the build 143 passed, buildErr := w.runBuild(task, buf) 144 println("build completed"); 145 146 task.Build.Finished = time.Now().UTC() 147 task.Commit.Finished = time.Now().UTC() 148 task.Build.Duration = task.Build.Finished.UnixNano() - task.Build.Started.UnixNano() 149 task.Commit.Duration = task.Build.Finished.UnixNano() - task.Build.Started.UnixNano() 150 task.Commit.Status = "Success" 151 task.Build.Status = "Success" 152 task.Build.Stdout = buf.buf.String() 153 154 // if exit code != 0 set to failure 155 if passed { 156 task.Commit.Status = "Failure" 157 task.Build.Status = "Failure" 158 if buildErr != nil && task.Build.Stdout == "" { 159 // TODO: If you wanted to have very friendly error messages, you could do that here 160 task.Build.Stdout = buildErr.Error() + "\n" 161 } 162 } 163 164 println("build status is ", task.Commit.Status) 165 println("build err is ", buildErr) 166 if passed { 167 println(buildErr.Error()) 168 } 169 // persist the build to the database 170 if err := database.SaveBuild(task.Build); err != nil { 171 return err 172 } 173 println("build saved") 174 175 // persist the commit to the database 176 if err := database.SaveCommit(task.Commit); err != nil { 177 return err 178 } 179 println("commit saved") 180 181 // notify the channels that the commit and build finished 182 channel.SendJSON(reposlug, task.Commit) 183 channel.SendJSON(commitslug, task.Build) 184 channel.Close(consoleslug) 185 186 // send all "finished" notifications 187 if task.Script.Notifications != nil { 188 task.Script.Notifications.Send(context) 189 } 190 println("notifications sent") 191 192 return nil 193 } 194 195 func (w *worker) runBuild(task *BuildTask, buf io.Writer) (bool, error) { 196 repo := &r.Repo{ 197 Name: task.Repo.Slug, 198 Path: task.Repo.URL, 199 Branch: task.Commit.Branch, 200 Commit: task.Commit.Hash, 201 PR: task.Commit.PullRequest, 202 Dir: filepath.Join("/var/cache/drone/src", task.Repo.Slug), 203 Depth: git.GitDepth(task.Script.Git), 204 } 205 206 return w.runner.Run( 207 task.Script, 208 repo, 209 []byte(task.Repo.PrivateKey), 210 task.Repo.Privileged, 211 buf, 212 ) 213 } 214 215 // updateGitHubStatus is a helper function that will send 216 // the build status to GitHub using the Status API. 217 // see https://github.com/blog/1227-commit-status-api 218 func updateGitHubStatus(repo *Repo, commit *Commit) error { 219 220 // convert from drone status to github status 221 var message, status string 222 switch commit.Status { 223 case "Success": 224 status = "success" 225 message = "The build succeeded on drone.io" 226 case "Failure": 227 status = "failure" 228 message = "The build failed on drone.io" 229 case "Started": 230 status = "pending" 231 message = "The build is pending on drone.io" 232 default: 233 status = "error" 234 message = "The build errored on drone.io" 235 } 236 237 // get the system settings 238 settings, _ := database.GetSettings() 239 240 // get the user from the database 241 // since we need his / her GitHub token 242 user, err := database.GetUser(repo.UserID) 243 if err != nil { 244 return err 245 } 246 247 client := github.New(user.GithubToken) 248 client.ApiUrl = settings.GitHubApiUrl 249 buildUrl := getBuildUrl(settings.URL().String(), repo, commit) 250 251 return client.Repos.CreateStatus(repo.Owner, repo.Name, status, buildUrl, message, commit.Hash) 252 } 253 254 func getBuildUrl(host string, repo *Repo, commit *Commit) string { 255 branchQuery := url.Values{} 256 branchQuery.Set("branch", commit.Branch) 257 buildUrl := fmt.Sprintf("%s/%s/commit/%s?%s", host, repo.Slug, commit.Hash, branchQuery.Encode()) 258 return buildUrl 259 } 260 261 type bufferWrapper struct { 262 buf bytes.Buffer 263 264 // name of the channel 265 channel string 266 } 267 268 func (b *bufferWrapper) Write(p []byte) (n int, err error) { 269 n, err = b.buf.Write(p) 270 channel.SendBytes(b.channel, p) 271 return 272 }