github.com/marinho/drone@v0.2.1-0.20140504195434-d3ba962e89a7/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 // serce 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 ) 140 141 m := pat.New() 142 m.Get("/login", handler.ErrorHandler(handler.Login)) 143 m.Post("/login", handler.ErrorHandler(handler.Authorize)) 144 m.Get("/logout", handler.ErrorHandler(handler.Logout)) 145 m.Get("/forgot", handler.ErrorHandler(handler.Forgot)) 146 m.Post("/forgot", handler.ErrorHandler(handler.ForgotPost)) 147 m.Get("/reset", handler.ErrorHandler(handler.Reset)) 148 m.Post("/reset", handler.ErrorHandler(handler.ResetPost)) 149 m.Get("/signup", handler.ErrorHandler(handler.SignUp)) 150 m.Post("/signup", handler.ErrorHandler(handler.SignUpPost)) 151 m.Get("/register", handler.ErrorHandler(handler.Register)) 152 m.Post("/register", handler.ErrorHandler(handler.RegisterPost)) 153 m.Get("/accept", handler.UserHandler(handler.TeamMemberAccept)) 154 155 // handlers for setting up your GitHub repository 156 m.Post("/new/github.com", handler.UserHandler(handler.RepoCreateGithub)) 157 m.Get("/new/github.com", handler.UserHandler(handler.RepoAddGithub)) 158 159 // handlers for linking your GitHub account 160 m.Get("/auth/login/github", handler.UserHandler(handler.LinkGithub)) 161 162 // handlers for setting up your Bitbucket repository 163 m.Post("/new/bitbucket.org", handler.UserHandler(handler.RepoCreateBitbucket)) 164 m.Get("/new/bitbucket.org", handler.UserHandler(handler.RepoAddBitbucket)) 165 166 // handlers for linking your Bitbucket account 167 m.Get("/auth/login/bitbucket", handler.UserHandler(handler.LinkBitbucket)) 168 169 // handlers for setting up your GitLab repository 170 m.Post("/new/gitlab", handler.UserHandler(gitlab.Create)) 171 m.Get("/new/gitlab", handler.UserHandler(gitlab.Add)) 172 173 // handler for linking GitLab account 174 m.Post("/link/gitlab", handler.UserHandler(gitlab.Link)) 175 m.Get("/link/gitlab", handler.UserHandler(gitlab.ReLink)) 176 177 // handlers for dashboard pages 178 m.Get("/dashboard/team/:team", handler.UserHandler(handler.TeamShow)) 179 m.Get("/dashboard", handler.UserHandler(handler.UserShow)) 180 181 // handlers for user account management 182 m.Get("/account/user/profile", handler.UserHandler(handler.UserEdit)) 183 m.Post("/account/user/profile", handler.UserHandler(handler.UserUpdate)) 184 m.Get("/account/user/delete", handler.UserHandler(handler.UserDeleteConfirm)) 185 m.Post("/account/user/delete", handler.UserHandler(handler.UserDelete)) 186 m.Get("/account/user/password", handler.UserHandler(handler.UserPass)) 187 m.Post("/account/user/password", handler.UserHandler(handler.UserPassUpdate)) 188 m.Get("/account/user/teams/add", handler.UserHandler(handler.TeamAdd)) 189 m.Post("/account/user/teams/add", handler.UserHandler(handler.TeamCreate)) 190 m.Get("/account/user/teams", handler.UserHandler(handler.UserTeams)) 191 192 // handlers for team managements 193 m.Get("/account/team/:team/profile", handler.UserHandler(handler.TeamEdit)) 194 m.Post("/account/team/:team/profile", handler.UserHandler(handler.TeamUpdate)) 195 m.Get("/account/team/:team/delete", handler.UserHandler(handler.TeamDeleteConfirm)) 196 m.Post("/account/team/:team/delete", handler.UserHandler(handler.TeamDelete)) 197 m.Get("/account/team/:team/members/add", handler.UserHandler(handler.TeamMemberAdd)) 198 m.Post("/account/team/:team/members/add", handler.UserHandler(handler.TeamMemberInvite)) 199 m.Get("/account/team/:team/members/edit", handler.UserHandler(handler.TeamMemberEdit)) 200 m.Post("/account/team/:team/members/edit", handler.UserHandler(handler.TeamMemberUpdate)) 201 m.Post("/account/team/:team/members/delete", handler.UserHandler(handler.TeamMemberDelete)) 202 m.Get("/account/team/:team/members", handler.UserHandler(handler.TeamMembers)) 203 204 // handlers for system administration 205 m.Get("/account/admin/settings", handler.AdminHandler(handler.AdminSettings)) 206 m.Post("/account/admin/settings", handler.AdminHandler(handler.AdminSettingsUpdate)) 207 m.Get("/account/admin/users/edit", handler.AdminHandler(handler.AdminUserEdit)) 208 m.Post("/account/admin/users/edit", handler.AdminHandler(handler.AdminUserUpdate)) 209 m.Post("/account/admin/users/delete", handler.AdminHandler(handler.AdminUserDelete)) 210 m.Get("/account/admin/users/add", handler.AdminHandler(handler.AdminUserAdd)) 211 m.Post("/account/admin/users", handler.AdminHandler(handler.AdminUserInvite)) 212 m.Get("/account/admin/users", handler.AdminHandler(handler.AdminUserList)) 213 214 // handlers for GitHub post-commit hooks 215 m.Post("/hook/github.com", handler.ErrorHandler(github.Hook)) 216 217 // handlers for Bitbucket post-commit hooks 218 m.Post("/hook/bitbucket.org", handler.ErrorHandler(bitbucket.Hook)) 219 220 // handlers for GitLab post-commit hooks 221 m.Post("/hook/gitlab", handler.ErrorHandler(gitlab.Hook)) 222 223 // handlers for first-time installation 224 m.Get("/install", handler.ErrorHandler(handler.Install)) 225 m.Post("/install", handler.ErrorHandler(handler.InstallPost)) 226 227 // handlers for repository, commits and build details 228 m.Get("/:host/:owner/:name/commit/:commit/build/:label/out.txt", handler.RepoHandler(handler.BuildOut)) 229 m.Get("/:host/:owner/:name/commit/:commit/build/:label", handler.RepoHandler(handler.CommitShow)) 230 m.Get("/:host/:owner/:name/commit/:commit", handler.RepoHandler(handler.CommitShow)) 231 m.Get("/:host/:owner/:name/tree", handler.RepoHandler(handler.RepoDashboard)) 232 m.Get("/:host/:owner/:name/status.svg", handler.ErrorHandler(handler.Badge)) 233 m.Get("/:host/:owner/:name/settings", handler.RepoAdminHandler(handler.RepoSettingsForm)) 234 m.Get("/:host/:owner/:name/params", handler.RepoAdminHandler(handler.RepoParamsForm)) 235 m.Get("/:host/:owner/:name/badges", handler.RepoAdminHandler(handler.RepoBadges)) 236 m.Get("/:host/:owner/:name/keys", handler.RepoAdminHandler(handler.RepoKeys)) 237 m.Get("/:host/:owner/:name/delete", handler.RepoAdminHandler(handler.RepoDeleteForm)) 238 m.Post("/:host/:owner/:name/delete", handler.RepoAdminHandler(handler.RepoDelete)) 239 m.Get("/:host/:owner/:name", handler.RepoHandler(handler.RepoDashboard)) 240 m.Post("/:host/:owner/:name", handler.RepoHandler(handler.RepoUpdate)) 241 http.Handle("/feed", websocket.Handler(channel.Read)) 242 243 // no routes are served at the root URL. Instead we will 244 // redirect the user to his/her dashboard page. 245 m.Get("/", http.RedirectHandler("/dashboard", http.StatusSeeOther)) 246 247 // the first time a page is requested we should record 248 // the scheme and hostname. 249 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 250 // our multiplexer is a bit finnicky and therefore requires 251 // us to strip any trailing slashes in order to correctly 252 // find and match a route. 253 if r.URL.Path != "/" && strings.HasSuffix(r.URL.Path, "/") { 254 http.Redirect(w, r, r.URL.Path[:len(r.URL.Path)-1], http.StatusSeeOther) 255 return 256 } 257 258 // standard header variables that should be set, for good measure. 259 w.Header().Add("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate") 260 w.Header().Add("X-Frame-Options", "DENY") 261 w.Header().Add("X-Content-Type-Options", "nosniff") 262 w.Header().Add("X-XSS-Protection", "1; mode=block") 263 264 // ok, now we're ready to serve the request. 265 m.ServeHTTP(w, r) 266 }) 267 }