mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	Merge pull request #1439 from crazy-max/last-activity
store: set nodegroup last activity
This commit is contained in:
		@@ -111,6 +111,9 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err = updateLastActivity(dockerCli, b.NodeGroup); err != nil {
 | 
			
		||||
			return errors.Wrapf(err, "failed to update builder last activity time")
 | 
			
		||||
		}
 | 
			
		||||
		nodes, err = b.LoadNodes(ctx, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ import (
 | 
			
		||||
	"github.com/docker/buildx/build"
 | 
			
		||||
	"github.com/docker/buildx/builder"
 | 
			
		||||
	"github.com/docker/buildx/monitor"
 | 
			
		||||
	"github.com/docker/buildx/store"
 | 
			
		||||
	"github.com/docker/buildx/store/storeutil"
 | 
			
		||||
	"github.com/docker/buildx/util/buildflags"
 | 
			
		||||
	"github.com/docker/buildx/util/confutil"
 | 
			
		||||
	"github.com/docker/buildx/util/dockerutil"
 | 
			
		||||
@@ -261,6 +263,9 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err = updateLastActivity(dockerCli, b.NodeGroup); err != nil {
 | 
			
		||||
		return errors.Wrapf(err, "failed to update builder last activity time")
 | 
			
		||||
	}
 | 
			
		||||
	nodes, err := b.LoadNodes(ctx, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -730,3 +735,12 @@ func isExperimental() bool {
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateLastActivity(dockerCli command.Cli, ng *store.NodeGroup) error {
 | 
			
		||||
	txn, release, err := storeutil.GetStore(dockerCli)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return txn.UpdateLastActivity(ng)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,9 @@ func runInspect(dockerCli command.Cli, in inspectOptions) error {
 | 
			
		||||
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
 | 
			
		||||
	fmt.Fprintf(w, "Name:\t%s\n", b.Name)
 | 
			
		||||
	fmt.Fprintf(w, "Driver:\t%s\n", b.Driver)
 | 
			
		||||
	if !b.NodeGroup.LastActivity.IsZero() {
 | 
			
		||||
		fmt.Fprintf(w, "Last Activity:\t%v\n", b.NodeGroup.LastActivity)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(w, "Error:\t%s\n", err.Error())
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package store
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/platforms"
 | 
			
		||||
	"github.com/docker/buildx/util/confutil"
 | 
			
		||||
@@ -19,6 +20,7 @@ type NodeGroup struct {
 | 
			
		||||
 | 
			
		||||
	// skip the following fields from being saved in the store
 | 
			
		||||
	DockerContext bool      `json:"-"`
 | 
			
		||||
	LastActivity  time.Time `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Node struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/pkg/ioutils"
 | 
			
		||||
	"github.com/gofrs/flock"
 | 
			
		||||
@@ -12,11 +13,20 @@ import (
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	instanceDir = "instances"
 | 
			
		||||
	defaultsDir = "defaults"
 | 
			
		||||
	activityDir = "activity"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func New(root string) (*Store, error) {
 | 
			
		||||
	if err := os.MkdirAll(filepath.Join(root, "instances"), 0700); err != nil {
 | 
			
		||||
	if err := os.MkdirAll(filepath.Join(root, instanceDir), 0700); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := os.MkdirAll(filepath.Join(root, "defaults"), 0700); err != nil {
 | 
			
		||||
	if err := os.MkdirAll(filepath.Join(root, defaultsDir), 0700); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := os.MkdirAll(filepath.Join(root, activityDir), 0700); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &Store{root: root}, nil
 | 
			
		||||
@@ -43,7 +53,7 @@ type Txn struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Txn) List() ([]*NodeGroup, error) {
 | 
			
		||||
	pp := filepath.Join(t.s.root, "instances")
 | 
			
		||||
	pp := filepath.Join(t.s.root, instanceDir)
 | 
			
		||||
	fis, err := os.ReadDir(pp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -73,7 +83,7 @@ func (t *Txn) NodeGroupByName(name string) (*NodeGroup, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	dt, err := os.ReadFile(filepath.Join(t.s.root, "instances", name))
 | 
			
		||||
	dt, err := os.ReadFile(filepath.Join(t.s.root, instanceDir, name))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -81,6 +91,9 @@ func (t *Txn) NodeGroupByName(name string) (*NodeGroup, error) {
 | 
			
		||||
	if err := json.Unmarshal(dt, &ng); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if ng.LastActivity, err = t.GetLastActivity(&ng); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &ng, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -89,11 +102,14 @@ func (t *Txn) Save(ng *NodeGroup) error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := t.UpdateLastActivity(ng); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	dt, err := json.Marshal(ng)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return ioutils.AtomicWriteFile(filepath.Join(t.s.root, "instances", name), dt, 0600)
 | 
			
		||||
	return ioutils.AtomicWriteFile(filepath.Join(t.s.root, instanceDir, name), dt, 0600)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Txn) Remove(name string) error {
 | 
			
		||||
@@ -101,7 +117,10 @@ func (t *Txn) Remove(name string) error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return os.RemoveAll(filepath.Join(t.s.root, "instances", name))
 | 
			
		||||
	if err := t.RemoveLastActivity(name); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return os.RemoveAll(filepath.Join(t.s.root, instanceDir, name))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Txn) SetCurrent(key, name string, global, def bool) error {
 | 
			
		||||
@@ -121,15 +140,38 @@ func (t *Txn) SetCurrent(key, name string, global, def bool) error {
 | 
			
		||||
	h := toHash(key)
 | 
			
		||||
 | 
			
		||||
	if def {
 | 
			
		||||
		if err := ioutils.AtomicWriteFile(filepath.Join(t.s.root, "defaults", h), []byte(name), 0600); err != nil {
 | 
			
		||||
		if err := ioutils.AtomicWriteFile(filepath.Join(t.s.root, defaultsDir, h), []byte(name), 0600); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		os.RemoveAll(filepath.Join(t.s.root, "defaults", h)) // ignore error
 | 
			
		||||
		os.RemoveAll(filepath.Join(t.s.root, defaultsDir, h)) // ignore error
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Txn) UpdateLastActivity(ng *NodeGroup) error {
 | 
			
		||||
	return ioutils.AtomicWriteFile(filepath.Join(t.s.root, activityDir, ng.Name), []byte(time.Now().UTC().Format(time.RFC3339)), 0600)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Txn) GetLastActivity(ng *NodeGroup) (la time.Time, _ error) {
 | 
			
		||||
	dt, err := os.ReadFile(filepath.Join(t.s.root, activityDir, ng.Name))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if os.IsNotExist(errors.Cause(err)) {
 | 
			
		||||
			return la, nil
 | 
			
		||||
		}
 | 
			
		||||
		return la, err
 | 
			
		||||
	}
 | 
			
		||||
	return time.Parse(time.RFC3339, string(dt))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Txn) RemoveLastActivity(name string) error {
 | 
			
		||||
	name, err := ValidateName(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return os.RemoveAll(filepath.Join(t.s.root, activityDir, name))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Txn) reset(key string) error {
 | 
			
		||||
	dt, err := json.Marshal(current{Key: key})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -173,7 +215,7 @@ func (t *Txn) Current(key string) (*NodeGroup, error) {
 | 
			
		||||
 | 
			
		||||
	h := toHash(key)
 | 
			
		||||
 | 
			
		||||
	dt, err = os.ReadFile(filepath.Join(t.s.root, "defaults", h))
 | 
			
		||||
	dt, err = os.ReadFile(filepath.Join(t.s.root, defaultsDir, h))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if os.IsNotExist(err) {
 | 
			
		||||
			t.reset(key)
 | 
			
		||||
 
 | 
			
		||||
@@ -92,6 +92,7 @@ func TestNodeManagement(t *testing.T) {
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	require.Equal(t, "mybuild", ng.Name)
 | 
			
		||||
	require.Equal(t, "mydriver", ng.Driver)
 | 
			
		||||
	require.True(t, !ng.LastActivity.IsZero())
 | 
			
		||||
 | 
			
		||||
	_, err = txn.NodeGroupByName("mybuild2")
 | 
			
		||||
	require.Error(t, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,7 @@ func GetNodeGroup(txn *store.Txn, dockerCli command.Cli, name string) (*store.No
 | 
			
		||||
	}
 | 
			
		||||
	for _, l := range list {
 | 
			
		||||
		if l.Name == name {
 | 
			
		||||
			return &store.NodeGroup{
 | 
			
		||||
			ng = &store.NodeGroup{
 | 
			
		||||
				Name: name,
 | 
			
		||||
				Nodes: []store.Node{
 | 
			
		||||
					{
 | 
			
		||||
@@ -93,8 +93,11 @@ func GetNodeGroup(txn *store.Txn, dockerCli command.Cli, name string) (*store.No
 | 
			
		||||
						Endpoint: name,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				DockerContext: true,
 | 
			
		||||
			}, nil
 | 
			
		||||
			}
 | 
			
		||||
			if ng.LastActivity, err = txn.GetLastActivity(ng); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return ng, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user