From c524709f4a66b8c7a02f1a6c2365c2035663e6e7 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 29 Aug 2018 14:00:25 -0700 Subject: [PATCH] buildkitd: add config file support Signed-off-by: Tonis Tiigi --- cmd/buildkitd/config/config.go | 78 +++++++++ cmd/buildkitd/config/config_test.go | 61 +++++++ cmd/buildkitd/main.go | 202 ++++++++++++++++++------ cmd/buildkitd/main_containerd_worker.go | 79 +++++++-- cmd/buildkitd/main_oci_worker.go | 81 ++++++++-- util/appdefaults/appdefaults_unix.go | 18 ++- util/appdefaults/appdefaults_windows.go | 9 +- 7 files changed, 447 insertions(+), 81 deletions(-) create mode 100644 cmd/buildkitd/config/config.go create mode 100644 cmd/buildkitd/config/config_test.go diff --git a/cmd/buildkitd/config/config.go b/cmd/buildkitd/config/config.go new file mode 100644 index 00000000..80ca22f0 --- /dev/null +++ b/cmd/buildkitd/config/config.go @@ -0,0 +1,78 @@ +package config + +import ( + "io" + "os" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" +) + +// Config provides containerd configuration data for the server +type Config struct { + Debug bool `toml:"debug"` + + // Root is the path to a directory where buildkit will store persistent data + Root string `toml:"root"` + + // GRPC configuration settings + GRPC GRPCConfig `toml:"grpc"` + + Workers struct { + OCI OCIConfig `toml:"oci"` + Containerd ContainerdConfig `toml:"containerd"` + } `toml:"worker"` +} + +type GRPCConfig struct { + Address []string `toml:"address"` + DebugAddress string `toml:"debugAddress"` + UID int `toml:"uid"` + GID int `toml:"gid"` + + TLS TLSConfig `toml:"tls"` + // MaxRecvMsgSize int `toml:"max_recv_message_size"` + // MaxSendMsgSize int `toml:"max_send_message_size"` +} + +type TLSConfig struct { + Cert string `toml:"cert"` + Key string `toml:"key"` + CA string `toml:"ca"` +} + +type OCIConfig struct { + Enabled *bool `toml:"enabled"` + Labels map[string]string `toml:"labels"` + Platforms []string `toml:"platforms"` + Snapshotter string `toml:"snapshotter"` + Rootless bool `toml:"rootless"` +} + +type ContainerdConfig struct { + Address string `toml:"address"` + Enabled *bool `toml:"enabled"` + Labels map[string]string `toml:"labels"` + Platforms []string `toml:"platforms"` +} + +func Load(r io.Reader) (Config, *toml.MetaData, error) { + var c Config + md, err := toml.DecodeReader(r, &c) + if err != nil { + return c, nil, errors.Wrap(err, "failed to parse config") + } + return c, &md, nil +} + +func LoadFile(fp string) (Config, *toml.MetaData, error) { + f, err := os.Open(fp) + if err != nil { + if os.IsNotExist(err) { + return Config{}, nil, nil + } + return Config{}, nil, errors.Wrapf(err, "failed to load config from %s", fp) + } + defer f.Close() + return Load(f) +} diff --git a/cmd/buildkitd/config/config_test.go b/cmd/buildkitd/config/config_test.go new file mode 100644 index 00000000..49fddb1d --- /dev/null +++ b/cmd/buildkitd/config/config_test.go @@ -0,0 +1,61 @@ +package config + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestConfig(t *testing.T) { + + const testConfig = ` +root = "/foo/bar" +debug=true + +[grpc] +address=["buildkit.sock"] +debugAddress="debug.sock" +gid=1234 +[grpc.tls] +cert="mycert.pem" + +[worker.oci] +enabled=true +snapshotter="overlay" +rootless=true +[worker.oci.labels] +foo="bar" +"aa.bb.cc"="baz" + +[worker.containerd] +platforms=["linux/amd64"] +address="containerd.sock" +` + + cfg, md, err := Load(bytes.NewBuffer([]byte(testConfig))) + require.NoError(t, err) + + require.Equal(t, "/foo/bar", cfg.Root) + require.Equal(t, true, cfg.Debug) + + require.Equal(t, "buildkit.sock", cfg.GRPC.Address[0]) + require.Equal(t, "debug.sock", cfg.GRPC.DebugAddress) + require.Equal(t, 1234, cfg.GRPC.GID) + require.Equal(t, "mycert.pem", cfg.GRPC.TLS.Cert) + + require.True(t, md.IsDefined("grpc", "gid")) + require.False(t, md.IsDefined("grpc", "uid")) + + require.NotNil(t, cfg.Workers.OCI.Enabled) + require.Equal(t, true, *cfg.Workers.OCI.Enabled) + require.Equal(t, "overlay", cfg.Workers.OCI.Snapshotter) + require.Equal(t, true, cfg.Workers.OCI.Rootless) + + require.Equal(t, "bar", cfg.Workers.OCI.Labels["foo"]) + require.Equal(t, "baz", cfg.Workers.OCI.Labels["aa.bb.cc"]) + + require.Nil(t, cfg.Workers.Containerd.Enabled) + require.Equal(t, 1, len(cfg.Workers.Containerd.Platforms)) + require.Equal(t, "containerd.sock", cfg.Workers.Containerd.Address) +} diff --git a/cmd/buildkitd/main.go b/cmd/buildkitd/main.go index fe518d07..f3d2391e 100644 --- a/cmd/buildkitd/main.go +++ b/cmd/buildkitd/main.go @@ -14,12 +14,14 @@ import ( "strconv" "strings" + "github.com/BurntSushi/toml" "github.com/containerd/containerd/pkg/seed" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/sys" "github.com/docker/go-connections/sockets" "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" registryremotecache "github.com/moby/buildkit/cache/remotecache/registry" + "github.com/moby/buildkit/cmd/buildkitd/config" "github.com/moby/buildkit/control" "github.com/moby/buildkit/frontend" dockerfile "github.com/moby/buildkit/frontend/dockerfile/builder" @@ -50,7 +52,7 @@ func init() { type workerInitializerOpt struct { sessionManager *session.Manager - root string + config *config.Config } type workerInitializer struct { @@ -80,29 +82,35 @@ func main() { app := cli.NewApp() app.Name = "buildkitd" app.Usage = "build daemon" - defaultRoot := appdefaults.Root - defaultAddress := appdefaults.Address + + defaultConf, md := defaultConf() + rootlessUsage := "set all the default options to be compatible with rootless containers" if system.RunningInUserNS() { app.Flags = append(app.Flags, cli.BoolTFlag{ Name: "rootless", Usage: rootlessUsage + " (default: true)", }) - // if buildkitd is being executed as the mapped-root (not only EUID==0 but also $USER==root) - // in a user namespace, we need to enable the rootless mode but - // we don't want to honor $HOME for setting up default paths. - if u := os.Getenv("USER"); u != "" && u != "root" { - defaultRoot = appdefaults.UserRoot() - defaultAddress = appdefaults.UserAddress() - appdefaults.EnsureUserAddressDir() - } } else { app.Flags = append(app.Flags, cli.BoolFlag{ Name: "rootless", Usage: rootlessUsage, }) } + + groupValue := func(gid int) string { + if md == nil || !md.IsDefined("grpc", "gid") { + return "" + } + return strconv.Itoa(gid) + } + app.Flags = append(app.Flags, + cli.StringFlag{ + Name: "config", + Usage: "path to config file", + Value: defaultConfigPath(), + }, cli.BoolFlag{ Name: "debug", Usage: "enable debug output in logs", @@ -110,34 +118,37 @@ func main() { cli.StringFlag{ Name: "root", Usage: "path to state directory", - Value: defaultRoot, + Value: defaultConf.Root, }, cli.StringSliceFlag{ Name: "addr", Usage: "listening address (socket or tcp)", - Value: &cli.StringSlice{defaultAddress}, + Value: &cli.StringSlice{defaultConf.GRPC.Address[0]}, }, cli.StringFlag{ Name: "group", Usage: "group (name or gid) which will own all Unix socket listening addresses", - Value: "", + Value: groupValue(defaultConf.GRPC.GID), }, cli.StringFlag{ Name: "debugaddr", Usage: "debugging address (eg. 0.0.0.0:6060)", - Value: "", + Value: defaultConf.GRPC.DebugAddress, }, cli.StringFlag{ Name: "tlscert", Usage: "certificate file to use", + Value: defaultConf.GRPC.TLS.Cert, }, cli.StringFlag{ Name: "tlskey", Usage: "key file to use", + Value: defaultConf.GRPC.TLS.Key, }, cli.StringFlag{ Name: "tlscacert", Usage: "ca certificate to verify clients", + Value: defaultConf.GRPC.TLS.CA, }, ) app.Flags = append(app.Flags, appFlags...) @@ -149,13 +160,27 @@ func main() { ctx, cancel := context.WithCancel(appcontext.Context()) defer cancel() - if debugAddr := c.GlobalString("debugaddr"); debugAddr != "" { - if err := setupDebugHandlers(debugAddr); err != nil { + cfg, md, err := config.LoadFile(c.GlobalString("config")) + if err != nil { + return err + } + + setDefaultConfig(&cfg) + if err := applyMainFlags(c, &cfg, md); err != nil { + return err + } + + if cfg.Debug { + logrus.SetLevel(logrus.DebugLevel) + } + + if cfg.GRPC.DebugAddress != "" { + if err := setupDebugHandlers(cfg.GRPC.DebugAddress); err != nil { return err } } opts := []grpc.ServerOption{unaryInterceptor(ctx), grpc.StreamInterceptor(otgrpc.OpenTracingStreamServerInterceptor(tracer))} - creds, err := serverCredentials(c) + creds, err := serverCredentials(cfg.GRPC.TLS) if err != nil { return err } @@ -165,16 +190,17 @@ func main() { server := grpc.NewServer(opts...) // relative path does not work with nightlyone/lockfile - root, err := filepath.Abs(c.GlobalString("root")) + root, err := filepath.Abs(cfg.Root) if err != nil { return err } + cfg.Root = root if err := os.MkdirAll(root, 0700); err != nil { return errors.Wrapf(err, "failed to create %s", root) } - controller, err := newController(c, root) + controller, err := newController(c, &cfg) if err != nil { return err } @@ -182,11 +208,7 @@ func main() { controller.Register(server) errCh := make(chan error, 1) - addrs := c.GlobalStringSlice("addr") - if len(addrs) > 1 { - addrs = addrs[1:] // https://github.com/urfave/cli/issues/160 - } - if err := serveGRPC(c, server, addrs, errCh); err != nil { + if err := serveGRPC(cfg.GRPC, server, errCh); err != nil { return err } @@ -203,12 +225,6 @@ func main() { return err } - app.Before = func(context *cli.Context) error { - if context.GlobalBool("debug") { - logrus.SetLevel(logrus.DebugLevel) - } - return nil - } app.After = func(context *cli.Context) error { if closeTracer != nil { @@ -225,14 +241,15 @@ func main() { } } -func serveGRPC(c *cli.Context, server *grpc.Server, addrs []string, errCh chan error) error { +func serveGRPC(cfg config.GRPCConfig, server *grpc.Server, errCh chan error) error { + addrs := cfg.Address if len(addrs) == 0 { return errors.New("--addr cannot be empty") } eg, _ := errgroup.WithContext(context.Background()) listeners := make([]net.Listener, 0, len(addrs)) for _, addr := range addrs { - l, err := getListener(c, addr) + l, err := getListener(cfg, addr) if err != nil { for _, l := range listeners { l.Close() @@ -256,6 +273,102 @@ func serveGRPC(c *cli.Context, server *grpc.Server, addrs []string, errCh chan e return nil } +func defaultConfigPath() string { + if system.RunningInUserNS() { + return filepath.Join(appdefaults.UserConfigDir(), "buildkitd.toml") + } + return filepath.Join(appdefaults.ConfigDir, "buildkitd.toml") +} + +func defaultConf() (config.Config, *toml.MetaData) { + cfg, md, err := config.LoadFile(defaultConfigPath()) + if err != nil { + return cfg, nil + } + setDefaultConfig(&cfg) + + return cfg, md +} + +func setDefaultConfig(cfg *config.Config) { + orig := *cfg + + if cfg.Root == "" { + cfg.Root = appdefaults.Root + } + + if len(cfg.GRPC.Address) == 0 { + cfg.GRPC.Address = []string{appdefaults.Address} + } + + if system.RunningInUserNS() { + // if buildkitd is being executed as the mapped-root (not only EUID==0 but also $USER==root) + // in a user namespace, we need to enable the rootless mode but + // we don't want to honor $HOME for setting up default paths. + if u := os.Getenv("USER"); u != "" && u != "root" { + if orig.Root == "" { + cfg.Root = appdefaults.UserRoot() + } + if len(orig.GRPC.Address) == 0 { + cfg.GRPC.Address = []string{appdefaults.UserAddress()} + } + appdefaults.EnsureUserAddressDir() + } + } +} + +func applyMainFlags(c *cli.Context, cfg *config.Config, md *toml.MetaData) error { + if c.IsSet("debug") { + cfg.Debug = c.Bool("debug") + } + if c.IsSet("root") { + cfg.Root = c.String("root") + } + + if c.IsSet("addr") || len(cfg.GRPC.Address) == 0 { + addrs := c.StringSlice("addr") + if len(addrs) > 1 { + addrs = addrs[1:] // https://github.com/urfave/cli/issues/160 + } + + cfg.GRPC.Address = make([]string, 0, len(addrs)) + for _, v := range addrs { + cfg.GRPC.Address = append(cfg.GRPC.Address, v) + } + } + + if c.IsSet("debugaddr") { + cfg.GRPC.DebugAddress = c.String("debugaddr") + } + + if md == nil || !md.IsDefined("grpc", "uid") { + cfg.GRPC.UID = os.Getuid() + } + + if md == nil || !md.IsDefined("grpc", "gid") { + cfg.GRPC.GID = os.Getgid() + } + + if group := c.String("group"); group != "" { + gid, err := groupToGid(group) + if err != nil { + return err + } + cfg.GRPC.GID = gid + } + + if tlscert := c.String("tlscert"); tlscert != "" { + cfg.GRPC.TLS.Cert = tlscert + } + if tlskey := c.String("tlskey"); tlskey != "" { + cfg.GRPC.TLS.Key = tlskey + } + if tlsca := c.String("tlsca"); tlsca != "" { + cfg.GRPC.TLS.CA = tlsca + } + return nil +} + // Convert a string containing either a group name or a stringified gid into a numeric id) func groupToGid(group string) (int, error) { if group == "" { @@ -289,7 +402,7 @@ func groupToGid(group string) (int, error) { return id, nil } -func getListener(c *cli.Context, addr string) (net.Listener, error) { +func getListener(cfg config.GRPCConfig, addr string) (net.Listener, error) { addrSlice := strings.SplitN(addr, "://", 2) if len(addrSlice) < 2 { return nil, errors.Errorf("address %s does not contain proto, you meant unix://%s ?", @@ -299,12 +412,7 @@ func getListener(c *cli.Context, addr string) (net.Listener, error) { listenAddr := addrSlice[1] switch proto { case "unix", "npipe": - uid := os.Getuid() - gid, err := groupToGid(c.GlobalString("group")) - if err != nil { - return nil, err - } - return sys.GetLocalListener(listenAddr, uid, gid) + return sys.GetLocalListener(listenAddr, cfg.UID, cfg.GID) case "tcp": return sockets.NewTCPSocket(listenAddr, nil) default: @@ -335,10 +443,10 @@ func unaryInterceptor(globalCtx context.Context) grpc.ServerOption { }) } -func serverCredentials(c *cli.Context) (grpc.ServerOption, error) { - certFile := c.GlobalString("tlscert") - keyFile := c.GlobalString("tlskey") - caFile := c.GlobalString("tlscacert") +func serverCredentials(cfg config.TLSConfig) (grpc.ServerOption, error) { + certFile := cfg.Cert + keyFile := cfg.Key + caFile := cfg.CA if certFile == "" && keyFile == "" { return nil, nil } @@ -373,14 +481,14 @@ func serverCredentials(c *cli.Context) (grpc.ServerOption, error) { return creds, nil } -func newController(c *cli.Context, root string) (*control.Controller, error) { +func newController(c *cli.Context, cfg *config.Config) (*control.Controller, error) { sessionManager, err := session.NewManager() if err != nil { return nil, err } wc, err := newWorkerController(c, workerInitializerOpt{ sessionManager: sessionManager, - root: root, + config: cfg, }) if err != nil { return nil, err @@ -389,7 +497,7 @@ func newController(c *cli.Context, root string) (*control.Controller, error) { frontends["dockerfile.v0"] = forwarder.NewGatewayForwarder(wc, dockerfile.Build) frontends["gateway.v0"] = gateway.NewGatewayFrontend(wc) - cacheStorage, err := boltdbcachestorage.NewStore(filepath.Join(root, "cache.db")) + cacheStorage, err := boltdbcachestorage.NewStore(filepath.Join(cfg.Root, "cache.db")) if err != nil { return nil, err } diff --git a/cmd/buildkitd/main_containerd_worker.go b/cmd/buildkitd/main_containerd_worker.go index 25bf2ad9..c7b6a9ea 100644 --- a/cmd/buildkitd/main_containerd_worker.go +++ b/cmd/buildkitd/main_containerd_worker.go @@ -4,10 +4,12 @@ package main import ( "os" + "strconv" "strings" "time" ctd "github.com/containerd/containerd" + "github.com/moby/buildkit/cmd/buildkitd/config" "github.com/moby/buildkit/worker" "github.com/moby/buildkit/worker/base" "github.com/moby/buildkit/worker/containerd" @@ -16,7 +18,22 @@ import ( "github.com/urfave/cli" ) +const defaultContainerdAddress = "/run/containerd/containerd.sock" + func init() { + defaultConf, _ := defaultConf() + + enabledValue := func(b *bool) string { + if b == nil { + return "auto" + } + return strconv.FormatBool(*b) + } + + if defaultConf.Workers.Containerd.Address == "" { + defaultConf.Workers.Containerd.Address = defaultContainerdAddress + } + registerWorkerInitializer( workerInitializer{ fn: containerdWorkerInitializer, @@ -26,12 +43,12 @@ func init() { cli.StringFlag{ Name: "containerd-worker", Usage: "enable containerd workers (true/false/auto)", - Value: "auto", + Value: enabledValue(defaultConf.Workers.Containerd.Enabled), }, cli.StringFlag{ Name: "containerd-worker-addr", Usage: "containerd socket", - Value: "/run/containerd/containerd.sock", + Value: defaultConf.Workers.Containerd.Address, }, cli.StringSliceFlag{ Name: "containerd-worker-labels", @@ -48,32 +65,64 @@ func init() { // TODO(AkihiroSuda): allow using multiple snapshotters. should be useful for some applications that does not work with the default overlay snapshotter. e.g. mysql (docker/for-linux#72)", } -func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker.Worker, error) { - socket := c.GlobalString("containerd-worker-addr") - boolOrAuto, err := parseBoolOrAuto(c.GlobalString("containerd-worker")) - if err != nil { - return nil, err +func applyContainerdFlags(c *cli.Context, cfg *config.Config) error { + if cfg.Workers.Containerd.Address == "" { + cfg.Workers.Containerd.Address = defaultContainerdAddress } - if (boolOrAuto == nil && !validContainerdSocket(socket)) || (boolOrAuto != nil && !*boolOrAuto) { - return nil, nil - } - labels, err := attrMap(c.GlobalStringSlice("containerd-worker-labels")) - if err != nil { - return nil, err + + if c.GlobalIsSet("containerd-worker") { + boolOrAuto, err := parseBoolOrAuto(c.GlobalString("containerd-worker")) + if err != nil { + return err + } + cfg.Workers.Containerd.Enabled = boolOrAuto } + // GlobalBool works for BoolT as well rootless := c.GlobalBool("rootless") if rootless { logrus.Warn("rootless mode is not supported for containerd workers. disabling containerd worker.") + b := false + cfg.Workers.Containerd.Enabled = &b + return nil + } + + labels, err := attrMap(c.GlobalStringSlice("containerd-worker-labels")) + if err != nil { + return err + } + for k, v := range labels { + cfg.Workers.Containerd.Labels[k] = v + } + if c.GlobalIsSet("containerd-worker-addr") { + cfg.Workers.Containerd.Address = c.GlobalString("containerd-worker-addr") + } + + if platforms := c.GlobalStringSlice("containerd-worker-platform"); len(platforms) != 0 { + cfg.Workers.Containerd.Platforms = platforms + } + + return nil +} + +func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker.Worker, error) { + if err := applyContainerdFlags(c, common.config); err != nil { + return nil, err + } + + cfg := common.config.Workers.Containerd + + if (cfg.Enabled == nil && !validContainerdSocket(cfg.Address)) || (cfg.Enabled != nil && !*cfg.Enabled) { return nil, nil } - opt, err := containerd.NewWorkerOpt(common.root, socket, ctd.DefaultSnapshotter, labels, ctd.WithTimeout(60*time.Second)) + + opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, ctd.DefaultSnapshotter, cfg.Labels, ctd.WithTimeout(60*time.Second)) if err != nil { return nil, err } opt.SessionManager = common.sessionManager - if platformsStr := c.GlobalStringSlice("containerd-worker-platform"); len(platformsStr) != 0 { + if platformsStr := cfg.Platforms; len(platformsStr) != 0 { platforms, err := parsePlatforms(platformsStr) if err != nil { return nil, errors.Wrap(err, "invalid platforms") diff --git a/cmd/buildkitd/main_oci_worker.go b/cmd/buildkitd/main_oci_worker.go index 65f70e65..9ae920d4 100644 --- a/cmd/buildkitd/main_oci_worker.go +++ b/cmd/buildkitd/main_oci_worker.go @@ -4,10 +4,12 @@ package main import ( "os/exec" + "strconv" ctdsnapshot "github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots/native" "github.com/containerd/containerd/snapshots/overlay" + "github.com/moby/buildkit/cmd/buildkitd/config" "github.com/moby/buildkit/worker" "github.com/moby/buildkit/worker/base" "github.com/moby/buildkit/worker/runc" @@ -18,11 +20,24 @@ import ( ) func init() { + defaultConf, _ := defaultConf() + + enabledValue := func(b *bool) string { + if b == nil { + return "auto" + } + return strconv.FormatBool(*b) + } + + if defaultConf.Workers.OCI.Snapshotter == "" { + defaultConf.Workers.OCI.Snapshotter = "auto" + } + flags := []cli.Flag{ cli.StringFlag{ Name: "oci-worker", Usage: "enable oci workers (true/false/auto)", - Value: "auto", + Value: enabledValue(defaultConf.Workers.OCI.Enabled), }, cli.StringSliceFlag{ Name: "oci-worker-labels", @@ -31,7 +46,7 @@ func init() { cli.StringFlag{ Name: "oci-worker-snapshotter", Usage: "name of snapshotter (overlayfs or native)", - Value: "auto", + Value: defaultConf.Workers.OCI.Snapshotter, }, cli.StringSliceFlag{ Name: "oci-worker-platform", @@ -61,34 +76,70 @@ func init() { // TODO: allow multiple oci runtimes } -func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker.Worker, error) { - boolOrAuto, err := parseBoolOrAuto(c.GlobalString("oci-worker")) - if err != nil { - return nil, err +func applyOCIFlags(c *cli.Context, cfg *config.Config) error { + if cfg.Workers.OCI.Snapshotter == "" { + cfg.Workers.OCI.Snapshotter = "auto" } - if (boolOrAuto == nil && !validOCIBinary()) || (boolOrAuto != nil && !*boolOrAuto) { - return nil, nil + + if c.GlobalIsSet("oci-worker") { + boolOrAuto, err := parseBoolOrAuto(c.GlobalString("oci-worker")) + if err != nil { + return err + } + cfg.Workers.OCI.Enabled = boolOrAuto } + labels, err := attrMap(c.GlobalStringSlice("oci-worker-labels")) if err != nil { + return err + } + for k, v := range labels { + cfg.Workers.OCI.Labels[k] = v + } + if c.GlobalIsSet("oci-worker-snapshotter") { + cfg.Workers.OCI.Snapshotter = c.GlobalString("oci-worker-snapshotter") + } + + if c.GlobalIsSet("rootless") || c.GlobalBool("rootless") { + cfg.Workers.OCI.Rootless = c.GlobalBool("rootless") + } + if c.GlobalIsSet("oci-worker-rootless") { + cfg.Workers.OCI.Rootless = c.GlobalBool("oci-worker-rootless") + } + + if platforms := c.GlobalStringSlice("oci-worker-platform"); len(platforms) != 0 { + cfg.Workers.OCI.Platforms = platforms + } + + return nil +} + +func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker.Worker, error) { + if err := applyOCIFlags(c, common.config); err != nil { return nil, err } - snFactory, err := snapshotterFactory(common.root, c.GlobalString("oci-worker-snapshotter")) + + cfg := common.config.Workers.OCI + + if (cfg.Enabled == nil && !validOCIBinary()) || (cfg.Enabled != nil && !*cfg.Enabled) { + return nil, nil + } + + snFactory, err := snapshotterFactory(common.config.Root, cfg.Snapshotter) if err != nil { return nil, err } - // GlobalBool works for BoolT as well - rootless := c.GlobalBool("oci-worker-rootless") || c.GlobalBool("rootless") - if rootless { + + if cfg.Rootless { logrus.Debugf("running in rootless mode") } - opt, err := runc.NewWorkerOpt(common.root, snFactory, rootless, labels) + opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, cfg.Labels) if err != nil { return nil, err } opt.SessionManager = common.sessionManager - platformsStr := c.GlobalStringSlice("oci-worker-platform") - if len(platformsStr) != 0 { + + if platformsStr := cfg.Platforms; len(platformsStr) != 0 { platforms, err := parsePlatforms(platformsStr) if err != nil { return nil, errors.Wrap(err, "invalid platforms") diff --git a/util/appdefaults/appdefaults_unix.go b/util/appdefaults/appdefaults_unix.go index 7b907ad3..6252147e 100644 --- a/util/appdefaults/appdefaults_unix.go +++ b/util/appdefaults/appdefaults_unix.go @@ -9,8 +9,9 @@ import ( ) const ( - Address = "unix:///run/buildkit/buildkitd.sock" - Root = "/var/lib/buildkit" + Address = "unix:///run/buildkit/buildkitd.sock" + Root = "/var/lib/buildkit" + ConfigDir = "/etc/buildkit" ) // UserAddress typically returns /run/user/$UID/buildkit/buildkitd.sock @@ -53,3 +54,16 @@ func UserRoot() string { } return Root } + +// UserConfigDir returns dir for storing config. /home/$USER/.config/buildkit/ +func UserConfigDir() string { + xdgConfigHome := os.Getenv("XDG_CONFIG_HOME") + if xdgConfigHome != "" { + return filepath.Join(xdgConfigHome, "buildkit") + } + home := os.Getenv("HOME") + if home != "" { + return filepath.Join(home, ".config", "buildkit") + } + return ConfigDir +} diff --git a/util/appdefaults/appdefaults_windows.go b/util/appdefaults/appdefaults_windows.go index dbc96c80..74f8389d 100644 --- a/util/appdefaults/appdefaults_windows.go +++ b/util/appdefaults/appdefaults_windows.go @@ -1,8 +1,9 @@ package appdefaults const ( - Address = "npipe:////./pipe/buildkitd" - Root = ".buildstate" + Address = "npipe:////./pipe/buildkitd" + Root = ".buildstate" + ConfigDir = "" ) func UserAddress() string { @@ -16,3 +17,7 @@ func EnsureUserAddressDir() error { func UserRoot() string { return Root } + +func UserConfigDir() string { + return ConfigDir +}