// Copyright 2019 Drone IO, 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. package main import ( "context" "flag" "fmt" "github.com/drone/drone/cmd/drone-server/bootstrap" "github.com/drone/drone/cmd/drone-server/config" "github.com/drone/drone/core" "github.com/drone/drone/metric/sink" "github.com/drone/drone/operator/runner" "github.com/drone/drone/service/canceler/reaper" "github.com/drone/drone/server" "github.com/drone/drone/trigger/cron" "github.com/drone/signal" "github.com/joho/godotenv" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" ) func main() { var envfile string flag.StringVar(&envfile, "env-file", ".env", "Read in a file of environment variables") flag.Parse() godotenv.Load(envfile) config, err := config.Environ() if err != nil { logger := logrus.WithError(err) logger.Fatalln("main: invalid configuration") } initLogging(config) ctx := signal.WithContext( context.Background(), ) // if trace level logging is enabled, output the // configuration parameters. if logrus.IsLevelEnabled(logrus.TraceLevel) { fmt.Println(config.String()) } app, err := InitializeApplication(config) if err != nil { logger := logrus.WithError(err) logger.Fatalln("main: cannot initialize server") } // optionally bootstrap the system with administrative or // machine users configured in the environment. err = bootstrap.New(app.users).Bootstrap(ctx, &core.User{ Login: config.Users.Create.Username, Machine: config.Users.Create.Machine, Admin: config.Users.Create.Admin, Hash: config.Users.Create.Token, }) if err != nil { logger := logrus.WithError(err) logger.Fatalln("cannot bootstrap user account") } g := errgroup.Group{} g.Go(func() error { logrus.WithFields( logrus.Fields{ "proto": config.Server.Proto, "host": config.Server.Host, "port": config.Server.Port, "url": config.Server.Addr, "acme": config.Server.Acme, }, ).Infoln("starting the http server") return app.server.ListenAndServe(ctx) }) // launches the datadog sink in a goroutine. If the sink // is disabled, the goroutine exits immediately without error. g.Go(func() (err error) { if !config.Datadog.Enabled { return nil } return app.sink.Start(ctx) }) // launches the cron runner in a goroutine. If the cron // runner is disabled, the goroutine exits immediately // without error. g.Go(func() (err error) { if config.Cron.Disabled { return nil } logrus.WithField("interval", config.Cron.Interval.String()). Infoln("starting the cron scheduler") return app.cron.Start(ctx, config.Cron.Interval) }) // launches the reaper in a goroutine. If the reaper // is disabled, the goroutine exits immediately // without error. g.Go(func() (err error) { if config.Cleanup.Disabled { return nil } logrus.WithField("interval", config.Cleanup.Interval.String()). Infoln("starting the zombie build reaper") return app.reaper.Start(ctx, config.Cleanup.Interval) }) // launches the build runner in a goroutine. If the local // runner is disabled (because nomad or kubernetes is enabled) // then the goroutine exits immediately without error. g.Go(func() (err error) { if app.runner == nil { return nil } logrus.WithField("threads", config.Runner.Capacity). Infoln("main: starting the local build runner") return app.runner.Start(ctx, config.Runner.Capacity) }) if err := g.Wait(); err != nil { logrus.WithError(err).Fatalln("program terminated") } } // helper function configures the logging. func initLogging(c config.Config) { if c.Logging.Debug { logrus.SetLevel(logrus.DebugLevel) } if c.Logging.Trace { logrus.SetLevel(logrus.TraceLevel) } if c.Logging.Text { logrus.SetFormatter(&logrus.TextFormatter{ ForceColors: c.Logging.Color, DisableColors: !c.Logging.Color, }) } else { logrus.SetFormatter(&logrus.JSONFormatter{ PrettyPrint: c.Logging.Pretty, }) } } // application is the main struct for the Drone server. type application struct { cron *cron.Scheduler reaper *reaper.Reaper sink *sink.Datadog runner *runner.Runner server *server.Server users core.UserStore } // newApplication creates a new application struct. func newApplication( cron *cron.Scheduler, reaper *reaper.Reaper, sink *sink.Datadog, runner *runner.Runner, server *server.Server, users core.UserStore) application { return application{ users: users, cron: cron, sink: sink, server: server, runner: runner, reaper: reaper, } }