github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/client/node_updater.go (about) 1 package client 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/hashicorp/nomad/client/devicemanager" 10 "github.com/hashicorp/nomad/client/pluginmanager/drivermanager" 11 "github.com/hashicorp/nomad/nomad/structs" 12 ) 13 14 var ( 15 // batchFirstFingerprintsTimeout is the maximum amount of time to wait for 16 // initial fingerprinting to complete before sending a batched Node update 17 batchFirstFingerprintsTimeout = 50 * time.Second 18 ) 19 20 // batchFirstFingerprints waits for the first fingerprint response from all 21 // plugin managers and sends a single Node update for all fingerprints. It 22 // should only ever be called once 23 func (c *Client) batchFirstFingerprints() { 24 ctx, cancel := context.WithTimeout(context.Background(), batchFirstFingerprintsTimeout) 25 defer cancel() 26 27 ch, err := c.pluginManagers.WaitForFirstFingerprint(ctx) 28 if err != nil { 29 c.logger.Warn("failed to batch initial fingerprint updates, switching to incemental updates") 30 goto SEND_BATCH 31 } 32 33 // Wait for fingerprinting to complete or timeout before processing batches 34 select { 35 case <-ch: 36 case <-ctx.Done(): 37 } 38 39 SEND_BATCH: 40 c.configLock.Lock() 41 defer c.configLock.Unlock() 42 43 // driver node updates 44 var driverChanged bool 45 c.batchNodeUpdates.batchDriverUpdates(func(driver string, info *structs.DriverInfo) { 46 if c.updateNodeFromDriverLocked(driver, info) { 47 c.config.Node.Drivers[driver] = info 48 if c.config.Node.Drivers[driver].UpdateTime.IsZero() { 49 c.config.Node.Drivers[driver].UpdateTime = time.Now() 50 } 51 driverChanged = true 52 } 53 }) 54 55 // device node updates 56 var devicesChanged bool 57 c.batchNodeUpdates.batchDevicesUpdates(func(devices []*structs.NodeDeviceResource) { 58 if c.updateNodeFromDevicesLocked(devices) { 59 devicesChanged = true 60 } 61 }) 62 63 // only update the node if changes occurred 64 if driverChanged || devicesChanged { 65 c.updateNodeLocked() 66 } 67 68 close(c.fpInitialized) 69 } 70 71 // updateNodeFromDriver receives a DriverInfo struct for the driver and updates 72 // the node accordingly 73 func (c *Client) updateNodeFromDriver(name string, info *structs.DriverInfo) { 74 c.configLock.Lock() 75 defer c.configLock.Unlock() 76 77 if c.updateNodeFromDriverLocked(name, info) { 78 c.config.Node.Drivers[name] = info 79 if c.config.Node.Drivers[name].UpdateTime.IsZero() { 80 c.config.Node.Drivers[name].UpdateTime = time.Now() 81 } 82 c.updateNodeLocked() 83 } 84 } 85 86 // updateNodeFromDriverLocked makes the changes to the node from a driver update 87 // but does not send the update to the server. c.configLock must be held before 88 // calling this func 89 func (c *Client) updateNodeFromDriverLocked(name string, info *structs.DriverInfo) bool { 90 var hasChanged bool 91 92 hadDriver := c.config.Node.Drivers[name] != nil 93 if !hadDriver { 94 // If the driver info has not yet been set, do that here 95 hasChanged = true 96 for attrName, newVal := range info.Attributes { 97 c.config.Node.Attributes[attrName] = newVal 98 } 99 } else { 100 oldVal := c.config.Node.Drivers[name] 101 // The driver info has already been set, fix it up 102 if oldVal.Detected != info.Detected { 103 hasChanged = true 104 } 105 106 // If health state has change, trigger node event 107 if oldVal.Healthy != info.Healthy || oldVal.HealthDescription != info.HealthDescription { 108 hasChanged = true 109 if info.HealthDescription != "" { 110 event := &structs.NodeEvent{ 111 Subsystem: "Driver", 112 Message: info.HealthDescription, 113 Timestamp: time.Now(), 114 Details: map[string]string{"driver": name}, 115 } 116 c.triggerNodeEvent(event) 117 } 118 } 119 120 for attrName, newVal := range info.Attributes { 121 oldVal := c.config.Node.Drivers[name].Attributes[attrName] 122 if oldVal == newVal { 123 continue 124 } 125 126 hasChanged = true 127 if newVal == "" { 128 delete(c.config.Node.Attributes, attrName) 129 } else { 130 c.config.Node.Attributes[attrName] = newVal 131 } 132 } 133 } 134 135 // COMPAT Remove in Nomad 0.10 136 // We maintain the driver enabled attribute until all drivers expose 137 // their attributes as DriverInfo 138 driverName := fmt.Sprintf("driver.%s", name) 139 if info.Detected { 140 c.config.Node.Attributes[driverName] = "1" 141 } else { 142 delete(c.config.Node.Attributes, driverName) 143 } 144 145 return hasChanged 146 } 147 148 // updateNodeFromFingerprint updates the node with the result of 149 // fingerprinting the node from the diff that was created 150 func (c *Client) updateNodeFromDevices(devices []*structs.NodeDeviceResource) { 151 c.configLock.Lock() 152 defer c.configLock.Unlock() 153 154 // Not updating node.Resources: the field is deprecated and includes 155 // dispatched task resources and not appropriate for expressing 156 // node available device resources 157 if c.updateNodeFromDevicesLocked(devices) { 158 c.updateNodeLocked() 159 } 160 } 161 162 // updateNodeFromDevicesLocked updates the node with the results of devices, 163 // but does send the update to the server. c.configLock must be held before 164 // calling this func 165 func (c *Client) updateNodeFromDevicesLocked(devices []*structs.NodeDeviceResource) bool { 166 if !structs.DevicesEquals(c.config.Node.NodeResources.Devices, devices) { 167 c.logger.Debug("new devices detected", "devices", len(devices)) 168 c.config.Node.NodeResources.Devices = devices 169 return true 170 } 171 172 return false 173 } 174 175 // batchNodeUpdates allows for batching multiple Node updates from fingerprinting. 176 // Once ready, the batches can be flushed and toggled to stop batching and forward 177 // all updates to a configured callback to be performed incrementally 178 type batchNodeUpdates struct { 179 // access to driver fields must hold driversMu lock 180 drivers map[string]*structs.DriverInfo 181 driversBatched bool 182 driverCB drivermanager.UpdateNodeDriverInfoFn 183 driversMu sync.Mutex 184 185 // access to devices fields must hold devicesMu lock 186 devices []*structs.NodeDeviceResource 187 devicesBatched bool 188 devicesCB devicemanager.UpdateNodeDevicesFn 189 devicesMu sync.Mutex 190 } 191 192 func newBatchNodeUpdates( 193 driverCB drivermanager.UpdateNodeDriverInfoFn, 194 devicesCB devicemanager.UpdateNodeDevicesFn) *batchNodeUpdates { 195 196 return &batchNodeUpdates{ 197 drivers: make(map[string]*structs.DriverInfo), 198 driverCB: driverCB, 199 devices: []*structs.NodeDeviceResource{}, 200 devicesCB: devicesCB, 201 } 202 } 203 204 // updateNodeFromDriver implements drivermanager.UpdateNodeDriverInfoFn and is 205 // used in the driver manager to send driver fingerprints to 206 func (b *batchNodeUpdates) updateNodeFromDriver(driver string, info *structs.DriverInfo) { 207 b.driversMu.Lock() 208 defer b.driversMu.Unlock() 209 if b.driversBatched { 210 b.driverCB(driver, info) 211 return 212 } 213 214 b.drivers[driver] = info 215 } 216 217 // batchDriverUpdates sends all of the batched driver node updates by calling f 218 // for each driver batched 219 func (b *batchNodeUpdates) batchDriverUpdates(f drivermanager.UpdateNodeDriverInfoFn) error { 220 b.driversMu.Lock() 221 defer b.driversMu.Unlock() 222 if b.driversBatched { 223 return fmt.Errorf("driver updates already batched") 224 } 225 226 b.driversBatched = true 227 for driver, info := range b.drivers { 228 f(driver, info) 229 } 230 return nil 231 } 232 233 // updateNodeFromDevices implements devicemanager.UpdateNodeDevicesFn and is 234 // used in the device manager to send device fingerprints to 235 func (b *batchNodeUpdates) updateNodeFromDevices(devices []*structs.NodeDeviceResource) { 236 b.devicesMu.Lock() 237 defer b.devicesMu.Unlock() 238 if b.devicesBatched { 239 b.devicesCB(devices) 240 return 241 } 242 243 b.devices = devices 244 } 245 246 // batchDevicesUpdates sends the batched device node updates by calling f with 247 // the devices 248 func (b *batchNodeUpdates) batchDevicesUpdates(f devicemanager.UpdateNodeDevicesFn) error { 249 b.devicesMu.Lock() 250 defer b.devicesMu.Unlock() 251 if b.devicesBatched { 252 return fmt.Errorf("devices updates already batched") 253 } 254 255 b.devicesBatched = true 256 f(b.devices) 257 return nil 258 }