Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remote driver #1078

Merged
merged 2 commits into from
Apr 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
- docker
- docker-container
- kubernetes
- remote
buildkit:
- moby/buildkit:buildx-stable-1
- moby/buildkit:master
Expand All @@ -68,13 +69,19 @@ jobs:
include:
- driver: kubernetes
driver-opt: qemu.install=true
- driver: remote
endpoint: tcp://localhost:1234
exclude:
- driver: docker
multi-node: mnode-true
- driver: docker
buildkit-cfg: bkcfg-true
- driver: docker-container
multi-node: mnode-true
- driver: remote
multi-node: mnode-true
- driver: remote
buildkit-cfg: bkcfg-true
steps:
-
name: Checkout
Expand Down Expand Up @@ -128,6 +135,15 @@ jobs:
if: matrix.driver == 'kubernetes'
run: |
kubectl get nodes
-
name: Launch remote buildkitd
if: matrix.driver == 'remote'
run: |
docker run -d --privileged \
--name=remote-buildkit \
-p 1234:1234 \
${{ matrix.buildkit }} \
--addr tcp://0.0.0.0:1234
-
name: Test
run: |
Expand All @@ -136,4 +152,5 @@ jobs:
BUILDKIT_IMAGE: ${{ matrix.buildkit }}
DRIVER: ${{ matrix.driver }}
DRIVER_OPT: ${{ matrix.driver-opt }}
ENDPOINT: ${{ matrix.endpoint }}
PLATFORMS: ${{ matrix.platforms }}
1 change: 1 addition & 0 deletions cmd/buildx/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
_ "github.com/docker/buildx/driver/docker"
_ "github.com/docker/buildx/driver/docker-container"
_ "github.com/docker/buildx/driver/kubernetes"
_ "github.com/docker/buildx/driver/remote"
)

func init() {
Expand Down
71 changes: 48 additions & 23 deletions commands/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,26 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
}
}

buildkitHost := os.Getenv("BUILDKIT_HOST")

driverName := in.driver
if driverName == "" {
f, err := driver.GetDefaultFactory(ctx, dockerCli.Client(), true)
if err != nil {
return err
}
if f == nil {
return errors.Errorf("no valid drivers found")
if len(args) == 0 && buildkitHost != "" {
driverName = "remote"
} else {
var arg string
if len(args) > 0 {
arg = args[0]
}
f, err := driver.GetDefaultFactory(ctx, arg, dockerCli.Client(), true)
if err != nil {
return err
}
if f == nil {
return errors.Errorf("no valid drivers found")
}
driverName = f.Name()
}
driverName = f.Name()
}

if driver.GetFactory(driverName, true) == nil {
Expand Down Expand Up @@ -129,44 +139,59 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
}

var ep string
var setEp bool
if in.actionLeave {
if err := ng.Leave(in.nodeName); err != nil {
return err
}
} else {
if len(args) > 0 {
switch {
case driverName == "kubernetes":
// naming endpoint to make --append works
ep = (&url.URL{
Scheme: driverName,
Path: "/" + in.name,
RawQuery: (&url.Values{
"deployment": {in.nodeName},
"kubeconfig": {os.Getenv("KUBECONFIG")},
}).Encode(),
}).String()
setEp = false
case driverName == "remote":
if len(args) > 0 {
ep = args[0]
} else if buildkitHost != "" {
ep = buildkitHost
} else {
return errors.Errorf("no remote endpoint provided")
}
ep, err = validateBuildkitEndpoint(ep)
if err != nil {
return err
}
setEp = true
case len(args) > 0:
ep, err = validateEndpoint(dockerCli, args[0])
if err != nil {
return err
}
} else {
setEp = true
default:
if dockerCli.CurrentContext() == "default" && dockerCli.DockerEndpoint().TLSData != nil {
return errors.Errorf("could not create a builder instance with TLS data loaded from environment. Please use `docker context create <context-name>` to create a context for current environment and then create a builder instance with `docker buildx create <context-name>`")
}

ep, err = storeutil.GetCurrentEndpoint(dockerCli)
if err != nil {
return err
}
}

if in.driver == "kubernetes" {
// naming endpoint to make --append works
ep = (&url.URL{
Scheme: in.driver,
Path: "/" + in.name,
RawQuery: (&url.Values{
"deployment": {in.nodeName},
"kubeconfig": {os.Getenv("KUBECONFIG")},
}).Encode(),
}).String()
setEp = false
}

m, err := csvToMap(in.driverOpts)
if err != nil {
return err
}
if err := ng.Update(in.nodeName, ep, in.platform, len(args) > 0, in.actionAppend, flags, in.configFile, m); err != nil {
if err := ng.Update(in.nodeName, ep, in.platform, setEp, in.actionAppend, flags, in.configFile, m); err != nil {
return err
}
}
Expand Down
22 changes: 18 additions & 4 deletions commands/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ func validateEndpoint(dockerCli command.Cli, ep string) (string, error) {
return h, nil
}

// validateBuildkitEndpoint validates that endpoint is a valid buildkit host
func validateBuildkitEndpoint(ep string) (string, error) {
endpoint, err := url.Parse(ep)
if err != nil {
return "", errors.Wrapf(err, "failed to parse endpoint %s", ep)
}
if endpoint.Scheme != "tcp" && endpoint.Scheme != "unix" {
return "", errors.Errorf("unrecognized url scheme %s", endpoint.Scheme)
}
return ep, nil
}

// driversForNodeGroup returns drivers for a nodegroup instance
func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, contextPathHash string) ([]build.DriverInfo, error) {
eg, _ := errgroup.WithContext(ctx)
Expand All @@ -54,11 +66,12 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
return nil, errors.Errorf("failed to find driver %q", f)
}
} else {
dockerapi, err := clientForEndpoint(dockerCli, ng.Nodes[0].Endpoint)
ep := ng.Nodes[0].Endpoint
dockerapi, err := clientForEndpoint(dockerCli, ep)
if err != nil {
return nil, err
}
f, err = driver.GetDefaultFactory(ctx, dockerapi, false)
f, err = driver.GetDefaultFactory(ctx, ep, dockerapi, false)
if err != nil {
return nil, err
}
Expand All @@ -80,6 +93,7 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
defer func() {
dis[i] = di
}()

dockerapi, err := clientForEndpoint(dockerCli, n.Endpoint)
if err != nil {
di.Err = err
Expand Down Expand Up @@ -118,7 +132,7 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
}
}

d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, f, dockerapi, imageopt.Auth, kcc, n.Flags, n.Files, n.DriverOpts, n.Platforms, contextPathHash)
d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, f, n.Endpoint, dockerapi, imageopt.Auth, kcc, n.Flags, n.Files, n.DriverOpts, n.Platforms, contextPathHash)
if err != nil {
di.Err = err
return nil
Expand Down Expand Up @@ -259,7 +273,7 @@ func getDefaultDrivers(ctx context.Context, dockerCli command.Cli, defaultOnly b
return nil, err
}

d, err := driver.GetDriver(ctx, "buildx_buildkit_default", nil, dockerCli.Client(), imageopt.Auth, nil, nil, nil, nil, nil, contextPathHash)
d, err := driver.GetDriver(ctx, "buildx_buildkit_default", nil, "", dockerCli.Client(), imageopt.Auth, nil, nil, nil, nil, nil, contextPathHash)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion driver/docker-container/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (*factory) Usage() string {
return "docker-container"
}

func (*factory) Priority(ctx context.Context, api dockerclient.APIClient) int {
func (*factory) Priority(ctx context.Context, endpoint string, api dockerclient.APIClient) int {
if api == nil {
return priorityUnsupported
}
Expand Down
2 changes: 1 addition & 1 deletion driver/docker/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (*factory) Usage() string {
return "docker"
}

func (*factory) Priority(ctx context.Context, api dockerclient.APIClient) int {
func (*factory) Priority(ctx context.Context, endpoint string, api dockerclient.APIClient) int {
if api == nil {
return priorityUnsupported
}
Expand Down
2 changes: 1 addition & 1 deletion driver/kubernetes/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (*factory) Usage() string {
return DriverName
}

func (*factory) Priority(ctx context.Context, api dockerclient.APIClient) int {
func (*factory) Priority(ctx context.Context, endpoint string, api dockerclient.APIClient) int {
if api == nil {
return priorityUnsupported
}
Expand Down
12 changes: 7 additions & 5 deletions driver/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
type Factory interface {
Name() string
Usage() string
Priority(context.Context, dockerclient.APIClient) int
Priority(ctx context.Context, endpoint string, api dockerclient.APIClient) int
New(ctx context.Context, cfg InitConfig) (Driver, error)
AllowsInstances() bool
}
Expand Down Expand Up @@ -50,6 +50,7 @@ func (k KubeClientConfigInCluster) Namespace() (string, bool, error) {
type InitConfig struct {
// This object needs updates to be generic for different drivers
Name string
EndpointAddr string
DockerAPI dockerclient.APIClient
KubeClientConfig KubeClientConfig
BuildkitFlags []string
Expand All @@ -70,7 +71,7 @@ func Register(f Factory) {
drivers[f.Name()] = f
}

func GetDefaultFactory(ctx context.Context, c dockerclient.APIClient, instanceRequired bool) (Factory, error) {
func GetDefaultFactory(ctx context.Context, ep string, c dockerclient.APIClient, instanceRequired bool) (Factory, error) {
if len(drivers) == 0 {
return nil, errors.Errorf("no drivers available")
}
Expand All @@ -83,7 +84,7 @@ func GetDefaultFactory(ctx context.Context, c dockerclient.APIClient, instanceRe
if instanceRequired && !f.AllowsInstances() {
continue
}
dd = append(dd, p{f: f, priority: f.Priority(ctx, c)})
dd = append(dd, p{f: f, priority: f.Priority(ctx, ep, c)})
}
sort.Slice(dd, func(i, j int) bool {
return dd[i].priority < dd[j].priority
Expand All @@ -103,8 +104,9 @@ func GetFactory(name string, instanceRequired bool) Factory {
return nil
}

func GetDriver(ctx context.Context, name string, f Factory, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, flags []string, files map[string][]byte, do map[string]string, platforms []specs.Platform, contextPathHash string) (Driver, error) {
func GetDriver(ctx context.Context, name string, f Factory, endpointAddr string, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, flags []string, files map[string][]byte, do map[string]string, platforms []specs.Platform, contextPathHash string) (Driver, error) {
ic := InitConfig{
EndpointAddr: endpointAddr,
DockerAPI: api,
KubeClientConfig: kcc,
Name: name,
Expand All @@ -117,7 +119,7 @@ func GetDriver(ctx context.Context, name string, f Factory, api dockerclient.API
}
if f == nil {
var err error
f, err = GetDefaultFactory(ctx, api, false)
f, err = GetDefaultFactory(ctx, endpointAddr, api, false)
if err != nil {
return nil, err
}
Expand Down
80 changes: 80 additions & 0 deletions driver/remote/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package remote

import (
"context"

"github.com/docker/buildx/driver"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/pkg/errors"
)

type Driver struct {
factory driver.Factory
driver.InitConfig
*tlsOpts
}

type tlsOpts struct {
serverName string
caCert string
cert string
key string
}

func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
return nil
}

func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
c, err := d.Client(ctx)
if err != nil {
return nil, errors.Wrapf(driver.ErrNotConnecting, err.Error())
}

if _, err := c.ListWorkers(ctx); err != nil {
return nil, errors.Wrapf(driver.ErrNotConnecting, err.Error())
}

return &driver.Info{
Status: driver.Running,
}, nil
}

func (d *Driver) Stop(ctx context.Context, force bool) error {
return nil
}

func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
return nil
}

func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
opts := []client.ClientOpt{}
if d.tlsOpts != nil {
opts = append(opts, client.WithCredentials(d.tlsOpts.serverName, d.tlsOpts.caCert, d.tlsOpts.cert, d.tlsOpts.key))
}

return client.New(ctx, d.InitConfig.EndpointAddr, opts...)
}

func (d *Driver) Features() map[driver.Feature]bool {
return map[driver.Feature]bool{
driver.OCIExporter: true,
driver.DockerExporter: false,
driver.CacheExport: true,
driver.MultiPlatform: true,
}
}

func (d *Driver) Factory() driver.Factory {
return d.factory
}

func (d *Driver) IsMobyDriver() bool {
return false
}

func (d *Driver) Config() driver.InitConfig {
return d.InitConfig
}
Loading