github.com/janma/nomad@v0.11.3/command/agent/csi_endpoint.go (about) 1 package agent 2 3 import ( 4 "net/http" 5 "strings" 6 7 "github.com/hashicorp/nomad/nomad/structs" 8 ) 9 10 func (s *HTTPServer) CSIVolumesRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 11 if req.Method != "GET" { 12 return nil, CodedError(405, ErrInvalidMethod) 13 } 14 15 // Type filters volume lists to a specific type. When support for non-CSI volumes is 16 // introduced, we'll need to dispatch here 17 query := req.URL.Query() 18 qtype, ok := query["type"] 19 if !ok { 20 return []*structs.CSIVolListStub{}, nil 21 } 22 if qtype[0] != "csi" { 23 return nil, nil 24 } 25 26 args := structs.CSIVolumeListRequest{} 27 28 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 29 return nil, nil 30 } 31 32 if plugin, ok := query["plugin_id"]; ok { 33 args.PluginID = plugin[0] 34 } 35 if node, ok := query["node_id"]; ok { 36 args.NodeID = node[0] 37 } 38 39 var out structs.CSIVolumeListResponse 40 if err := s.agent.RPC("CSIVolume.List", &args, &out); err != nil { 41 return nil, err 42 } 43 44 setMeta(resp, &out.QueryMeta) 45 return out.Volumes, nil 46 } 47 48 // CSIVolumeSpecificRequest dispatches GET and PUT 49 func (s *HTTPServer) CSIVolumeSpecificRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 50 // Tokenize the suffix of the path to get the volume id 51 reqSuffix := strings.TrimPrefix(req.URL.Path, "/v1/volume/csi/") 52 tokens := strings.Split(reqSuffix, "/") 53 if len(tokens) > 2 || len(tokens) < 1 { 54 return nil, CodedError(404, resourceNotFoundErr) 55 } 56 id := tokens[0] 57 58 switch req.Method { 59 case "GET": 60 return s.csiVolumeGet(id, resp, req) 61 case "PUT": 62 return s.csiVolumePut(id, resp, req) 63 case "DELETE": 64 return s.csiVolumeDelete(id, resp, req) 65 default: 66 return nil, CodedError(405, ErrInvalidMethod) 67 } 68 } 69 70 func (s *HTTPServer) csiVolumeGet(id string, resp http.ResponseWriter, req *http.Request) (interface{}, error) { 71 args := structs.CSIVolumeGetRequest{ 72 ID: id, 73 } 74 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 75 return nil, nil 76 } 77 78 var out structs.CSIVolumeGetResponse 79 if err := s.agent.RPC("CSIVolume.Get", &args, &out); err != nil { 80 return nil, err 81 } 82 83 setMeta(resp, &out.QueryMeta) 84 if out.Volume == nil { 85 return nil, CodedError(404, "volume not found") 86 } 87 88 // remove sensitive fields, as our redaction mechanism doesn't 89 // help serializing here 90 out.Volume.Secrets = nil 91 out.Volume.MountOptions = nil 92 return out.Volume, nil 93 } 94 95 func (s *HTTPServer) csiVolumePut(id string, resp http.ResponseWriter, req *http.Request) (interface{}, error) { 96 if req.Method != "PUT" { 97 return nil, CodedError(405, ErrInvalidMethod) 98 } 99 100 args0 := structs.CSIVolumeRegisterRequest{} 101 if err := decodeBody(req, &args0); err != nil { 102 return err, CodedError(400, err.Error()) 103 } 104 105 args := structs.CSIVolumeRegisterRequest{ 106 Volumes: args0.Volumes, 107 } 108 s.parseWriteRequest(req, &args.WriteRequest) 109 110 var out structs.CSIVolumeRegisterResponse 111 if err := s.agent.RPC("CSIVolume.Register", &args, &out); err != nil { 112 return nil, err 113 } 114 115 setMeta(resp, &out.QueryMeta) 116 117 return nil, nil 118 } 119 120 func (s *HTTPServer) csiVolumeDelete(id string, resp http.ResponseWriter, req *http.Request) (interface{}, error) { 121 if req.Method != "DELETE" { 122 return nil, CodedError(405, ErrInvalidMethod) 123 } 124 125 args := structs.CSIVolumeDeregisterRequest{ 126 VolumeIDs: []string{id}, 127 } 128 s.parseWriteRequest(req, &args.WriteRequest) 129 130 var out structs.CSIVolumeDeregisterResponse 131 if err := s.agent.RPC("CSIVolume.Deregister", &args, &out); err != nil { 132 return nil, err 133 } 134 135 setMeta(resp, &out.QueryMeta) 136 137 return nil, nil 138 } 139 140 // CSIPluginsRequest lists CSI plugins 141 func (s *HTTPServer) CSIPluginsRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 142 if req.Method != "GET" { 143 return nil, CodedError(405, ErrInvalidMethod) 144 } 145 146 // Type filters plugin lists to a specific type. When support for non-CSI plugins is 147 // introduced, we'll need to dispatch here 148 query := req.URL.Query() 149 qtype, ok := query["type"] 150 if !ok { 151 return []*structs.CSIPluginListStub{}, nil 152 } 153 if qtype[0] != "csi" { 154 return nil, nil 155 } 156 157 args := structs.CSIPluginListRequest{} 158 159 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 160 return nil, nil 161 } 162 163 var out structs.CSIPluginListResponse 164 if err := s.agent.RPC("CSIPlugin.List", &args, &out); err != nil { 165 return nil, err 166 } 167 168 setMeta(resp, &out.QueryMeta) 169 return out.Plugins, nil 170 } 171 172 // CSIPluginSpecificRequest list the job with CSIInfo 173 func (s *HTTPServer) CSIPluginSpecificRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 174 if req.Method != "GET" { 175 return nil, CodedError(405, ErrInvalidMethod) 176 } 177 178 // Tokenize the suffix of the path to get the plugin id 179 reqSuffix := strings.TrimPrefix(req.URL.Path, "/v1/plugin/csi/") 180 tokens := strings.Split(reqSuffix, "/") 181 if len(tokens) > 2 || len(tokens) < 1 { 182 return nil, CodedError(404, resourceNotFoundErr) 183 } 184 id := tokens[0] 185 186 args := structs.CSIPluginGetRequest{ID: id} 187 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 188 return nil, nil 189 } 190 191 var out structs.CSIPluginGetResponse 192 if err := s.agent.RPC("CSIPlugin.Get", &args, &out); err != nil { 193 return nil, err 194 } 195 196 setMeta(resp, &out.QueryMeta) 197 if out.Plugin == nil { 198 return nil, CodedError(404, "plugin not found") 199 } 200 201 return out.Plugin, nil 202 }