github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/allocator/allocator.go (about)

     1  package allocator
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	"github.com/docker/docker/pkg/plugingetter"
     8  	"github.com/docker/go-events"
     9  	"github.com/docker/swarmkit/api"
    10  	"github.com/docker/swarmkit/manager/allocator/cnmallocator"
    11  	"github.com/docker/swarmkit/manager/state"
    12  	"github.com/docker/swarmkit/manager/state/store"
    13  )
    14  
    15  // Allocator controls how the allocation stage in the manager is handled.
    16  type Allocator struct {
    17  	// The manager store.
    18  	store *store.MemoryStore
    19  
    20  	// the ballot used to synchronize across all allocators to ensure
    21  	// all of them have completed their respective allocations so that the
    22  	// task can be moved to ALLOCATED state.
    23  	taskBallot *taskBallot
    24  
    25  	// context for the network allocator that will be needed by
    26  	// network allocator.
    27  	netCtx *networkContext
    28  
    29  	// stopChan signals to the allocator to stop running.
    30  	stopChan chan struct{}
    31  	// doneChan is closed when the allocator is finished running.
    32  	doneChan chan struct{}
    33  
    34  	// pluginGetter provides access to docker's plugin inventory.
    35  	pluginGetter plugingetter.PluginGetter
    36  
    37  	// networkConfig stores network related config for the cluster
    38  	networkConfig *cnmallocator.NetworkConfig
    39  }
    40  
    41  // taskBallot controls how the voting for task allocation is
    42  // coordinated b/w different allocators. This the only structure that
    43  // will be written by all allocator goroutines concurrently. Hence the
    44  // mutex.
    45  type taskBallot struct {
    46  	sync.Mutex
    47  
    48  	// List of registered voters who have to cast their vote to
    49  	// indicate their allocation complete
    50  	voters []string
    51  
    52  	// List of votes collected for every task so far from different voters.
    53  	votes map[string][]string
    54  }
    55  
    56  // allocActor controls the various phases in the lifecycle of one kind of allocator.
    57  type allocActor struct {
    58  	// Task voter identity of the allocator.
    59  	taskVoter string
    60  
    61  	// Action routine which is called for every event that the
    62  	// allocator received.
    63  	action func(context.Context, events.Event)
    64  
    65  	// Init routine which is called during the initialization of
    66  	// the allocator.
    67  	init func(ctx context.Context) error
    68  }
    69  
    70  // New returns a new instance of Allocator for use during allocation
    71  // stage of the manager.
    72  func New(store *store.MemoryStore, pg plugingetter.PluginGetter, netConfig *cnmallocator.NetworkConfig) (*Allocator, error) {
    73  	a := &Allocator{
    74  		store: store,
    75  		taskBallot: &taskBallot{
    76  			votes: make(map[string][]string),
    77  		},
    78  		stopChan:      make(chan struct{}),
    79  		doneChan:      make(chan struct{}),
    80  		pluginGetter:  pg,
    81  		networkConfig: netConfig,
    82  	}
    83  
    84  	return a, nil
    85  }
    86  
    87  // Run starts all allocator go-routines and waits for Stop to be called.
    88  func (a *Allocator) Run(ctx context.Context) error {
    89  	// Setup cancel context for all goroutines to use.
    90  	ctx, cancel := context.WithCancel(ctx)
    91  	var (
    92  		wg     sync.WaitGroup
    93  		actors []func() error
    94  	)
    95  
    96  	defer func() {
    97  		cancel()
    98  		wg.Wait()
    99  		close(a.doneChan)
   100  	}()
   101  
   102  	for _, aa := range []allocActor{
   103  		{
   104  			taskVoter: networkVoter,
   105  			init:      a.doNetworkInit,
   106  			action:    a.doNetworkAlloc,
   107  		},
   108  	} {
   109  		if aa.taskVoter != "" {
   110  			a.registerToVote(aa.taskVoter)
   111  		}
   112  
   113  		// Assign a pointer for variable capture
   114  		aaPtr := &aa
   115  		actor := func() error {
   116  			wg.Add(1)
   117  			defer wg.Done()
   118  
   119  			// init might return an allocator specific context
   120  			// which is a child of the passed in context to hold
   121  			// allocator specific state
   122  			watch, watchCancel, err := a.init(ctx, aaPtr)
   123  			if err != nil {
   124  				return err
   125  			}
   126  
   127  			wg.Add(1)
   128  			go func(watch <-chan events.Event, watchCancel func()) {
   129  				defer func() {
   130  					wg.Done()
   131  					watchCancel()
   132  				}()
   133  				a.run(ctx, *aaPtr, watch)
   134  			}(watch, watchCancel)
   135  			return nil
   136  		}
   137  
   138  		actors = append(actors, actor)
   139  	}
   140  
   141  	for _, actor := range actors {
   142  		if err := actor(); err != nil {
   143  			return err
   144  		}
   145  	}
   146  
   147  	<-a.stopChan
   148  	return nil
   149  }
   150  
   151  // Stop stops the allocator
   152  func (a *Allocator) Stop() {
   153  	close(a.stopChan)
   154  	// Wait for all allocator goroutines to truly exit
   155  	<-a.doneChan
   156  }
   157  
   158  func (a *Allocator) init(ctx context.Context, aa *allocActor) (<-chan events.Event, func(), error) {
   159  	watch, watchCancel := state.Watch(a.store.WatchQueue(),
   160  		api.EventCreateNetwork{},
   161  		api.EventDeleteNetwork{},
   162  		api.EventCreateService{},
   163  		api.EventUpdateService{},
   164  		api.EventDeleteService{},
   165  		api.EventCreateTask{},
   166  		api.EventUpdateTask{},
   167  		api.EventDeleteTask{},
   168  		api.EventCreateNode{},
   169  		api.EventUpdateNode{},
   170  		api.EventDeleteNode{},
   171  		state.EventCommit{},
   172  	)
   173  
   174  	if err := aa.init(ctx); err != nil {
   175  		watchCancel()
   176  		return nil, nil, err
   177  	}
   178  
   179  	return watch, watchCancel, nil
   180  }
   181  
   182  func (a *Allocator) run(ctx context.Context, aa allocActor, watch <-chan events.Event) {
   183  	for {
   184  		select {
   185  		case ev, ok := <-watch:
   186  			if !ok {
   187  				return
   188  			}
   189  
   190  			aa.action(ctx, ev)
   191  		case <-ctx.Done():
   192  			return
   193  		}
   194  	}
   195  }
   196  
   197  func (a *Allocator) registerToVote(name string) {
   198  	a.taskBallot.Lock()
   199  	defer a.taskBallot.Unlock()
   200  
   201  	a.taskBallot.voters = append(a.taskBallot.voters, name)
   202  }
   203  
   204  func (a *Allocator) taskAllocateVote(voter string, id string) bool {
   205  	a.taskBallot.Lock()
   206  	defer a.taskBallot.Unlock()
   207  
   208  	// If voter has already voted, return false
   209  	for _, v := range a.taskBallot.votes[id] {
   210  		// check if voter is in x
   211  		if v == voter {
   212  			return false
   213  		}
   214  	}
   215  
   216  	a.taskBallot.votes[id] = append(a.taskBallot.votes[id], voter)
   217  
   218  	// We haven't gotten enough votes yet
   219  	if len(a.taskBallot.voters) > len(a.taskBallot.votes[id]) {
   220  		return false
   221  	}
   222  
   223  nextVoter:
   224  	for _, voter := range a.taskBallot.voters {
   225  		for _, vote := range a.taskBallot.votes[id] {
   226  			if voter == vote {
   227  				continue nextVoter
   228  			}
   229  		}
   230  
   231  		// Not every registered voter has registered a vote.
   232  		return false
   233  	}
   234  
   235  	return true
   236  }