buildx: rollback configuration if create fails

This builds on the added warnings from initialized builders, now
erroring the command, and additionally attempting to revert to the
previous configuration.

To preserve the previous configuration, we add a deep Copy() function to
the NodeGroup and Node so that we can easily restore it later if we
encounter a failure.

Signed-off-by: Justin Chadwell <me@jedevc.com>
This commit is contained in:
Justin Chadwell 2022-07-27 11:48:52 +01:00
parent 309c49413c
commit 86825a95ce
2 changed files with 64 additions and 11 deletions

View File

@ -134,6 +134,11 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
}
}
ngOriginal := ng
if ngOriginal != nil {
ngOriginal = ngOriginal.Copy()
}
if ng == nil {
ng = &store.NodeGroup{
Name: name,
@ -224,16 +229,6 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
return err
}
if in.use && ep != "" {
current, err := storeutil.GetCurrentEndpoint(dockerCli)
if err != nil {
return err
}
if err := txn.SetCurrent(current, ng.Name, false, false); err != nil {
return err
}
}
ngi := &nginfo{ng: ng}
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
@ -244,7 +239,27 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
}
for _, info := range ngi.drivers {
if err := info.di.Err; err != nil {
logrus.Errorf("failed to initialize builder %s (%s): %s", ng.Name, info.di.Name, err)
err := errors.Errorf("failed to initialize builder %s (%s): %s", ng.Name, info.di.Name, err)
var err2 error
if ngOriginal == nil {
err2 = txn.Remove(ng.Name)
} else {
err2 = txn.Save(ngOriginal)
}
if err2 != nil {
logrus.Warnf("Could not rollback to previous state: %s", err2)
}
return err
}
}
if in.use && ep != "" {
current, err := storeutil.GetCurrentEndpoint(dockerCli)
if err != nil {
return err
}
if err := txn.SetCurrent(current, ng.Name, false, false); err != nil {
return err
}
}

View File

@ -110,6 +110,44 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints
return nil
}
func (ng *NodeGroup) Copy() *NodeGroup {
nodes := make([]Node, len(ng.Nodes))
for i, node := range ng.Nodes {
nodes[i] = *node.Copy()
}
return &NodeGroup{
Name: ng.Name,
Driver: ng.Driver,
Nodes: nodes,
Dynamic: ng.Dynamic,
}
}
func (n *Node) Copy() *Node {
platforms := []specs.Platform{}
copy(platforms, n.Platforms)
flags := []string{}
copy(flags, n.Flags)
driverOpts := map[string]string{}
for k, v := range n.DriverOpts {
driverOpts[k] = v
}
files := map[string][]byte{}
for k, v := range n.Files {
vv := []byte{}
copy(vv, v)
files[k] = vv
}
return &Node{
Name: n.Name,
Endpoint: n.Endpoint,
Platforms: platforms,
Flags: flags,
DriverOpts: driverOpts,
Files: files,
}
}
func (ng *NodeGroup) validateDuplicates(ep string, idx int) error {
i := 0
for _, n := range ng.Nodes {