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  }