github.com/hernad/nomad@v1.6.112/nomad/drainer/draining_node.go (about)

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