github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/pluginmanager/csimanager/fingerprint.go (about)

     1  package csimanager
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/hashicorp/go-hclog"
     8  	"github.com/hashicorp/nomad/client/dynamicplugins"
     9  	"github.com/hashicorp/nomad/helper"
    10  	"github.com/hashicorp/nomad/nomad/structs"
    11  	"github.com/hashicorp/nomad/plugins/csi"
    12  )
    13  
    14  type pluginFingerprinter struct {
    15  	logger hclog.Logger
    16  	client csi.CSIPlugin
    17  	info   *dynamicplugins.PluginInfo
    18  
    19  	// basicInfo holds a cache of data that should not change within a CSI plugin.
    20  	// This allows us to minimize the number of requests we make to plugins on each
    21  	// run of the fingerprinter, and reduces the chances of performing overly
    22  	// expensive actions repeatedly, and improves stability of data through
    23  	// transient failures.
    24  	basicInfo *structs.CSIInfo
    25  
    26  	fingerprintNode       bool
    27  	fingerprintController bool
    28  
    29  	hadFirstSuccessfulFingerprint bool
    30  	// hadFirstSuccessfulFingerprintCh is closed the first time a fingerprint
    31  	// is completed successfully.
    32  	hadFirstSuccessfulFingerprintCh chan struct{}
    33  
    34  	// requiresStaging is set on a first successful fingerprint. It allows the
    35  	// csimanager to efficiently query this as it shouldn't change after a plugin
    36  	// is started. Removing this bool will require storing a cache of recent successful
    37  	// results that can be used by subscribers of the `hadFirstSuccessfulFingerprintCh`.
    38  	requiresStaging bool
    39  }
    40  
    41  func (p *pluginFingerprinter) fingerprint(ctx context.Context) *structs.CSIInfo {
    42  	if p.basicInfo == nil {
    43  		info, err := p.buildBasicFingerprint(ctx)
    44  		if err != nil {
    45  			// If we receive a fingerprinting error, update the stats with as much
    46  			// info as possible and wait for the next fingerprint interval.
    47  			info.HealthDescription = fmt.Sprintf("failed initial fingerprint with err: %v", err)
    48  			info.Healthy = false
    49  
    50  			return info
    51  		}
    52  
    53  		// If fingerprinting succeeded, we don't need to repopulate the basic
    54  		// info again.
    55  		p.basicInfo = info
    56  	}
    57  
    58  	info := p.basicInfo.Copy()
    59  	var fp *structs.CSIInfo
    60  	var err error
    61  
    62  	if p.fingerprintNode {
    63  		fp, err = p.buildNodeFingerprint(ctx, info)
    64  	} else if p.fingerprintController {
    65  		fp, err = p.buildControllerFingerprint(ctx, info)
    66  	}
    67  
    68  	if err != nil {
    69  		info.Healthy = false
    70  		info.HealthDescription = fmt.Sprintf("failed fingerprinting with error: %v", err)
    71  	} else {
    72  		info = fp
    73  		if !p.hadFirstSuccessfulFingerprint {
    74  			p.hadFirstSuccessfulFingerprint = true
    75  			if p.fingerprintNode {
    76  				p.requiresStaging = info.NodeInfo.RequiresNodeStageVolume
    77  			}
    78  			close(p.hadFirstSuccessfulFingerprintCh)
    79  		}
    80  	}
    81  
    82  	return info
    83  }
    84  
    85  func (p *pluginFingerprinter) buildBasicFingerprint(ctx context.Context) (*structs.CSIInfo, error) {
    86  	info := &structs.CSIInfo{
    87  		PluginID:          p.info.Name,
    88  		AllocID:           p.info.AllocID,
    89  		Provider:          p.info.Options["Provider"],
    90  		ProviderVersion:   p.info.Version,
    91  		Healthy:           false,
    92  		HealthDescription: "initial fingerprint not completed",
    93  	}
    94  
    95  	if p.fingerprintNode {
    96  		info.NodeInfo = &structs.CSINodeInfo{}
    97  	}
    98  	if p.fingerprintController {
    99  		info.ControllerInfo = &structs.CSIControllerInfo{}
   100  	}
   101  
   102  	capabilities, err := p.client.PluginGetCapabilities(ctx)
   103  	if err != nil {
   104  		return info, err
   105  	}
   106  
   107  	info.RequiresControllerPlugin = capabilities.HasControllerService()
   108  	info.RequiresTopologies = capabilities.HasToplogies()
   109  
   110  	if p.fingerprintNode {
   111  		nodeInfo, err := p.client.NodeGetInfo(ctx)
   112  		if err != nil {
   113  			return info, err
   114  		}
   115  
   116  		info.NodeInfo.ID = nodeInfo.NodeID
   117  		info.NodeInfo.MaxVolumes = nodeInfo.MaxVolumes
   118  		info.NodeInfo.AccessibleTopology = structCSITopologyFromCSITopology(nodeInfo.AccessibleTopology)
   119  	}
   120  
   121  	return info, nil
   122  }
   123  
   124  func applyCapabilitySetToControllerInfo(cs *csi.ControllerCapabilitySet, info *structs.CSIControllerInfo) {
   125  	info.SupportsReadOnlyAttach = cs.HasPublishReadonly
   126  	info.SupportsAttachDetach = cs.HasPublishUnpublishVolume
   127  	info.SupportsListVolumes = cs.HasListVolumes
   128  	info.SupportsListVolumesAttachedNodes = cs.HasListVolumesPublishedNodes
   129  }
   130  
   131  func (p *pluginFingerprinter) buildControllerFingerprint(ctx context.Context, base *structs.CSIInfo) (*structs.CSIInfo, error) {
   132  	fp := base.Copy()
   133  
   134  	healthy, err := p.client.PluginProbe(ctx)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	fp.SetHealthy(healthy)
   139  
   140  	caps, err := p.client.ControllerGetCapabilities(ctx)
   141  	if err != nil {
   142  		return fp, err
   143  	}
   144  	applyCapabilitySetToControllerInfo(caps, fp.ControllerInfo)
   145  
   146  	return fp, nil
   147  }
   148  
   149  func (p *pluginFingerprinter) buildNodeFingerprint(ctx context.Context, base *structs.CSIInfo) (*structs.CSIInfo, error) {
   150  	fp := base.Copy()
   151  
   152  	healthy, err := p.client.PluginProbe(ctx)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	fp.SetHealthy(healthy)
   157  
   158  	caps, err := p.client.NodeGetCapabilities(ctx)
   159  	if err != nil {
   160  		return fp, err
   161  	}
   162  	fp.NodeInfo.RequiresNodeStageVolume = caps.HasStageUnstageVolume
   163  
   164  	return fp, nil
   165  }
   166  
   167  func structCSITopologyFromCSITopology(a *csi.Topology) *structs.CSITopology {
   168  	if a == nil {
   169  		return nil
   170  	}
   171  
   172  	return &structs.CSITopology{
   173  		Segments: helper.CopyMapStringString(a.Segments),
   174  	}
   175  }