2019-04-13 07:39:06 +08:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"os"
|
|
|
|
|
2019-04-25 10:29:56 +08:00
|
|
|
"github.com/docker/buildx/build"
|
|
|
|
"github.com/docker/buildx/driver"
|
2022-02-27 07:35:39 +08:00
|
|
|
ctxkube "github.com/docker/buildx/driver/kubernetes/context"
|
2022-05-17 23:12:07 +08:00
|
|
|
remoteutil "github.com/docker/buildx/driver/remote/util"
|
2019-04-25 10:29:56 +08:00
|
|
|
"github.com/docker/buildx/store"
|
2021-11-04 12:17:27 +08:00
|
|
|
"github.com/docker/buildx/store/storeutil"
|
2019-04-25 10:29:56 +08:00
|
|
|
"github.com/docker/buildx/util/platformutil"
|
2021-08-12 14:45:40 +08:00
|
|
|
"github.com/docker/buildx/util/progress"
|
2019-04-13 07:39:06 +08:00
|
|
|
"github.com/docker/cli/cli/command"
|
|
|
|
"github.com/docker/cli/cli/context/docker"
|
|
|
|
dopts "github.com/docker/cli/opts"
|
|
|
|
dockerclient "github.com/docker/docker/client"
|
2022-04-09 07:22:33 +08:00
|
|
|
"github.com/moby/buildkit/util/grpcerrors"
|
2021-08-12 14:45:40 +08:00
|
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
2019-04-13 07:39:06 +08:00
|
|
|
"github.com/pkg/errors"
|
2019-10-21 14:02:37 +08:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-04-13 07:39:06 +08:00
|
|
|
"golang.org/x/sync/errgroup"
|
2022-04-09 07:22:33 +08:00
|
|
|
"google.golang.org/grpc/codes"
|
2019-04-13 07:39:06 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// validateEndpoint validates that endpoint is either a context or a docker host
|
|
|
|
func validateEndpoint(dockerCli command.Cli, ep string) (string, error) {
|
2021-11-04 12:17:27 +08:00
|
|
|
de, err := storeutil.GetDockerEndpoint(dockerCli, ep)
|
2019-04-13 07:39:06 +08:00
|
|
|
if err == nil && de != "" {
|
|
|
|
if ep == "default" {
|
|
|
|
return de, nil
|
|
|
|
}
|
|
|
|
return ep, nil
|
|
|
|
}
|
|
|
|
h, err := dopts.ParseHost(true, ep)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrapf(err, "failed to parse endpoint %s", ep)
|
|
|
|
}
|
|
|
|
return h, nil
|
|
|
|
}
|
|
|
|
|
2022-04-25 18:28:40 +08:00
|
|
|
// validateBuildkitEndpoint validates that endpoint is a valid buildkit host
|
|
|
|
func validateBuildkitEndpoint(ep string) (string, error) {
|
2022-05-17 23:12:07 +08:00
|
|
|
if err := remoteutil.IsValidEndpoint(ep); err != nil {
|
|
|
|
return "", err
|
2022-04-25 18:28:40 +08:00
|
|
|
}
|
|
|
|
return ep, nil
|
|
|
|
}
|
|
|
|
|
2019-04-13 07:39:06 +08:00
|
|
|
// driversForNodeGroup returns drivers for a nodegroup instance
|
2019-10-21 14:02:37 +08:00
|
|
|
func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, contextPathHash string) ([]build.DriverInfo, error) {
|
2019-04-13 07:39:06 +08:00
|
|
|
eg, _ := errgroup.WithContext(ctx)
|
|
|
|
|
|
|
|
dis := make([]build.DriverInfo, len(ng.Nodes))
|
|
|
|
|
|
|
|
var f driver.Factory
|
|
|
|
if ng.Driver != "" {
|
2022-08-16 17:58:23 +08:00
|
|
|
var err error
|
|
|
|
f, err = driver.GetFactory(ng.Driver, true)
|
|
|
|
if err != nil {
|
2022-08-18 21:20:47 +08:00
|
|
|
return nil, err
|
2019-04-13 07:39:06 +08:00
|
|
|
}
|
|
|
|
} else {
|
2022-07-29 23:39:05 +08:00
|
|
|
// empty driver means nodegroup was implicitly created as a default
|
|
|
|
// driver for a docker context and allows falling back to a
|
|
|
|
// docker-container driver for older daemon that doesn't support
|
|
|
|
// buildkit (< 18.06).
|
2022-04-25 18:28:40 +08:00
|
|
|
ep := ng.Nodes[0].Endpoint
|
|
|
|
dockerapi, err := clientForEndpoint(dockerCli, ep)
|
2019-04-13 07:39:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-07-29 23:39:05 +08:00
|
|
|
// check if endpoint is healthy is needed to determine the driver type.
|
|
|
|
// if this fails then can't continue with driver selection.
|
|
|
|
if _, err = dockerapi.Ping(ctx); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-04-25 18:28:40 +08:00
|
|
|
f, err = driver.GetDefaultFactory(ctx, ep, dockerapi, false)
|
2019-04-13 07:39:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-04-16 01:21:09 +08:00
|
|
|
ng.Driver = f.Name()
|
2019-04-13 07:39:06 +08:00
|
|
|
}
|
2021-11-04 12:17:27 +08:00
|
|
|
imageopt, err := storeutil.GetImageConfig(dockerCli, ng)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-04-13 07:39:06 +08:00
|
|
|
|
|
|
|
for i, n := range ng.Nodes {
|
|
|
|
func(i int, n store.Node) {
|
|
|
|
eg.Go(func() error {
|
|
|
|
di := build.DriverInfo{
|
2022-02-18 14:21:08 +08:00
|
|
|
Name: n.Name,
|
|
|
|
Platform: n.Platforms,
|
|
|
|
ProxyConfig: storeutil.GetProxyConfig(dockerCli),
|
2019-04-13 07:39:06 +08:00
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
dis[i] = di
|
|
|
|
}()
|
2022-03-17 23:36:35 +08:00
|
|
|
|
2019-04-13 07:39:06 +08:00
|
|
|
dockerapi, err := clientForEndpoint(dockerCli, n.Endpoint)
|
|
|
|
if err != nil {
|
|
|
|
di.Err = err
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-10-21 14:02:37 +08:00
|
|
|
contextStore := dockerCli.ContextStore()
|
2020-08-28 10:27:56 +08:00
|
|
|
|
|
|
|
var kcc driver.KubeClientConfig
|
2022-11-17 07:34:42 +08:00
|
|
|
kcc, err = ctxkube.ConfigFromEndpoint(n.Endpoint, contextStore)
|
2019-10-21 14:02:37 +08:00
|
|
|
if err != nil {
|
|
|
|
// err is returned if n.Endpoint is non-context name like "unix:///var/run/docker.sock".
|
|
|
|
// try again with name="default".
|
|
|
|
// FIXME: n should retain real context name.
|
2022-11-17 07:34:42 +08:00
|
|
|
kcc, err = ctxkube.ConfigFromEndpoint("default", contextStore)
|
2019-10-21 14:02:37 +08:00
|
|
|
if err != nil {
|
|
|
|
logrus.Error(err)
|
|
|
|
}
|
|
|
|
}
|
2020-12-03 12:13:11 +08:00
|
|
|
|
|
|
|
tryToUseKubeConfigInCluster := false
|
2020-08-28 10:27:56 +08:00
|
|
|
if kcc == nil {
|
2020-12-03 12:13:11 +08:00
|
|
|
tryToUseKubeConfigInCluster = true
|
|
|
|
} else {
|
|
|
|
if _, err := kcc.ClientConfig(); err != nil {
|
|
|
|
tryToUseKubeConfigInCluster = true
|
|
|
|
}
|
2020-08-28 10:27:56 +08:00
|
|
|
}
|
2020-12-03 12:13:11 +08:00
|
|
|
if tryToUseKubeConfigInCluster {
|
|
|
|
kccInCluster := driver.KubeClientConfigInCluster{}
|
|
|
|
if _, err := kccInCluster.ClientConfig(); err == nil {
|
|
|
|
logrus.Debug("using kube config in cluster")
|
|
|
|
kcc = kccInCluster
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-25 18:28:40 +08:00
|
|
|
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)
|
2019-04-13 07:39:06 +08:00
|
|
|
if err != nil {
|
|
|
|
di.Err = err
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
di.Driver = d
|
2021-11-04 12:17:27 +08:00
|
|
|
di.ImageOpt = imageopt
|
2019-04-13 07:39:06 +08:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}(i, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := eg.Wait(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return dis, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// clientForEndpoint returns a docker client for an endpoint
|
|
|
|
func clientForEndpoint(dockerCli command.Cli, name string) (dockerclient.APIClient, error) {
|
2019-05-24 02:43:14 +08:00
|
|
|
list, err := dockerCli.ContextStore().List()
|
2019-04-13 07:39:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, l := range list {
|
|
|
|
if l.Name == name {
|
2022-08-04 21:38:04 +08:00
|
|
|
epm, err := docker.EndpointFromContext(l)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-04-13 07:39:06 +08:00
|
|
|
}
|
|
|
|
ep, err := docker.WithTLSData(dockerCli.ContextStore(), name, epm)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
clientOpts, err := ep.ClientOpts()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return dockerclient.NewClientWithOpts(clientOpts...)
|
|
|
|
}
|
|
|
|
}
|
2019-04-24 06:07:31 +08:00
|
|
|
|
|
|
|
ep := docker.Endpoint{
|
|
|
|
EndpointMeta: docker.EndpointMeta{
|
|
|
|
Host: name,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
clientOpts, err := ep.ClientOpts()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return dockerclient.NewClientWithOpts(clientOpts...)
|
2019-04-13 07:39:06 +08:00
|
|
|
}
|
|
|
|
|
2020-03-26 07:32:46 +08:00
|
|
|
func getInstanceOrDefault(ctx context.Context, dockerCli command.Cli, instance, contextPathHash string) ([]build.DriverInfo, error) {
|
2020-10-22 14:19:28 +08:00
|
|
|
var defaultOnly bool
|
|
|
|
|
|
|
|
if instance == "default" && instance != dockerCli.CurrentContext() {
|
|
|
|
return nil, errors.Errorf("use `docker --context=default buildx` to switch to default context")
|
|
|
|
}
|
|
|
|
if instance == "default" || instance == dockerCli.CurrentContext() {
|
|
|
|
instance = ""
|
|
|
|
defaultOnly = true
|
|
|
|
}
|
|
|
|
list, err := dockerCli.ContextStore().List()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, l := range list {
|
|
|
|
if l.Name == instance {
|
|
|
|
return nil, errors.Errorf("use `docker --context=%s buildx` to switch to context %s", instance, instance)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-26 07:32:46 +08:00
|
|
|
if instance != "" {
|
|
|
|
return getInstanceByName(ctx, dockerCli, instance, contextPathHash)
|
|
|
|
}
|
2020-10-22 14:19:28 +08:00
|
|
|
return getDefaultDrivers(ctx, dockerCli, defaultOnly, contextPathHash)
|
2020-03-26 07:32:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func getInstanceByName(ctx context.Context, dockerCli command.Cli, instance, contextPathHash string) ([]build.DriverInfo, error) {
|
2021-11-04 12:17:27 +08:00
|
|
|
txn, release, err := storeutil.GetStore(dockerCli)
|
2020-03-26 07:32:46 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer release()
|
|
|
|
|
|
|
|
ng, err := txn.NodeGroupByName(instance)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return driversForNodeGroup(ctx, dockerCli, ng, contextPathHash)
|
|
|
|
}
|
|
|
|
|
2019-04-13 07:39:06 +08:00
|
|
|
// getDefaultDrivers returns drivers based on current cli config
|
2020-10-22 14:19:28 +08:00
|
|
|
func getDefaultDrivers(ctx context.Context, dockerCli command.Cli, defaultOnly bool, contextPathHash string) ([]build.DriverInfo, error) {
|
2021-11-04 12:17:27 +08:00
|
|
|
txn, release, err := storeutil.GetStore(dockerCli)
|
2019-04-13 07:39:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer release()
|
|
|
|
|
2020-10-22 14:19:28 +08:00
|
|
|
if !defaultOnly {
|
2021-11-04 12:17:27 +08:00
|
|
|
ng, err := storeutil.GetCurrentInstance(txn, dockerCli)
|
2020-10-22 14:19:28 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-04-13 07:39:06 +08:00
|
|
|
|
2020-10-22 14:19:28 +08:00
|
|
|
if ng != nil {
|
|
|
|
return driversForNodeGroup(ctx, dockerCli, ng, contextPathHash)
|
|
|
|
}
|
2019-04-13 07:39:06 +08:00
|
|
|
}
|
|
|
|
|
2021-11-04 12:17:27 +08:00
|
|
|
imageopt, err := storeutil.GetImageConfig(dockerCli, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-04-25 18:28:40 +08:00
|
|
|
d, err := driver.GetDriver(ctx, "buildx_buildkit_default", nil, "", dockerCli.Client(), imageopt.Auth, nil, nil, nil, nil, nil, contextPathHash)
|
2019-04-13 07:39:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return []build.DriverInfo{
|
|
|
|
{
|
2022-02-18 14:21:08 +08:00
|
|
|
Name: "default",
|
|
|
|
Driver: d,
|
|
|
|
ImageOpt: imageopt,
|
|
|
|
ProxyConfig: storeutil.GetProxyConfig(dockerCli),
|
2019-04-13 07:39:06 +08:00
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-04-16 01:21:09 +08:00
|
|
|
func loadInfoData(ctx context.Context, d *dinfo) error {
|
|
|
|
if d.di.Driver == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
info, err := d.di.Driver.Info(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
d.info = info
|
|
|
|
if info.Status == driver.Running {
|
|
|
|
c, err := d.di.Driver.Client(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
workers, err := c.ListWorkers(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "listing workers")
|
|
|
|
}
|
|
|
|
for _, w := range workers {
|
2021-11-23 15:57:42 +08:00
|
|
|
d.platforms = append(d.platforms, w.Platforms...)
|
2019-04-16 01:21:09 +08:00
|
|
|
}
|
2019-04-18 08:55:27 +08:00
|
|
|
d.platforms = platformutil.Dedupe(d.platforms)
|
2022-04-09 07:22:33 +08:00
|
|
|
inf, err := c.Info(ctx)
|
|
|
|
if err != nil {
|
|
|
|
if st, ok := grpcerrors.AsGRPCStatus(err); ok && st.Code() == codes.Unimplemented {
|
2022-05-09 22:17:01 +08:00
|
|
|
d.version, err = d.di.Driver.Version(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "getting version")
|
|
|
|
}
|
2022-04-09 07:22:33 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
d.version = inf.BuildkitVersion.Version
|
|
|
|
}
|
2019-04-16 01:21:09 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadNodeGroupData(ctx context.Context, dockerCli command.Cli, ngi *nginfo) error {
|
|
|
|
eg, _ := errgroup.WithContext(ctx)
|
|
|
|
|
2019-10-21 14:02:37 +08:00
|
|
|
dis, err := driversForNodeGroup(ctx, dockerCli, ngi.ng, "")
|
2019-04-16 01:21:09 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ngi.drivers = make([]dinfo, len(dis))
|
|
|
|
for i, di := range dis {
|
2019-04-16 08:01:00 +08:00
|
|
|
d := di
|
|
|
|
ngi.drivers[i].di = &d
|
2019-04-16 01:21:09 +08:00
|
|
|
func(d *dinfo) {
|
|
|
|
eg.Go(func() error {
|
|
|
|
if err := loadInfoData(ctx, d); err != nil {
|
|
|
|
d.err = err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}(&ngi.drivers[i])
|
|
|
|
}
|
|
|
|
|
2019-11-12 18:10:39 +08:00
|
|
|
if eg.Wait(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-28 14:51:58 +08:00
|
|
|
|
2020-12-15 16:02:04 +08:00
|
|
|
kubernetesDriverCount := 0
|
|
|
|
|
|
|
|
for _, di := range ngi.drivers {
|
|
|
|
if di.info != nil && len(di.info.DynamicNodes) > 0 {
|
|
|
|
kubernetesDriverCount++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isAllKubernetesDrivers := len(ngi.drivers) == kubernetesDriverCount
|
|
|
|
|
|
|
|
if isAllKubernetesDrivers {
|
|
|
|
var drivers []dinfo
|
|
|
|
var dynamicNodes []store.Node
|
|
|
|
|
2020-08-28 14:51:58 +08:00
|
|
|
for _, di := range ngi.drivers {
|
|
|
|
// dynamic nodes are used in Kubernetes driver.
|
|
|
|
// Kubernetes pods are dynamically mapped to BuildKit Nodes.
|
|
|
|
if di.info != nil && len(di.info.DynamicNodes) > 0 {
|
|
|
|
for i := 0; i < len(di.info.DynamicNodes); i++ {
|
|
|
|
// all []dinfo share *build.DriverInfo and *driver.Info
|
|
|
|
diClone := di
|
|
|
|
if pl := di.info.DynamicNodes[i].Platforms; len(pl) > 0 {
|
|
|
|
diClone.platforms = pl
|
|
|
|
}
|
|
|
|
drivers = append(drivers, di)
|
2019-11-12 18:10:39 +08:00
|
|
|
}
|
2020-12-15 16:02:04 +08:00
|
|
|
dynamicNodes = append(dynamicNodes, di.info.DynamicNodes...)
|
2019-11-12 18:10:39 +08:00
|
|
|
}
|
|
|
|
}
|
2020-12-15 16:02:04 +08:00
|
|
|
|
|
|
|
// not append (remove the static nodes in the store)
|
|
|
|
ngi.ng.Nodes = dynamicNodes
|
|
|
|
ngi.drivers = drivers
|
|
|
|
ngi.ng.Dynamic = true
|
2019-11-12 18:10:39 +08:00
|
|
|
}
|
2020-12-15 16:02:04 +08:00
|
|
|
|
2019-11-12 18:10:39 +08:00
|
|
|
return nil
|
2019-04-16 01:21:09 +08:00
|
|
|
}
|
2019-04-17 08:11:43 +08:00
|
|
|
|
2022-05-15 14:30:31 +08:00
|
|
|
func hasNodeGroup(list []*nginfo, ngi *nginfo) bool {
|
|
|
|
for _, l := range list {
|
|
|
|
if ngi.ng.Name == l.ng.Name {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-04-17 08:11:43 +08:00
|
|
|
func dockerAPI(dockerCli command.Cli) *api {
|
|
|
|
return &api{dockerCli: dockerCli}
|
|
|
|
}
|
|
|
|
|
|
|
|
type api struct {
|
|
|
|
dockerCli command.Cli
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *api) DockerAPI(name string) (dockerclient.APIClient, error) {
|
|
|
|
if name == "" {
|
|
|
|
name = a.dockerCli.CurrentContext()
|
|
|
|
}
|
|
|
|
return clientForEndpoint(a.dockerCli, name)
|
|
|
|
}
|
2021-08-12 14:45:40 +08:00
|
|
|
|
|
|
|
type dinfo struct {
|
|
|
|
di *build.DriverInfo
|
|
|
|
info *driver.Info
|
|
|
|
platforms []specs.Platform
|
2022-04-09 07:22:33 +08:00
|
|
|
version string
|
2021-08-12 14:45:40 +08:00
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
type nginfo struct {
|
|
|
|
ng *store.NodeGroup
|
|
|
|
drivers []dinfo
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
2022-02-03 20:39:44 +08:00
|
|
|
// inactive checks if all nodes are inactive for this builder
|
|
|
|
func (n *nginfo) inactive() bool {
|
|
|
|
for idx := range n.ng.Nodes {
|
|
|
|
d := n.drivers[idx]
|
|
|
|
if d.info != nil && d.info.Status == driver.Running {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-08-12 14:45:40 +08:00
|
|
|
func boot(ctx context.Context, ngi *nginfo) (bool, error) {
|
|
|
|
toBoot := make([]int, 0, len(ngi.drivers))
|
|
|
|
for i, d := range ngi.drivers {
|
|
|
|
if d.err != nil || d.di.Err != nil || d.di.Driver == nil || d.info == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if d.info.Status != driver.Running {
|
|
|
|
toBoot = append(toBoot, i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(toBoot) == 0 {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2022-10-25 17:55:36 +08:00
|
|
|
printer, err := progress.NewPrinter(context.TODO(), os.Stderr, os.Stderr, progress.PrinterModeAuto)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2021-08-12 14:45:40 +08:00
|
|
|
|
2021-08-20 11:36:24 +08:00
|
|
|
baseCtx := ctx
|
2021-08-12 14:45:40 +08:00
|
|
|
eg, _ := errgroup.WithContext(ctx)
|
|
|
|
for _, idx := range toBoot {
|
|
|
|
func(idx int) {
|
|
|
|
eg.Go(func() error {
|
|
|
|
pw := progress.WithPrefix(printer, ngi.ng.Nodes[idx].Name, len(toBoot) > 1)
|
2021-08-20 11:36:24 +08:00
|
|
|
_, err := driver.Boot(ctx, baseCtx, ngi.drivers[idx].di.Driver, pw)
|
2021-08-12 14:45:40 +08:00
|
|
|
if err != nil {
|
|
|
|
ngi.drivers[idx].err = err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}(idx)
|
|
|
|
}
|
|
|
|
|
2022-10-25 17:55:36 +08:00
|
|
|
err = eg.Wait()
|
2021-08-12 14:45:40 +08:00
|
|
|
err1 := printer.Wait()
|
|
|
|
if err == nil {
|
|
|
|
err = err1
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, err
|
|
|
|
}
|