github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/pluginmanager/csimanager/instance.go (about) 1 package csimanager 2 3 import ( 4 "context" 5 "time" 6 7 "github.com/hashicorp/go-hclog" 8 "github.com/hashicorp/nomad/client/dynamicplugins" 9 "github.com/hashicorp/nomad/plugins/csi" 10 ) 11 12 const managerFingerprintInterval = 30 * time.Second 13 14 // instanceManager is used to manage the fingerprinting and supervision of a 15 // single CSI Plugin. 16 type instanceManager struct { 17 info *dynamicplugins.PluginInfo 18 logger hclog.Logger 19 eventer TriggerNodeEvent 20 21 updater UpdateNodeCSIInfoFunc 22 23 shutdownCtx context.Context 24 shutdownCtxCancelFn context.CancelFunc 25 shutdownCh chan struct{} 26 27 // mountPoint is the root of the mount dir where plugin specific data may be 28 // stored and where mount points will be created 29 mountPoint string 30 31 // containerMountPoint is the location _inside_ the plugin container that the 32 // `mountPoint` is bound in to. 33 containerMountPoint string 34 35 // AllocID is the allocation id of the task group running the dynamic plugin 36 allocID string 37 38 fp *pluginFingerprinter 39 40 volumeManager *volumeManager 41 volumeManagerSetupCh chan struct{} 42 43 client csi.CSIPlugin 44 } 45 46 func newInstanceManager(logger hclog.Logger, eventer TriggerNodeEvent, updater UpdateNodeCSIInfoFunc, p *dynamicplugins.PluginInfo) *instanceManager { 47 ctx, cancelFn := context.WithCancel(context.Background()) 48 logger = logger.Named(p.Name) 49 return &instanceManager{ 50 logger: logger, 51 eventer: eventer, 52 info: p, 53 updater: updater, 54 55 fp: &pluginFingerprinter{ 56 logger: logger.Named("fingerprinter"), 57 info: p, 58 fingerprintNode: p.Type == dynamicplugins.PluginTypeCSINode, 59 fingerprintController: p.Type == dynamicplugins.PluginTypeCSIController, 60 hadFirstSuccessfulFingerprintCh: make(chan struct{}), 61 }, 62 63 mountPoint: p.Options["MountPoint"], 64 containerMountPoint: p.Options["ContainerMountPoint"], 65 allocID: p.AllocID, 66 67 volumeManagerSetupCh: make(chan struct{}), 68 69 shutdownCtx: ctx, 70 shutdownCtxCancelFn: cancelFn, 71 shutdownCh: make(chan struct{}), 72 } 73 } 74 75 func (i *instanceManager) run() { 76 c := csi.NewClient(i.info.ConnectionInfo.SocketPath, i.logger) 77 i.client = c 78 i.fp.client = c 79 80 go i.setupVolumeManager() 81 go i.runLoop() 82 } 83 84 func (i *instanceManager) setupVolumeManager() { 85 if i.info.Type != dynamicplugins.PluginTypeCSINode { 86 i.logger.Debug("not a node plugin, skipping volume manager setup", "type", i.info.Type) 87 return 88 } 89 90 select { 91 case <-i.shutdownCtx.Done(): 92 return 93 case <-i.fp.hadFirstSuccessfulFingerprintCh: 94 i.volumeManager = newVolumeManager(i.logger, i.eventer, i.client, i.mountPoint, i.containerMountPoint, i.fp.requiresStaging) 95 i.logger.Debug("volume manager setup complete") 96 close(i.volumeManagerSetupCh) 97 return 98 } 99 } 100 101 // VolumeMounter returns the volume manager that is configured for the given plugin 102 // instance. If called before the volume manager has been setup, it will block until 103 // the volume manager is ready or the context is closed. 104 func (i *instanceManager) VolumeMounter(ctx context.Context) (VolumeMounter, error) { 105 select { 106 case <-i.volumeManagerSetupCh: 107 return i.volumeManager, nil 108 case <-ctx.Done(): 109 return nil, ctx.Err() 110 } 111 } 112 113 func (i *instanceManager) requestCtxWithTimeout(timeout time.Duration) (context.Context, context.CancelFunc) { 114 return context.WithTimeout(i.shutdownCtx, timeout) 115 } 116 117 func (i *instanceManager) runLoop() { 118 timer := time.NewTimer(0) 119 for { 120 select { 121 case <-i.shutdownCtx.Done(): 122 if i.client != nil { 123 i.client.Close() 124 i.client = nil 125 } 126 127 // run one last fingerprint so that we mark the plugin as unhealthy. 128 // the client has been closed so this will return quickly with the 129 // plugin's basic info 130 ctx, cancelFn := i.requestCtxWithTimeout(time.Second) 131 info := i.fp.fingerprint(ctx) 132 cancelFn() 133 if info != nil { 134 i.updater(i.info.Name, info) 135 } 136 close(i.shutdownCh) 137 return 138 139 case <-timer.C: 140 ctx, cancelFn := i.requestCtxWithTimeout(managerFingerprintInterval) 141 info := i.fp.fingerprint(ctx) 142 cancelFn() 143 if info != nil { 144 i.updater(i.info.Name, info) 145 } 146 timer.Reset(managerFingerprintInterval) 147 } 148 } 149 } 150 151 func (i *instanceManager) shutdown() { 152 i.shutdownCtxCancelFn() 153 <-i.shutdownCh 154 }