github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/allocrunner/csi_hook.go (about) 1 package allocrunner 2 3 import ( 4 "context" 5 "fmt" 6 7 hclog "github.com/hashicorp/go-hclog" 8 "github.com/hashicorp/nomad/client/pluginmanager/csimanager" 9 "github.com/hashicorp/nomad/nomad/structs" 10 ) 11 12 // csiHook will wait for remote csi volumes to be attached to the host before 13 // continuing. 14 // 15 // It is a noop for allocs that do not depend on CSI Volumes. 16 type csiHook struct { 17 alloc *structs.Allocation 18 logger hclog.Logger 19 csimanager csimanager.Manager 20 rpcClient RPCer 21 updater hookResourceSetter 22 } 23 24 func (c *csiHook) Name() string { 25 return "csi_hook" 26 } 27 28 func (c *csiHook) Prerun() error { 29 if !c.shouldRun() { 30 return nil 31 } 32 33 // TODO(tgross): the contexts for the CSI RPC calls made during 34 // mounting can have very long timeouts. Until these are better 35 // tuned, there's not a good value to put here for a WithCancel 36 // without risking conflicts with the grpc retries/timeouts in the 37 // pluginmanager package. 38 ctx := context.TODO() 39 40 volumes, err := c.claimVolumesFromAlloc() 41 if err != nil { 42 return fmt.Errorf("claim volumes: %v", err) 43 } 44 45 mounts := make(map[string]*csimanager.MountInfo, len(volumes)) 46 for alias, pair := range volumes { 47 mounter, err := c.csimanager.MounterForPlugin(ctx, pair.volume.PluginID) 48 if err != nil { 49 return err 50 } 51 52 usageOpts := &csimanager.UsageOptions{ 53 ReadOnly: pair.request.ReadOnly, 54 AttachmentMode: string(pair.volume.AttachmentMode), 55 AccessMode: string(pair.volume.AccessMode), 56 MountOptions: pair.request.MountOptions, 57 } 58 59 mountInfo, err := mounter.MountVolume(ctx, pair.volume, c.alloc, usageOpts, pair.publishContext) 60 if err != nil { 61 return err 62 } 63 64 mounts[alias] = mountInfo 65 } 66 67 res := c.updater.GetAllocHookResources() 68 res.CSIMounts = mounts 69 c.updater.SetAllocHookResources(res) 70 71 return nil 72 } 73 74 type volumeAndRequest struct { 75 volume *structs.CSIVolume 76 request *structs.VolumeRequest 77 78 // When volumeAndRequest was returned from a volume claim, this field will be 79 // populated for plugins that require it. 80 publishContext map[string]string 81 } 82 83 // claimVolumesFromAlloc is used by the pre-run hook to fetch all of the volume 84 // metadata and claim it for use by this alloc/node at the same time. 85 func (c *csiHook) claimVolumesFromAlloc() (map[string]*volumeAndRequest, error) { 86 result := make(map[string]*volumeAndRequest) 87 tg := c.alloc.Job.LookupTaskGroup(c.alloc.TaskGroup) 88 89 // Initially, populate the result map with all of the requests 90 for alias, volumeRequest := range tg.Volumes { 91 if volumeRequest.Type == structs.VolumeTypeCSI { 92 result[alias] = &volumeAndRequest{request: volumeRequest} 93 } 94 } 95 96 // Iterate over the result map and upsert the volume field as each volume gets 97 // claimed by the server. 98 for alias, pair := range result { 99 claimType := structs.CSIVolumeClaimWrite 100 if pair.request.ReadOnly { 101 claimType = structs.CSIVolumeClaimRead 102 } 103 104 req := &structs.CSIVolumeClaimRequest{ 105 VolumeID: pair.request.Source, 106 AllocationID: c.alloc.ID, 107 NodeID: c.alloc.NodeID, 108 Claim: claimType, 109 } 110 req.Region = c.alloc.Job.Region 111 112 var resp structs.CSIVolumeClaimResponse 113 if err := c.rpcClient.RPC("CSIVolume.Claim", req, &resp); err != nil { 114 return nil, err 115 } 116 117 if resp.Volume == nil { 118 return nil, fmt.Errorf("Unexpected nil volume returned for ID: %v", pair.request.Source) 119 } 120 121 result[alias].volume = resp.Volume 122 result[alias].publishContext = resp.PublishContext 123 } 124 125 return result, nil 126 } 127 128 func newCSIHook(logger hclog.Logger, alloc *structs.Allocation, rpcClient RPCer, csi csimanager.Manager, updater hookResourceSetter) *csiHook { 129 return &csiHook{ 130 alloc: alloc, 131 logger: logger.Named("csi_hook"), 132 rpcClient: rpcClient, 133 csimanager: csi, 134 updater: updater, 135 } 136 } 137 138 func (h *csiHook) shouldRun() bool { 139 tg := h.alloc.Job.LookupTaskGroup(h.alloc.TaskGroup) 140 for _, vol := range tg.Volumes { 141 if vol.Type == structs.VolumeTypeCSI { 142 return true 143 } 144 } 145 146 return false 147 }