github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/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, err := csi.NewClient(i.info.ConnectionInfo.SocketPath, i.logger) 77 if err != nil { 78 i.logger.Error("failed to setup instance manager client", "error", err) 79 close(i.shutdownCh) 80 return 81 } 82 i.client = c 83 i.fp.client = c 84 85 go i.setupVolumeManager() 86 go i.runLoop() 87 } 88 89 func (i *instanceManager) setupVolumeManager() { 90 if i.info.Type != dynamicplugins.PluginTypeCSINode { 91 i.logger.Debug("not a node plugin, skipping volume manager setup", "type", i.info.Type) 92 return 93 } 94 95 select { 96 case <-i.shutdownCtx.Done(): 97 return 98 case <-i.fp.hadFirstSuccessfulFingerprintCh: 99 i.volumeManager = newVolumeManager(i.logger, i.eventer, i.client, i.mountPoint, i.containerMountPoint, i.fp.requiresStaging) 100 i.logger.Debug("volume manager setup complete") 101 close(i.volumeManagerSetupCh) 102 return 103 } 104 } 105 106 // VolumeMounter returns the volume manager that is configured for the given plugin 107 // instance. If called before the volume manager has been setup, it will block until 108 // the volume manager is ready or the context is closed. 109 func (i *instanceManager) VolumeMounter(ctx context.Context) (VolumeMounter, error) { 110 select { 111 case <-i.volumeManagerSetupCh: 112 return i.volumeManager, nil 113 case <-ctx.Done(): 114 return nil, ctx.Err() 115 } 116 } 117 118 func (i *instanceManager) requestCtxWithTimeout(timeout time.Duration) (context.Context, context.CancelFunc) { 119 return context.WithTimeout(i.shutdownCtx, timeout) 120 } 121 122 func (i *instanceManager) runLoop() { 123 timer := time.NewTimer(0) 124 for { 125 select { 126 case <-i.shutdownCtx.Done(): 127 if i.client != nil { 128 i.client.Close() 129 i.client = nil 130 } 131 132 // run one last fingerprint so that we mark the plugin as unhealthy. 133 // the client has been closed so this will return quickly with the 134 // plugin's basic info 135 ctx, cancelFn := i.requestCtxWithTimeout(time.Second) 136 info := i.fp.fingerprint(ctx) 137 cancelFn() 138 if info != nil { 139 i.updater(i.info.Name, info) 140 } 141 close(i.shutdownCh) 142 return 143 144 case <-timer.C: 145 ctx, cancelFn := i.requestCtxWithTimeout(managerFingerprintInterval) 146 info := i.fp.fingerprint(ctx) 147 cancelFn() 148 if info != nil { 149 i.updater(i.info.Name, info) 150 } 151 timer.Reset(managerFingerprintInterval) 152 } 153 } 154 } 155 156 func (i *instanceManager) shutdown() { 157 i.shutdownCtxCancelFn() 158 <-i.shutdownCh 159 }