2021-10-29 04:29:55 +08:00
|
|
|
package confutil
|
|
|
|
|
|
|
|
import (
|
2024-10-18 19:28:45 +08:00
|
|
|
"crypto/rand"
|
|
|
|
"encoding/hex"
|
2021-10-29 04:29:55 +08:00
|
|
|
"os"
|
2021-10-30 12:15:04 +08:00
|
|
|
"path/filepath"
|
2024-10-18 19:28:45 +08:00
|
|
|
"sync"
|
2021-10-29 04:29:55 +08:00
|
|
|
|
2021-10-30 12:15:04 +08:00
|
|
|
"github.com/docker/cli/cli/command"
|
2024-10-18 19:28:45 +08:00
|
|
|
"github.com/docker/docker/pkg/ioutils"
|
2021-10-29 04:29:55 +08:00
|
|
|
"github.com/pelletier/go-toml"
|
|
|
|
"github.com/pkg/errors"
|
2024-10-18 19:28:45 +08:00
|
|
|
fs "github.com/tonistiigi/fsutil/copy"
|
2021-10-29 04:29:55 +08:00
|
|
|
)
|
|
|
|
|
2024-10-18 19:28:45 +08:00
|
|
|
const defaultBuildKitConfigFile = "buildkitd.default.toml"
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
dir string
|
|
|
|
chowner *chowner
|
|
|
|
}
|
|
|
|
|
|
|
|
type chowner struct {
|
|
|
|
uid int
|
|
|
|
gid int
|
|
|
|
}
|
|
|
|
|
|
|
|
type ConfigOption func(*configOptions)
|
|
|
|
|
|
|
|
type configOptions struct {
|
|
|
|
dir string
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithDir(dir string) ConfigOption {
|
|
|
|
return func(o *configOptions) {
|
|
|
|
o.dir = dir
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewConfig(dockerCli command.Cli, opts ...ConfigOption) *Config {
|
|
|
|
co := configOptions{}
|
|
|
|
for _, opt := range opts {
|
|
|
|
opt(&co)
|
|
|
|
}
|
|
|
|
|
|
|
|
configDir := co.dir
|
|
|
|
if configDir == "" {
|
|
|
|
configDir = os.Getenv("BUILDX_CONFIG")
|
|
|
|
if configDir == "" {
|
|
|
|
configDir = filepath.Join(filepath.Dir(dockerCli.ConfigFile().Filename), "buildx")
|
|
|
|
}
|
2021-10-30 12:15:04 +08:00
|
|
|
}
|
|
|
|
|
2024-10-18 19:28:45 +08:00
|
|
|
return &Config{
|
|
|
|
dir: configDir,
|
|
|
|
chowner: sudoer(configDir),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dir will look for correct configuration store path;
|
|
|
|
// if `$BUILDX_CONFIG` is set - use it, otherwise use parent directory
|
|
|
|
// of Docker config file (i.e. `${DOCKER_CONFIG}/buildx`)
|
|
|
|
func (c *Config) Dir() string {
|
|
|
|
return c.dir
|
2021-10-30 12:15:04 +08:00
|
|
|
}
|
|
|
|
|
2024-10-18 19:28:45 +08:00
|
|
|
// BuildKitConfigFile returns the default BuildKit configuration file path
|
|
|
|
func (c *Config) BuildKitConfigFile() (string, bool) {
|
|
|
|
f := filepath.Join(c.dir, defaultBuildKitConfigFile)
|
2022-05-13 17:53:53 +08:00
|
|
|
if _, err := os.Stat(f); err == nil {
|
|
|
|
return f, true
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
2024-10-18 19:28:45 +08:00
|
|
|
// MkdirAll creates a directory and all necessary parents within the config dir.
|
|
|
|
func (c *Config) MkdirAll(dir string, perm os.FileMode) error {
|
|
|
|
var chown fs.Chowner
|
|
|
|
if c.chowner != nil {
|
|
|
|
chown = func(user *fs.User) (*fs.User, error) {
|
|
|
|
return &fs.User{UID: c.chowner.uid, GID: c.chowner.gid}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d := filepath.Join(c.dir, dir)
|
|
|
|
st, err := os.Stat(d)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
2024-10-29 05:56:43 +08:00
|
|
|
_, err := fs.MkdirAll(d, perm, chown, nil)
|
|
|
|
return err
|
2024-10-18 19:28:45 +08:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// if directory already exists, fix the owner if necessary
|
|
|
|
if c.chowner == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
currentOwner := fileOwner(st)
|
|
|
|
if currentOwner != nil && (currentOwner.uid != c.chowner.uid || currentOwner.gid != c.chowner.gid) {
|
|
|
|
return os.Chown(d, c.chowner.uid, c.chowner.gid)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AtomicWriteFile writes data to a file within the config dir atomically
|
|
|
|
func (c *Config) AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
|
|
|
|
f := filepath.Join(c.dir, filename)
|
|
|
|
if err := ioutils.AtomicWriteFile(f, data, perm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if c.chowner == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return os.Chown(f, c.chowner.uid, c.chowner.gid)
|
|
|
|
}
|
|
|
|
|
|
|
|
var nodeIdentifierMu sync.Mutex
|
|
|
|
|
|
|
|
func (c *Config) TryNodeIdentifier() (out string) {
|
|
|
|
nodeIdentifierMu.Lock()
|
|
|
|
defer nodeIdentifierMu.Unlock()
|
|
|
|
sessionFilename := ".buildNodeID"
|
|
|
|
sessionFilepath := filepath.Join(c.Dir(), sessionFilename)
|
|
|
|
if _, err := os.Lstat(sessionFilepath); err != nil {
|
|
|
|
if os.IsNotExist(err) { // create a new file with stored randomness
|
|
|
|
b := make([]byte, 8)
|
|
|
|
if _, err := rand.Read(b); err != nil {
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
if err := c.AtomicWriteFile(sessionFilename, []byte(hex.EncodeToString(b)), 0600); err != nil {
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dt, err := os.ReadFile(sessionFilepath)
|
|
|
|
if err == nil {
|
|
|
|
return string(dt)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-09-11 18:27:29 +08:00
|
|
|
// LoadConfigTree loads BuildKit config toml tree
|
|
|
|
func LoadConfigTree(fp string) (*toml.Tree, error) {
|
2021-10-29 04:29:55 +08:00
|
|
|
f, err := os.Open(fp)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return nil, errors.Wrapf(err, "failed to load config from %s", fp)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
t, err := toml.LoadReader(f)
|
|
|
|
if err != nil {
|
|
|
|
return t, errors.Wrap(err, "failed to parse config")
|
|
|
|
}
|
|
|
|
return t, nil
|
|
|
|
}
|