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 }