github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/nomad/drainer/draining_node.go (about)

     1  package drainer
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/hashicorp/nomad/nomad/state"
     9  	"github.com/hashicorp/nomad/nomad/structs"
    10  )
    11  
    12  type drainingNode struct {
    13  	state *state.StateStore
    14  	node  *structs.Node
    15  	l     sync.RWMutex
    16  }
    17  
    18  func NewDrainingNode(node *structs.Node, state *state.StateStore) *drainingNode {
    19  	return &drainingNode{
    20  		state: state,
    21  		node:  node,
    22  	}
    23  }
    24  
    25  func (n *drainingNode) GetNode() *structs.Node {
    26  	n.l.Lock()
    27  	defer n.l.Unlock()
    28  	return n.node
    29  }
    30  
    31  func (n *drainingNode) Update(node *structs.Node) {
    32  	n.l.Lock()
    33  	defer n.l.Unlock()
    34  	n.node = node
    35  }
    36  
    37  // DeadlineTime returns if the node has a deadline and if so what it is
    38  func (n *drainingNode) DeadlineTime() (bool, time.Time) {
    39  	n.l.RLock()
    40  	defer n.l.RUnlock()
    41  
    42  	// Should never happen
    43  	if n.node == nil || n.node.DrainStrategy == nil {
    44  		return false, time.Time{}
    45  	}
    46  
    47  	return n.node.DrainStrategy.DeadlineTime()
    48  }
    49  
    50  // IsDone returns if the node is done draining batch and service allocs. System
    51  // allocs must be stopped before marking drain complete unless they're being
    52  // ignored.
    53  func (n *drainingNode) IsDone() (bool, error) {
    54  	n.l.RLock()
    55  	defer n.l.RUnlock()
    56  
    57  	// Should never happen
    58  	if n.node == nil || n.node.DrainStrategy == nil {
    59  		return false, fmt.Errorf("node doesn't have a drain strategy set")
    60  	}
    61  
    62  	// Retrieve the allocs on the node
    63  	allocs, err := n.state.AllocsByNode(nil, n.node.ID)
    64  	if err != nil {
    65  		return false, err
    66  	}
    67  
    68  	for _, alloc := range allocs {
    69  		// System jobs are only stopped after a node is done draining
    70  		// everything else, so ignore them here.
    71  		if alloc.Job.Type == structs.JobTypeSystem {
    72  			continue
    73  		}
    74  
    75  		// If there is a non-terminal we aren't done
    76  		if !alloc.TerminalStatus() {
    77  			return false, nil
    78  		}
    79  	}
    80  
    81  	return true, nil
    82  }
    83  
    84  // RemainingAllocs returns the set of allocations remaining on a node that
    85  // still need to be drained.
    86  func (n *drainingNode) RemainingAllocs() ([]*structs.Allocation, error) {
    87  	n.l.RLock()
    88  	defer n.l.RUnlock()
    89  
    90  	// Should never happen
    91  	if n.node == nil || n.node.DrainStrategy == nil {
    92  		return nil, fmt.Errorf("node doesn't have a drain strategy set")
    93  	}
    94  
    95  	// Grab the relevant drain info
    96  	ignoreSystem := n.node.DrainStrategy.IgnoreSystemJobs
    97  
    98  	// Retrieve the allocs on the node
    99  	allocs, err := n.state.AllocsByNode(nil, n.node.ID)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	var drain []*structs.Allocation
   105  	for _, alloc := range allocs {
   106  		// Nothing to do on a terminal allocation
   107  		if alloc.TerminalStatus() {
   108  			continue
   109  		}
   110  
   111  		// Skip system if configured to
   112  		if alloc.Job.Type == structs.JobTypeSystem && ignoreSystem {
   113  			continue
   114  		}
   115  
   116  		drain = append(drain, alloc)
   117  	}
   118  
   119  	return drain, nil
   120  }
   121  
   122  // DrainingJobs returns the set of jobs on the node that can block a drain.
   123  // These include batch and service jobs.
   124  func (n *drainingNode) DrainingJobs() ([]structs.NamespacedID, error) {
   125  	n.l.RLock()
   126  	defer n.l.RUnlock()
   127  
   128  	// Should never happen
   129  	if n.node == nil || n.node.DrainStrategy == nil {
   130  		return nil, fmt.Errorf("node doesn't have a drain strategy set")
   131  	}
   132  
   133  	// Retrieve the allocs on the node
   134  	allocs, err := n.state.AllocsByNode(nil, n.node.ID)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	jobIDs := make(map[structs.NamespacedID]struct{})
   140  	var jobs []structs.NamespacedID
   141  	for _, alloc := range allocs {
   142  		if alloc.TerminalStatus() || alloc.Job.Type == structs.JobTypeSystem {
   143  			continue
   144  		}
   145  
   146  		jns := structs.NamespacedID{Namespace: alloc.Namespace, ID: alloc.JobID}
   147  		if _, ok := jobIDs[jns]; ok {
   148  			continue
   149  		}
   150  		jobIDs[jns] = struct{}{}
   151  		jobs = append(jobs, jns)
   152  	}
   153  
   154  	return jobs, nil
   155  }