This repository has been archived on 2025-11-20. You can view files and clone it, but cannot push or open issues or pull requests.
Files
drone-runner-podman/command/daemon/daemon.go
2023-10-07 00:12:59 -05:00

281 lines
6.7 KiB
Go

// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Polyform License
// that can be found in the LICENSE file.
package daemon
import (
"context"
"time"
"github.com/drone-runners/drone-runner-podman/engine"
"github.com/drone-runners/drone-runner-podman/engine/compiler"
"github.com/drone-runners/drone-runner-podman/engine/linter"
"github.com/drone-runners/drone-runner-podman/engine/resource"
"github.com/drone-runners/drone-runner-podman/internal/match"
"github.com/drone/runner-go/client"
"github.com/drone/runner-go/environ/provider"
"github.com/drone/runner-go/handler/router"
"github.com/drone/runner-go/logger"
loghistory "github.com/drone/runner-go/logger/history"
"github.com/drone/runner-go/pipeline/reporter/history"
"github.com/drone/runner-go/pipeline/reporter/remote"
"github.com/drone/runner-go/pipeline/runtime"
"github.com/drone/runner-go/pipeline/uploader"
"github.com/drone/runner-go/poller"
"github.com/drone/runner-go/registry"
"github.com/drone/runner-go/secret"
"github.com/drone/runner-go/server"
"github.com/drone/signal"
"github.com/joho/godotenv"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
"gopkg.in/alecthomas/kingpin.v2"
)
// empty context.
var nocontext = context.Background()
type daemonCommand struct {
envfile string
}
func (c *daemonCommand) run(*kingpin.ParseContext) error {
// load environment variables from file.
godotenv.Load(c.envfile)
// load the configuration from the environment
config, err := fromEnviron()
if err != nil {
return err
}
// setup the global logrus logger.
setupLogger(config)
ctx, cancel := context.WithCancel(nocontext)
defer cancel()
// listen for termination signals to gracefully shutdown
// the runner daemon.
ctx = signal.WithContextFunc(ctx, func() {
println("received signal, terminating process")
cancel()
})
cli := client.New(
config.Client.Address,
config.Client.Secret,
config.Client.SkipVerify,
)
if config.Client.Dump {
cli.Dumper = logger.StandardDumper(
config.Client.DumpBody,
)
}
cli.Logger = logger.Logrus(
logrus.NewEntry(
logrus.StandardLogger(),
),
)
opts := engine.Opts{
HidePull: !config.Podman.Stream,
}
engine, err := engine.NewEnv(ctx, opts)
if err != nil {
logrus.WithError(err).
Fatalln("cannot load the podman engine")
}
remote := remote.New(cli)
upload := uploader.New(cli)
tracer := history.New(remote)
hook := loghistory.New()
logrus.AddHook(hook)
runner := &runtime.Runner{
Client: cli,
Machine: config.Runner.Name,
Environ: config.Runner.Environ,
Reporter: tracer,
Lookup: resource.Lookup,
Lint: linter.New().Lint,
Match: match.Func(
config.Limit.Repos,
config.Limit.Events,
config.Limit.Trusted,
),
Compiler: &compiler.Compiler{
Clone: config.Runner.Clone,
Privileged: append(config.Runner.Privileged, compiler.Privileged...),
Networks: config.Runner.Networks,
NetworkOpts: config.Runner.NetworkOpts,
NetrcCloneOnly: config.Netrc.CloneOnly,
Volumes: config.Runner.Volumes,
Resources: compiler.Resources{
Memory: config.Resources.Memory,
MemorySwap: config.Resources.MemorySwap,
CPUQuota: config.Resources.CPUQuota,
CPUPeriod: config.Resources.CPUPeriod,
CPUShares: config.Resources.CPUShares,
CPUSet: config.Resources.CPUSet,
ShmSize: config.Resources.ShmSize,
},
Tmate: compiler.Tmate{
Image: config.Tmate.Image,
Enabled: config.Tmate.Enabled,
Server: config.Tmate.Server,
Port: config.Tmate.Port,
RSA: config.Tmate.RSA,
ED25519: config.Tmate.ED25519,
AuthorizedKeys: config.Tmate.AuthorizedKeys,
},
Environ: provider.Combine(
provider.Static(config.Runner.Environ),
provider.External(
config.Environ.Endpoint,
config.Environ.Token,
config.Environ.SkipVerify,
),
),
Registry: registry.Combine(
registry.File(
config.Podman.Config,
),
registry.External(
config.Registry.Endpoint,
config.Registry.Token,
config.Registry.SkipVerify,
),
),
Secret: secret.Combine(
secret.StaticVars(
config.Runner.Secrets,
),
secret.External(
config.Secret.Endpoint,
config.Secret.Token,
config.Secret.SkipVerify,
),
),
},
Exec: runtime.NewExecer(
tracer,
remote,
upload,
engine,
config.Runner.Procs,
).Exec,
}
poller := &poller.Poller{
Client: cli,
Dispatch: runner.Run,
Filter: &client.Filter{
Kind: resource.Kind,
Type: resource.Type,
OS: config.Platform.OS,
Arch: config.Platform.Arch,
Variant: config.Platform.Variant,
Kernel: config.Platform.Kernel,
Labels: config.Runner.Labels,
},
}
var g errgroup.Group
server := server.Server{
Addr: config.Server.Port,
Handler: router.New(tracer, hook, router.Config{
Username: config.Dashboard.Username,
Password: config.Dashboard.Password,
Realm: config.Dashboard.Realm,
}),
}
logrus.WithField("addr", config.Server.Port).
Infoln("starting the server")
g.Go(func() error {
return server.ListenAndServe(ctx)
})
// Ping the server and block until a successful connection
// to the server has been established.
for {
err := cli.Ping(ctx, config.Runner.Name)
select {
case <-ctx.Done():
return nil
default:
}
if ctx.Err() != nil {
break
}
if err != nil {
logrus.WithError(err).
Errorln("cannot ping the remote server")
time.Sleep(time.Second)
} else {
logrus.Infoln("successfully pinged the remote server")
break
}
}
g.Go(func() error {
logrus.WithField("capacity", config.Runner.Capacity).
WithField("endpoint", config.Client.Address).
WithField("kind", resource.Kind).
WithField("type", resource.Type).
WithField("os", config.Platform.OS).
WithField("arch", config.Platform.Arch).
Infoln("polling the remote server")
poller.Poll(ctx, config.Runner.Capacity)
return nil
})
err = g.Wait()
if err != nil {
logrus.WithError(err).
Errorln("shutting down the server")
}
return err
}
// helper function configures the global logger from
// the loaded configuration.
func setupLogger(config Config) {
logger.Default = logger.Logrus(
logrus.NewEntry(
logrus.StandardLogger(),
),
)
if config.Debug {
logrus.SetLevel(logrus.DebugLevel)
}
if config.Trace {
logrus.SetLevel(logrus.TraceLevel)
}
}
// Register the daemon command.
func Register(app *kingpin.Application) {
registerDaemon(app)
registerProcess(app)
}
func registerDaemon(app *kingpin.Application) {
c := new(daemonCommand)
cmd := app.Command("daemon", "starts the runner daemon").
Default().
Action(c.run)
cmd.Arg("envfile", "load the environment variable file").
Default("").
StringVar(&c.envfile)
}