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  }