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 }