mirror of https://github.com/docker/buildx.git
190 lines
4.9 KiB
Go
190 lines
4.9 KiB
Go
package driver
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
dockerclient "github.com/docker/docker/client"
|
|
"github.com/moby/buildkit/client"
|
|
"github.com/moby/buildkit/util/tracing/detect"
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
"k8s.io/client-go/rest"
|
|
)
|
|
|
|
type Factory interface {
|
|
Name() string
|
|
Usage() string
|
|
Priority(ctx context.Context, endpoint string, api dockerclient.APIClient, dialMeta map[string][]string) int
|
|
New(ctx context.Context, cfg InitConfig) (Driver, error)
|
|
AllowsInstances() bool
|
|
}
|
|
|
|
type BuildkitConfig struct {
|
|
// Entitlements []string
|
|
// Rootless bool
|
|
}
|
|
|
|
type KubeClientConfig interface {
|
|
ClientConfig() (*rest.Config, error)
|
|
Namespace() (string, bool, error)
|
|
}
|
|
|
|
type KubeClientConfigInCluster struct{}
|
|
|
|
func (k KubeClientConfigInCluster) ClientConfig() (*rest.Config, error) {
|
|
return rest.InClusterConfig()
|
|
}
|
|
|
|
func (k KubeClientConfigInCluster) Namespace() (string, bool, error) {
|
|
namespace, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
return strings.TrimSpace(string(namespace)), true, nil
|
|
}
|
|
|
|
type InitConfig struct {
|
|
// This object needs updates to be generic for different drivers
|
|
Name string
|
|
EndpointAddr string
|
|
DockerAPI dockerclient.APIClient
|
|
KubeClientConfig KubeClientConfig
|
|
BuildkitdFlags []string
|
|
Files map[string][]byte
|
|
DriverOpts map[string]string
|
|
Auth Auth
|
|
Platforms []specs.Platform
|
|
ContextPathHash string // can be used for determining pods in the driver instance
|
|
DialMeta map[string][]string
|
|
}
|
|
|
|
var drivers map[string]Factory
|
|
|
|
func Register(f Factory) {
|
|
if drivers == nil {
|
|
drivers = map[string]Factory{}
|
|
}
|
|
drivers[f.Name()] = f
|
|
}
|
|
|
|
func GetDefaultFactory(ctx context.Context, ep string, c dockerclient.APIClient, instanceRequired bool, dialMeta map[string][]string) (Factory, error) {
|
|
if len(drivers) == 0 {
|
|
return nil, errors.Errorf("no drivers available")
|
|
}
|
|
type p struct {
|
|
f Factory
|
|
priority int
|
|
}
|
|
dd := make([]p, 0, len(drivers))
|
|
for _, f := range drivers {
|
|
if instanceRequired && !f.AllowsInstances() {
|
|
continue
|
|
}
|
|
dd = append(dd, p{f: f, priority: f.Priority(ctx, ep, c, dialMeta)})
|
|
}
|
|
sort.Slice(dd, func(i, j int) bool {
|
|
return dd[i].priority < dd[j].priority
|
|
})
|
|
return dd[0].f, nil
|
|
}
|
|
|
|
func GetFactory(name string, instanceRequired bool) (Factory, error) {
|
|
for _, f := range drivers {
|
|
if f.Name() == name {
|
|
if instanceRequired && !f.AllowsInstances() {
|
|
return nil, errors.Errorf("additional instances of driver %q cannot be created", name)
|
|
}
|
|
return f, nil
|
|
}
|
|
}
|
|
return nil, errors.Errorf("failed to find driver %q", name)
|
|
}
|
|
|
|
func GetDriver(ctx context.Context, name string, f Factory, endpointAddr string, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, buildkitdFlags []string, files map[string][]byte, do map[string]string, platforms []specs.Platform, contextPathHash string, dialMeta map[string][]string) (*DriverHandle, error) {
|
|
ic := InitConfig{
|
|
EndpointAddr: endpointAddr,
|
|
DockerAPI: api,
|
|
KubeClientConfig: kcc,
|
|
Name: name,
|
|
BuildkitdFlags: buildkitdFlags,
|
|
DriverOpts: do,
|
|
Auth: auth,
|
|
Platforms: platforms,
|
|
ContextPathHash: contextPathHash,
|
|
DialMeta: dialMeta,
|
|
Files: files,
|
|
}
|
|
if f == nil {
|
|
var err error
|
|
f, err = GetDefaultFactory(ctx, endpointAddr, api, false, dialMeta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
d, err := f.New(ctx, ic)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DriverHandle{Driver: d}, nil
|
|
}
|
|
|
|
func GetFactories(instanceRequired bool) []Factory {
|
|
ds := make([]Factory, 0, len(drivers))
|
|
for _, d := range drivers {
|
|
if instanceRequired && !d.AllowsInstances() {
|
|
continue
|
|
}
|
|
ds = append(ds, d)
|
|
}
|
|
sort.Slice(ds, func(i, j int) bool {
|
|
return ds[i].Name() < ds[j].Name()
|
|
})
|
|
return ds
|
|
}
|
|
|
|
type DriverHandle struct {
|
|
Driver
|
|
client *client.Client
|
|
err error
|
|
once sync.Once
|
|
historyAPISupportedOnce sync.Once
|
|
historyAPISupported bool
|
|
}
|
|
|
|
func (d *DriverHandle) Client(ctx context.Context) (*client.Client, error) {
|
|
d.once.Do(func() {
|
|
opts, err := d.getClientOptions()
|
|
if err != nil {
|
|
d.err = err
|
|
return
|
|
}
|
|
d.client, d.err = d.Driver.Client(ctx, opts...)
|
|
})
|
|
return d.client, d.err
|
|
}
|
|
|
|
func (d *DriverHandle) getClientOptions() ([]client.ClientOpt, error) {
|
|
exp, _, err := detect.Exporter()
|
|
if err != nil {
|
|
return nil, err
|
|
} else if td, ok := exp.(client.TracerDelegate); ok {
|
|
return []client.ClientOpt{
|
|
client.WithTracerDelegate(td),
|
|
}, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (d *DriverHandle) HistoryAPISupported(ctx context.Context) bool {
|
|
d.historyAPISupportedOnce.Do(func() {
|
|
if c, err := d.Client(ctx); err == nil {
|
|
d.historyAPISupported = historyAPISupported(ctx, c)
|
|
}
|
|
})
|
|
return d.historyAPISupported
|
|
}
|