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  }