github.com/robryk/drone@v0.2.1-0.20140602202253-40fe4305815d/cmd/droned/drone.go (about) 1 package main 2 3 import ( 4 "flag" 5 "log" 6 "net/http" 7 "runtime" 8 "strings" 9 "time" 10 11 "code.google.com/p/go.net/websocket" 12 "github.com/GeertJohan/go.rice" 13 "github.com/bmizerany/pat" 14 15 "github.com/drone/drone/pkg/build/docker" 16 "github.com/drone/drone/pkg/channel" 17 "github.com/drone/drone/pkg/database" 18 "github.com/drone/drone/pkg/handler" 19 "github.com/drone/drone/pkg/queue" 20 ) 21 22 var ( 23 // port the server will run on 24 port string 25 26 // database driver used to connect to the database 27 driver string 28 29 // driver specific connection information. In this 30 // case, it should be the location of the SQLite file 31 datasource string 32 33 // optional flags for tls listener 34 sslcert string 35 sslkey string 36 37 // build will timeout after N milliseconds. 38 // this will default to 500 minutes (6 hours) 39 timeout time.Duration 40 41 // commit sha for the current build. 42 version string 43 44 // Number of concurrent build workers to run 45 // default to number of CPUs on machine 46 workers int 47 ) 48 49 func main() { 50 // parse command line flags 51 flag.StringVar(&port, "port", ":8080", "") 52 flag.StringVar(&driver, "driver", "sqlite3", "") 53 flag.StringVar(&datasource, "datasource", "drone.sqlite", "") 54 flag.StringVar(&sslcert, "sslcert", "", "") 55 flag.StringVar(&sslkey, "sslkey", "", "") 56 flag.DurationVar(&timeout, "timeout", 300*time.Minute, "") 57 flag.IntVar(&workers, "workers", runtime.NumCPU(), "") 58 flag.Parse() 59 60 // validate the TLS arguments 61 checkTLSFlags() 62 63 // setup database and handlers 64 if err := database.Init(driver, datasource); err != nil { 65 log.Fatal("Can't initialize database: ", err) 66 } 67 discardOldBuilds() 68 setupStatic() 69 setupHandlers() 70 71 // debug 72 log.Printf("starting drone version %s on port %s\n", version, port) 73 74 // start webserver using HTTPS or HTTP 75 if sslcert != "" && sslkey != "" { 76 panic(http.ListenAndServeTLS(port, sslcert, sslkey, nil)) 77 } else { 78 panic(http.ListenAndServe(port, nil)) 79 } 80 } 81 82 // checking if the TLS flags where supplied correctly. 83 func checkTLSFlags() { 84 85 if sslcert != "" && sslkey == "" { 86 log.Fatal("invalid configuration: -sslkey unspecified, but -sslcert was specified.") 87 } else if sslcert == "" && sslkey != "" { 88 log.Fatal("invalid configuration: -sslcert unspecified, but -sslkey was specified.") 89 } 90 91 } 92 93 // discardOldBuilds sets builds that are in the 'Started' 94 // state to 'Failure' on startup. The assumption is that 95 // the drone process was shut down mid-build and thus the 96 // builds will never complete. 97 func discardOldBuilds() { 98 err := database.FailUnfinishedBuilds() 99 if err != nil { 100 log.Fatal(err) 101 } 102 103 err = database.FailUnfinishedCommits() 104 if err != nil { 105 log.Fatal(err) 106 } 107 } 108 109 // setup routes for static assets. These assets may 110 // be directly embedded inside the application using 111 // the `rice embed` command, else they are served from disk. 112 func setupStatic() { 113 box := rice.MustFindBox("assets") 114 http.Handle("/css/", http.FileServer(box.HTTPBox())) 115 http.Handle("/js/", http.FileServer(box.HTTPBox())) 116 117 // we need to intercept all attempts to serve images 118 // so that we can add a cache-control settings 119 var images = http.FileServer(box.HTTPBox()) 120 http.HandleFunc("/img/", func(w http.ResponseWriter, r *http.Request) { 121 if strings.HasPrefix(r.URL.Path, "/img/build_") { 122 w.Header().Add("Cache-Control", "no-cache") 123 } 124 125 // serve images 126 images.ServeHTTP(w, r) 127 }) 128 } 129 130 // setup routes for serving dynamic content. 131 func setupHandlers() { 132 queueRunner := queue.NewBuildRunner(docker.New(), timeout) 133 queue := queue.Start(workers, queueRunner) 134 135 var ( 136 github = handler.NewGithubHandler(queue) 137 gitlab = handler.NewGitlabHandler(queue) 138 bitbucket = handler.NewBitbucketHandler(queue) 139 rebuild = handler.NewCommitRebuildHandler(queue) 140 ) 141 142 m := pat.New() 143 m.Get("/login", handler.ErrorHandler(handler.Login)) 144 m.Post("/login", handler.ErrorHandler(handler.Authorize)) 145 m.Get("/logout", handler.ErrorHandler(handler.Logout)) 146 m.Get("/forgot", handler.ErrorHandler(handler.Forgot)) 147 m.Post("/forgot", handler.ErrorHandler(handler.ForgotPost)) 148 m.Get("/reset", handler.ErrorHandler(handler.Reset)) 149 m.Post("/reset", handler.ErrorHandler(handler.ResetPost)) 150 m.Get("/signup", handler.ErrorHandler(handler.SignUp)) 151 m.Post("/signup", handler.ErrorHandler(handler.SignUpPost)) 152 m.Get("/register", handler.ErrorHandler(handler.Register)) 153 m.Post("/register", handler.ErrorHandler(handler.RegisterPost)) 154 m.Get("/accept", handler.UserHandler(handler.TeamMemberAccept)) 155 156 // handlers for setting up your GitHub repository 157 m.Post("/new/github.com", handler.UserHandler(handler.RepoCreateGithub)) 158 m.Get("/new/github.com", handler.UserHandler(handler.RepoAddGithub)) 159 160 // handlers for linking your GitHub account 161 m.Get("/auth/login/github", handler.UserHandler(handler.LinkGithub)) 162 163 // handlers for setting up your Bitbucket repository 164 m.Post("/new/bitbucket.org", handler.UserHandler(handler.RepoCreateBitbucket)) 165 m.Get("/new/bitbucket.org", handler.UserHandler(handler.RepoAddBitbucket)) 166 167 // handlers for linking your Bitbucket account 168 m.Get("/auth/login/bitbucket", handler.UserHandler(handler.LinkBitbucket)) 169 170 // handlers for setting up your GitLab repository 171 m.Post("/new/gitlab", handler.UserHandler(gitlab.Create)) 172 m.Get("/new/gitlab", handler.UserHandler(gitlab.Add)) 173 174 // handler for linking GitLab account 175 m.Post("/link/gitlab", handler.UserHandler(gitlab.Link)) 176 m.Get("/link/gitlab", handler.UserHandler(gitlab.ReLink)) 177 178 // handlers for dashboard pages 179 m.Get("/dashboard/team/:team", handler.UserHandler(handler.TeamShow)) 180 m.Get("/dashboard", handler.UserHandler(handler.UserShow)) 181 182 // handlers for user account management 183 m.Get("/account/user/profile", handler.UserHandler(handler.UserEdit)) 184 m.Post("/account/user/profile", handler.UserHandler(handler.UserUpdate)) 185 m.Get("/account/user/delete", handler.UserHandler(handler.UserDeleteConfirm)) 186 m.Post("/account/user/delete", handler.UserHandler(handler.UserDelete)) 187 m.Get("/account/user/password", handler.UserHandler(handler.UserPass)) 188 m.Post("/account/user/password", handler.UserHandler(handler.UserPassUpdate)) 189 m.Get("/account/user/teams/add", handler.UserHandler(handler.TeamAdd)) 190 m.Post("/account/user/teams/add", handler.UserHandler(handler.TeamCreate)) 191 m.Get("/account/user/teams", handler.UserHandler(handler.UserTeams)) 192 193 // handlers for team managements 194 m.Get("/account/team/:team/profile", handler.UserHandler(handler.TeamEdit)) 195 m.Post("/account/team/:team/profile", handler.UserHandler(handler.TeamUpdate)) 196 m.Get("/account/team/:team/delete", handler.UserHandler(handler.TeamDeleteConfirm)) 197 m.Post("/account/team/:team/delete", handler.UserHandler(handler.TeamDelete)) 198 m.Get("/account/team/:team/members/add", handler.UserHandler(handler.TeamMemberAdd)) 199 m.Post("/account/team/:team/members/add", handler.UserHandler(handler.TeamMemberInvite)) 200 m.Get("/account/team/:team/members/edit", handler.UserHandler(handler.TeamMemberEdit)) 201 m.Post("/account/team/:team/members/edit", handler.UserHandler(handler.TeamMemberUpdate)) 202 m.Post("/account/team/:team/members/delete", handler.UserHandler(handler.TeamMemberDelete)) 203 m.Get("/account/team/:team/members", handler.UserHandler(handler.TeamMembers)) 204 205 // handlers for system administration 206 m.Get("/account/admin/settings", handler.AdminHandler(handler.AdminSettings)) 207 m.Post("/account/admin/settings", handler.AdminHandler(handler.AdminSettingsUpdate)) 208 m.Get("/account/admin/users/edit", handler.AdminHandler(handler.AdminUserEdit)) 209 m.Post("/account/admin/users/edit", handler.AdminHandler(handler.AdminUserUpdate)) 210 m.Post("/account/admin/users/delete", handler.AdminHandler(handler.AdminUserDelete)) 211 m.Get("/account/admin/users/add", handler.AdminHandler(handler.AdminUserAdd)) 212 m.Post("/account/admin/users", handler.AdminHandler(handler.AdminUserInvite)) 213 m.Get("/account/admin/users", handler.AdminHandler(handler.AdminUserList)) 214 215 // handlers for GitHub post-commit hooks 216 m.Post("/hook/github.com", handler.ErrorHandler(github.Hook)) 217 218 // handlers for Bitbucket post-commit hooks 219 m.Post("/hook/bitbucket.org", handler.ErrorHandler(bitbucket.Hook)) 220 221 // handlers for GitLab post-commit hooks 222 m.Post("/hook/gitlab", handler.ErrorHandler(gitlab.Hook)) 223 224 // handlers for first-time installation 225 m.Get("/install", handler.ErrorHandler(handler.Install)) 226 m.Post("/install", handler.ErrorHandler(handler.InstallPost)) 227 228 // handlers for repository, commits and build details 229 m.Get("/:host/:owner/:name/commit/:commit/build/:label/out.txt", handler.RepoHandler(handler.BuildOut)) 230 m.Post("/:host/:owner/:name/commit/:commit/build/:label/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild)) 231 m.Get("/:host/:owner/:name/commit/:commit/build/:label", handler.RepoHandler(handler.CommitShow)) 232 m.Post("/:host/:owner/:name/commit/:commit/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild)) 233 m.Get("/:host/:owner/:name/commit/:commit", handler.RepoHandler(handler.CommitShow)) 234 m.Get("/:host/:owner/:name/tree", handler.RepoHandler(handler.RepoDashboard)) 235 m.Get("/:host/:owner/:name/status.svg", handler.ErrorHandler(handler.Badge)) 236 m.Get("/:host/:owner/:name/settings", handler.RepoAdminHandler(handler.RepoSettingsForm)) 237 m.Get("/:host/:owner/:name/params", handler.RepoAdminHandler(handler.RepoParamsForm)) 238 m.Get("/:host/:owner/:name/badges", handler.RepoAdminHandler(handler.RepoBadges)) 239 m.Get("/:host/:owner/:name/keys", handler.RepoAdminHandler(handler.RepoKeys)) 240 m.Get("/:host/:owner/:name/delete", handler.RepoAdminHandler(handler.RepoDeleteForm)) 241 m.Post("/:host/:owner/:name/delete", handler.RepoAdminHandler(handler.RepoDelete)) 242 m.Get("/:host/:owner/:name", handler.RepoHandler(handler.RepoDashboard)) 243 m.Post("/:host/:owner/:name", handler.RepoHandler(handler.RepoUpdate)) 244 http.Handle("/feed", websocket.Handler(channel.Read)) 245 246 // no routes are served at the root URL. Instead we will 247 // redirect the user to his/her dashboard page. 248 m.Get("/", http.RedirectHandler("/dashboard", http.StatusSeeOther)) 249 250 // the first time a page is requested we should record 251 // the scheme and hostname. 252 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 253 // our multiplexer is a bit finnicky and therefore requires 254 // us to strip any trailing slashes in order to correctly 255 // find and match a route. 256 if r.URL.Path != "/" && strings.HasSuffix(r.URL.Path, "/") { 257 http.Redirect(w, r, r.URL.Path[:len(r.URL.Path)-1], http.StatusSeeOther) 258 return 259 } 260 261 // standard header variables that should be set, for good measure. 262 w.Header().Add("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate") 263 w.Header().Add("X-Frame-Options", "DENY") 264 w.Header().Add("X-Content-Type-Options", "nosniff") 265 w.Header().Add("X-XSS-Protection", "1; mode=block") 266 267 // ok, now we're ready to serve the request. 268 m.ServeHTTP(w, r) 269 }) 270 }