github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/gateway/storageprovider.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package gateway 20 21 import ( 22 "context" 23 "encoding/json" 24 "encoding/xml" 25 "fmt" 26 "net/url" 27 "strconv" 28 "strings" 29 "time" 30 31 "github.com/BurntSushi/toml" 32 gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 33 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 34 collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" 35 linkv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" 36 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 37 registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1" 38 typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 39 "google.golang.org/grpc/codes" 40 41 "github.com/cs3org/reva/v2/pkg/appctx" 42 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 43 "github.com/cs3org/reva/v2/pkg/errtypes" 44 "github.com/cs3org/reva/v2/pkg/publicshare" 45 "github.com/cs3org/reva/v2/pkg/rgrpc/status" 46 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 47 sdk "github.com/cs3org/reva/v2/pkg/sdk/common" 48 "github.com/cs3org/reva/v2/pkg/share" 49 "github.com/cs3org/reva/v2/pkg/storagespace" 50 "github.com/cs3org/reva/v2/pkg/utils" 51 "github.com/golang-jwt/jwt/v5" 52 "github.com/pkg/errors" 53 gstatus "google.golang.org/grpc/status" 54 ) 55 56 /* About caching 57 The gateway is doing a lot of requests to look up the responsible storage providers for a reference. 58 - when the reference uses an id we can use a global id -> provider cache because it is the same for all users 59 - when the reference is an absolute path we 60 - 1. look up the corresponding space in the space registry 61 - 2. can reuse the global id -> provider cache to look up the provider 62 - paths are unique per user: when a rule mounts shares at /shares/{{.Space.Name}} 63 the path /shares/Documents might show different content for einstein than for marie 64 -> path -> spaceid lookup needs a per user cache 65 When can we invalidate? 66 - the global cache needs to be invalidated when the provider for a space id changes. 67 - happens when a space is moved from one provider to another. Not yet implemented 68 -> should be good enough to use a TTL. daily should be good enough 69 - the user individual file cache is actually a cache of the mount points 70 - we could do a registry.ListProviders (for user) on startup to warm up the cache ... 71 - when a share is granted or removed we need to invalidate that path 72 - when a share is renamed we need to invalidate the path 73 - we can use a ttl for all paths? 74 - the findProviders func in the gateway needs to look up in the user cache first 75 We want to cache the root etag of spaces 76 - can be invalidated on every write or delete with fallback via TTL? 77 */ 78 79 // transferClaims are custom claims for a JWT token to be used between the metadata and data gateways. 80 type transferClaims struct { 81 jwt.RegisteredClaims 82 Target string `json:"target"` 83 } 84 85 func (s *svc) sign(_ context.Context, target string, expiresAt int64) (string, error) { 86 // Tus sends a separate request to the datagateway service for every chunk. 87 // For large files, this can take a long time, so we extend the expiration 88 claims := transferClaims{ 89 RegisteredClaims: jwt.RegisteredClaims{ 90 ExpiresAt: jwt.NewNumericDate(time.Unix(expiresAt, 0)), 91 Audience: jwt.ClaimStrings{"reva"}, 92 IssuedAt: jwt.NewNumericDate(time.Now()), 93 }, 94 Target: target, 95 } 96 97 t := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), claims) 98 99 tkn, err := t.SignedString([]byte(s.c.TransferSharedSecret)) 100 if err != nil { 101 return "", errors.Wrapf(err, "error signing token with claims %+v", claims) 102 } 103 104 return tkn, nil 105 } 106 107 func (s *svc) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (*provider.CreateHomeResponse, error) { 108 u, ok := ctxpkg.ContextGetUser(ctx) 109 if !ok { 110 return &provider.CreateHomeResponse{ 111 Status: status.NewPermissionDenied(ctx, nil, "can't create home for anonymous user"), 112 }, nil 113 114 } 115 quotaStr := utils.ReadPlainFromOpaque(req.Opaque, "quota") 116 var quota *provider.Quota 117 if quotaStr != "" { 118 q, err := strconv.ParseUint(quotaStr, 10, 64) 119 if err != nil { 120 return &provider.CreateHomeResponse{ 121 Status: status.NewInvalid(ctx, fmt.Sprintf("can't parse quotaStr: %s", quotaStr)), 122 }, nil 123 } 124 quota = &provider.Quota{ 125 QuotaMaxBytes: q, 126 } 127 } 128 createReq := &provider.CreateStorageSpaceRequest{ 129 Type: "personal", 130 Owner: u, 131 Name: u.DisplayName, 132 Quota: quota, 133 } 134 135 // send the user id as the space id, makes debugging easier 136 if u.Id != nil && u.Id.OpaqueId != "" { 137 createReq.Opaque = &typesv1beta1.Opaque{ 138 Map: map[string]*typesv1beta1.OpaqueEntry{ 139 "space_id": { 140 Decoder: "plain", 141 Value: []byte(u.Id.OpaqueId), 142 }, 143 }, 144 } 145 } 146 res, err := s.CreateStorageSpace(ctx, createReq) 147 if err != nil { 148 return &provider.CreateHomeResponse{ 149 Status: status.NewStatusFromErrType(ctx, "gateway could not call CreateStorageSpace", err), 150 }, nil 151 } 152 return &provider.CreateHomeResponse{ 153 Opaque: res.Opaque, 154 Status: res.Status, 155 }, nil 156 } 157 158 func (s *svc) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) { 159 // TODO change the CreateStorageSpaceRequest to contain a space instead of sending individual properties 160 space := &provider.StorageSpace{ 161 Owner: req.Owner, 162 SpaceType: req.Type, 163 Name: req.Name, 164 Quota: req.Quota, 165 } 166 167 if req.Opaque != nil && req.Opaque.Map != nil && req.Opaque.Map["id"] != nil { 168 if req.Opaque.Map["space_id"].Decoder == "plain" { 169 space.Id = &provider.StorageSpaceId{OpaqueId: string(req.Opaque.Map["id"].Value)} 170 } 171 } 172 173 srClient, err := s.getStorageRegistryClient(ctx, s.c.StorageRegistryEndpoint) 174 if err != nil { 175 return &provider.CreateStorageSpaceResponse{ 176 Status: status.NewStatusFromErrType(ctx, "gateway could get storage registry client", err), 177 }, nil 178 } 179 180 spaceJSON, err := json.Marshal(space) 181 if err != nil { 182 return &provider.CreateStorageSpaceResponse{ 183 Status: status.NewStatusFromErrType(ctx, "gateway could not marshal space json", err), 184 }, nil 185 } 186 187 // The registry is responsible for choosing the right provider 188 res, err := srClient.GetStorageProviders(ctx, ®istry.GetStorageProvidersRequest{ 189 Opaque: &typesv1beta1.Opaque{ 190 Map: map[string]*typesv1beta1.OpaqueEntry{ 191 "space": { 192 Decoder: "json", 193 Value: spaceJSON, 194 }, 195 }, 196 }, 197 }) 198 if err != nil { 199 return &provider.CreateStorageSpaceResponse{ 200 Status: status.NewStatusFromErrType(ctx, "gateway could not call GetStorageProviders", err), 201 }, nil 202 } 203 if res.Status.Code != rpc.Code_CODE_OK { 204 return &provider.CreateStorageSpaceResponse{ 205 Status: res.Status, 206 }, nil 207 } 208 209 if len(res.Providers) == 0 { 210 return &provider.CreateStorageSpaceResponse{ 211 Status: status.NewNotFound(ctx, fmt.Sprintf("gateway found no provider for space %+v", space)), 212 }, nil 213 } 214 215 // just pick the first provider, we expect only one 216 c, err := s.getSpacesProviderClient(ctx, res.Providers[0]) 217 if err != nil { 218 return &provider.CreateStorageSpaceResponse{ 219 Status: status.NewStatusFromErrType(ctx, "gateway could not get storage provider client", err), 220 }, nil 221 } 222 createRes, err := c.CreateStorageSpace(ctx, req) 223 if err != nil { 224 return &provider.CreateStorageSpaceResponse{ 225 Status: status.NewStatusFromErrType(ctx, "gateway could not call CreateStorageSpace", err), 226 }, nil 227 } 228 229 return createRes, nil 230 } 231 232 func (s *svc) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSpacesRequest) (*provider.ListStorageSpacesResponse, error) { 233 // TODO update CS3 api to forward the filters to the registry so it can filter the number of providers the gateway needs to query 234 filters := map[string]string{ 235 // TODO add opaque / CS3 api to expand 'path,root,stat?' properties / field mask 236 "mask": "*", // fetch all properties when listing storage spaces 237 } 238 239 mask := utils.ReadPlainFromOpaque(req.Opaque, "mask") 240 if mask != "" { 241 // TODO check for allowed filters 242 filters["mask"] = mask 243 } 244 path := utils.ReadPlainFromOpaque(req.Opaque, "path") 245 if path != "" { 246 // TODO check for allowed filters 247 filters["path"] = path 248 } 249 250 for _, f := range req.Filters { 251 switch f.Type { 252 case provider.ListStorageSpacesRequest_Filter_TYPE_ID: 253 sid, spid, oid, err := storagespace.SplitID(f.GetId().GetOpaqueId()) 254 if err != nil { 255 continue 256 } 257 filters["storage_id"], filters["space_id"], filters["opaque_id"] = sid, spid, oid 258 case provider.ListStorageSpacesRequest_Filter_TYPE_OWNER: 259 filters["owner_idp"] = f.GetOwner().GetIdp() 260 filters["owner_id"] = f.GetOwner().GetOpaqueId() 261 case provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE: 262 filters["space_type"] = f.GetSpaceType() 263 case provider.ListStorageSpacesRequest_Filter_TYPE_USER: 264 filters["user_idp"] = f.GetUser().GetIdp() 265 filters["user_id"] = f.GetUser().GetOpaqueId() 266 default: 267 return &provider.ListStorageSpacesResponse{ 268 Status: status.NewInvalid(ctx, fmt.Sprintf("unknown filter %v", f.Type)), 269 }, nil 270 } 271 } 272 273 c, err := s.getStorageRegistryClient(ctx, s.c.StorageRegistryEndpoint) 274 if err != nil { 275 return &provider.ListStorageSpacesResponse{ 276 Status: status.NewStatusFromErrType(ctx, "gateway could not get storage registry client", err), 277 }, nil 278 } 279 280 listReq := ®istry.ListStorageProvidersRequest{Opaque: req.Opaque} 281 if listReq.Opaque == nil { 282 listReq.Opaque = &typesv1beta1.Opaque{} 283 } 284 if len(filters) > 0 { 285 sdk.EncodeOpaqueMap(listReq.Opaque, filters) 286 } 287 res, err := c.ListStorageProviders(ctx, listReq) 288 if err != nil { 289 return &provider.ListStorageSpacesResponse{ 290 Status: status.NewStatusFromErrType(ctx, "gateway could not call ListStorageSpaces", err), 291 }, nil 292 } 293 if res.Status.Code != rpc.Code_CODE_OK { 294 return &provider.ListStorageSpacesResponse{ 295 Status: res.Status, 296 }, nil 297 } 298 299 spaces := []*provider.StorageSpace{} 300 for _, providerInfo := range res.Providers { 301 spaces = append(spaces, decodeSpaces(providerInfo)...) 302 } 303 304 return &provider.ListStorageSpacesResponse{ 305 Status: status.NewOK(ctx), 306 StorageSpaces: spaces, 307 }, nil 308 } 309 310 func (s *svc) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { 311 // TODO: needs to be fixed 312 ref := &provider.Reference{ResourceId: req.StorageSpace.Root} 313 c, _, err := s.findSpacesProvider(ctx, ref) 314 if err != nil { 315 return &provider.UpdateStorageSpaceResponse{ 316 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find reference %+v", ref), err), 317 }, nil 318 } 319 320 res, err := c.UpdateStorageSpace(ctx, req) 321 if err != nil { 322 return &provider.UpdateStorageSpaceResponse{ 323 Status: status.NewStatusFromErrType(ctx, "gateway could not call UpdateStorageSpace", err), 324 }, nil 325 } 326 327 if res.Status.Code == rpc.Code_CODE_OK { 328 id := res.StorageSpace.Root 329 s.providerCache.RemoveListStorageProviders(id) 330 } 331 return res, nil 332 } 333 334 func (s *svc) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*provider.DeleteStorageSpaceResponse, error) { 335 opaque := req.Opaque 336 var purge bool 337 // This is just a temporary hack until the CS3 API get's updated to have a dedicated purge parameter or a dedicated PurgeStorageSpace method. 338 if opaque != nil { 339 _, purge = opaque.Map["purge"] 340 } 341 342 rid, err := storagespace.ParseID(req.GetId().GetOpaqueId()) 343 if err != nil { 344 return &provider.DeleteStorageSpaceResponse{ 345 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not parse space id %s", req.GetId().GetOpaqueId()), err), 346 }, nil 347 } 348 349 ref := &provider.Reference{ResourceId: &rid} 350 c, _, err := s.findSpacesProvider(ctx, ref) 351 if err != nil { 352 return &provider.DeleteStorageSpaceResponse{ 353 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find reference %+v", ref), err), 354 }, nil 355 } 356 357 dsRes, err := c.DeleteStorageSpace(ctx, req) 358 if err != nil { 359 return &provider.DeleteStorageSpaceResponse{ 360 Status: status.NewStatusFromErrType(ctx, "gateway could not call DeleteStorageSpace", err), 361 }, nil 362 } 363 364 id := &provider.ResourceId{OpaqueId: req.GetId().GetOpaqueId()} 365 s.providerCache.RemoveListStorageProviders(id) 366 367 if dsRes.Status.Code != rpc.Code_CODE_OK { 368 return dsRes, nil 369 } 370 371 if !purge { 372 return dsRes, nil 373 } 374 375 log := appctx.GetLogger(ctx) 376 log.Debug().Msg("purging storage space") 377 // List all shares in this storage space 378 lsRes, err := s.ListShares(ctx, &collaborationv1beta1.ListSharesRequest{ 379 Filters: []*collaborationv1beta1.Filter{share.SpaceIDFilter(id.SpaceId)}, 380 }) 381 switch { 382 case err != nil: 383 return &provider.DeleteStorageSpaceResponse{ 384 Status: status.NewStatusFromErrType(ctx, "gateway could not delete shares of StorageSpace", err), 385 }, nil 386 case lsRes.Status.Code != rpc.Code_CODE_OK: 387 return &provider.DeleteStorageSpaceResponse{ 388 Status: status.NewInternal(ctx, "gateway could not delete shares of StorageSpace"), 389 }, nil 390 } 391 for _, share := range lsRes.Shares { 392 rsRes, err := s.RemoveShare(ctx, &collaborationv1beta1.RemoveShareRequest{ 393 Ref: &collaborationv1beta1.ShareReference{ 394 Spec: &collaborationv1beta1.ShareReference_Id{Id: share.Id}, 395 }, 396 }) 397 if err != nil || rsRes.Status.Code != rpc.Code_CODE_OK { 398 log.Error().Err(err).Interface("status", rsRes.Status).Str("share_id", share.Id.OpaqueId).Msg("failed to delete share") 399 } 400 } 401 402 // List all public shares in this storage space 403 lpsRes, err := s.ListPublicShares(ctx, &linkv1beta1.ListPublicSharesRequest{ 404 Filters: []*linkv1beta1.ListPublicSharesRequest_Filter{publicshare.StorageIDFilter(id.SpaceId)}, // FIXME rename the filter? @c0rby 405 }) 406 switch { 407 case err != nil: 408 return &provider.DeleteStorageSpaceResponse{ 409 Status: status.NewStatusFromErrType(ctx, "gateway could not delete shares of StorageSpace", err), 410 }, nil 411 case lpsRes.Status.Code != rpc.Code_CODE_OK: 412 return &provider.DeleteStorageSpaceResponse{ 413 Status: status.NewInternal(ctx, "gateway could not delete shares of StorageSpace"), 414 }, nil 415 } 416 for _, share := range lpsRes.Share { 417 rsRes, err := s.RemovePublicShare(ctx, &linkv1beta1.RemovePublicShareRequest{ 418 Ref: &linkv1beta1.PublicShareReference{ 419 Spec: &linkv1beta1.PublicShareReference_Id{Id: share.Id}, 420 }, 421 }) 422 if err != nil || rsRes.Status.Code != rpc.Code_CODE_OK { 423 log.Error().Err(err).Interface("status", rsRes.Status).Str("share_id", share.Id.OpaqueId).Msg("failed to delete share") 424 } 425 } 426 427 return dsRes, nil 428 } 429 430 func (s *svc) GetHome(ctx context.Context, _ *provider.GetHomeRequest) (*provider.GetHomeResponse, error) { 431 currentUser, ok := ctxpkg.ContextGetUser(ctx) 432 if !ok { 433 return nil, errors.New("user not found in context") 434 } 435 436 srClient, err := s.getStorageRegistryClient(ctx, s.c.StorageRegistryEndpoint) 437 if err != nil { 438 return &provider.GetHomeResponse{ 439 Status: status.NewStatusFromErrType(ctx, "gateway could not get storage registry client", err), 440 }, nil 441 } 442 443 spaceJSON, err := json.Marshal(&provider.StorageSpace{ 444 Owner: currentUser, 445 SpaceType: "personal", 446 }) 447 if err != nil { 448 return &provider.GetHomeResponse{ 449 Status: status.NewStatusFromErrType(ctx, "gateway could not marshal space", err), 450 }, nil 451 } 452 453 // The registry is responsible for choosing the right provider 454 // TODO fix naming GetStorageProviders calls the GetProvider functon on the registry implementation 455 res, err := srClient.GetStorageProviders(ctx, ®istry.GetStorageProvidersRequest{ 456 Opaque: &typesv1beta1.Opaque{ 457 Map: map[string]*typesv1beta1.OpaqueEntry{ 458 "space": { 459 Decoder: "json", 460 Value: spaceJSON, 461 }, 462 }, 463 }, 464 }) 465 if err != nil { 466 return &provider.GetHomeResponse{ 467 Status: status.NewStatusFromErrType(ctx, "gateway could not call GetStorageProviders", err), 468 }, nil 469 } 470 if res.Status.Code != rpc.Code_CODE_OK { 471 return &provider.GetHomeResponse{ 472 Status: res.Status, 473 }, nil 474 } 475 476 if len(res.Providers) == 0 { 477 return &provider.GetHomeResponse{ 478 Status: status.NewNotFound(ctx, fmt.Sprintf("error finding provider for home space of %+v", currentUser)), 479 }, nil 480 } 481 482 // NOTE: this will cause confusion if len(spaces) > 1 483 spaces := decodeSpaces(res.Providers[0]) 484 for _, space := range spaces { 485 return &provider.GetHomeResponse{ 486 Path: decodePath(space), 487 Status: status.NewOK(ctx), 488 }, nil 489 } 490 491 return &provider.GetHomeResponse{ 492 Status: status.NewNotFound(ctx, fmt.Sprintf("error finding home path for provider %+v with spaces %+v ", res.Providers[0], spaces)), 493 }, nil 494 } 495 496 func (s *svc) InitiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*gateway.InitiateFileDownloadResponse, error) { 497 // TODO(ishank011): enable downloading references spread across storage providers, eg. /eos 498 var c provider.ProviderAPIClient 499 var err error 500 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 501 if err != nil { 502 return &gateway.InitiateFileDownloadResponse{ 503 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 504 }, nil 505 } 506 507 storageRes, err := c.InitiateFileDownload(ctx, req) 508 if err != nil { 509 return &gateway.InitiateFileDownloadResponse{ 510 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not call InitiateFileDownload, ref=%+v", req.Ref), err), 511 }, nil 512 } 513 514 protocols := make([]*gateway.FileDownloadProtocol, len(storageRes.Protocols)) 515 for p := range storageRes.Protocols { 516 protocols[p] = &gateway.FileDownloadProtocol{ 517 Opaque: storageRes.Protocols[p].Opaque, 518 Protocol: storageRes.Protocols[p].Protocol, 519 DownloadEndpoint: storageRes.Protocols[p].DownloadEndpoint, 520 } 521 522 if !storageRes.Protocols[p].Expose { 523 // sign the download location and pass it to the data gateway 524 u, err := url.Parse(protocols[p].DownloadEndpoint) 525 if err != nil { 526 return &gateway.InitiateFileDownloadResponse{ 527 Status: status.NewStatusFromErrType(ctx, "wrong format for download endpoint", err), 528 }, nil 529 } 530 531 // TODO(labkode): calculate signature of the whole request? we only sign the URI now. Maybe worth https://tools.ietf.org/html/draft-cavage-http-signatures-11 532 target := u.String() 533 token, err := s.sign(ctx, target, time.Now().UTC().Add(time.Duration(s.c.TransferExpires)*time.Second).Unix()) 534 if err != nil { 535 return &gateway.InitiateFileDownloadResponse{ 536 Status: status.NewStatusFromErrType(ctx, "error creating signature for download", err), 537 }, nil 538 } 539 540 protocols[p].DownloadEndpoint = s.c.DataGatewayEndpoint 541 protocols[p].Token = token 542 } 543 } 544 545 return &gateway.InitiateFileDownloadResponse{ 546 Opaque: storageRes.Opaque, 547 Status: storageRes.Status, 548 Protocols: protocols, 549 }, nil 550 } 551 552 func (s *svc) InitiateFileUpload(ctx context.Context, req *provider.InitiateFileUploadRequest) (*gateway.InitiateFileUploadResponse, error) { 553 var c provider.ProviderAPIClient 554 var err error 555 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 556 if err != nil { 557 return &gateway.InitiateFileUploadResponse{ 558 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 559 }, nil 560 } 561 562 storageRes, err := c.InitiateFileUpload(ctx, req) 563 if err != nil { 564 return &gateway.InitiateFileUploadResponse{ 565 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not call InitiateFileUpload, ref=%+v", req.Ref), err), 566 }, nil 567 } 568 569 if storageRes.Status.Code != rpc.Code_CODE_OK { 570 return &gateway.InitiateFileUploadResponse{ 571 Status: storageRes.Status, 572 }, nil 573 } 574 575 protocols := make([]*gateway.FileUploadProtocol, len(storageRes.Protocols)) 576 for p := range storageRes.Protocols { 577 protocols[p] = &gateway.FileUploadProtocol{ 578 Opaque: storageRes.Protocols[p].Opaque, 579 Protocol: storageRes.Protocols[p].Protocol, 580 UploadEndpoint: storageRes.Protocols[p].UploadEndpoint, 581 AvailableChecksums: storageRes.Protocols[p].AvailableChecksums, 582 } 583 584 if !storageRes.Protocols[p].Expose { 585 // sign the upload location and pass it to the data gateway 586 u, err := url.Parse(protocols[p].UploadEndpoint) 587 if err != nil { 588 return &gateway.InitiateFileUploadResponse{ 589 Status: status.NewStatusFromErrType(ctx, "wrong format for upload endpoint", err), 590 }, nil 591 } 592 593 // TODO(labkode): calculate signature of the whole request? we only sign the URI now. Maybe worth https://tools.ietf.org/html/draft-cavage-http-signatures-11 594 target := u.String() 595 ttl := time.Duration(s.c.TransferExpires) * time.Second 596 expiresAt := time.Now().Add(ttl).Unix() 597 if storageRes.Protocols[p].Expiration != nil { 598 expiresAt = utils.TSToTime(storageRes.Protocols[p].Expiration).Unix() 599 } 600 token, err := s.sign(ctx, target, expiresAt) 601 if err != nil { 602 return &gateway.InitiateFileUploadResponse{ 603 Status: status.NewStatusFromErrType(ctx, "error creating signature for upload", err), 604 }, nil 605 } 606 607 protocols[p].UploadEndpoint = s.c.DataGatewayEndpoint 608 protocols[p].Token = token 609 } 610 } 611 612 return &gateway.InitiateFileUploadResponse{ 613 Opaque: storageRes.Opaque, 614 Status: storageRes.Status, 615 Protocols: protocols, 616 }, nil 617 } 618 619 func (s *svc) GetPath(ctx context.Context, req *provider.GetPathRequest) (*provider.GetPathResponse, error) { 620 c, _, ref, err := s.findAndUnwrap(ctx, &provider.Reference{ResourceId: req.ResourceId}) 621 if err != nil { 622 return &provider.GetPathResponse{ 623 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find reference %+v", ref), err), 624 }, nil 625 } 626 627 req.ResourceId = ref.ResourceId 628 return c.GetPath(ctx, req) 629 } 630 631 func (s *svc) CreateContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) { 632 var c provider.ProviderAPIClient 633 var err error 634 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 635 if err != nil { 636 return &provider.CreateContainerResponse{ 637 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 638 }, nil 639 } 640 641 res, err := c.CreateContainer(ctx, req) 642 if err != nil { 643 return &provider.CreateContainerResponse{ 644 Status: status.NewStatusFromErrType(ctx, "gateway could not call CreateContainer", err), 645 }, nil 646 } 647 648 return res, nil 649 } 650 651 func (s *svc) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) { 652 var c provider.ProviderAPIClient 653 var err error 654 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 655 if err != nil { 656 return &provider.TouchFileResponse{ 657 Status: status.NewStatusFromErrType(ctx, "TouchFile ref="+req.Ref.String(), err), 658 }, nil 659 } 660 661 res, err := c.TouchFile(ctx, req) 662 if err != nil { 663 if gstatus.Code(err) == codes.PermissionDenied { 664 return &provider.TouchFileResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil 665 } 666 return nil, errors.Wrap(err, "gateway: error calling TouchFile") 667 } 668 669 return res, nil 670 } 671 672 func (s *svc) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) { 673 // TODO(ishank011): enable deleting references spread across storage providers, eg. /eos 674 var c provider.ProviderAPIClient 675 var err error 676 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 677 if err != nil { 678 return &provider.DeleteResponse{ 679 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 680 }, nil 681 } 682 683 res, err := c.Delete(ctx, req) 684 if err != nil { 685 return &provider.DeleteResponse{ 686 Status: status.NewStatusFromErrType(ctx, "gateway could not call Delete", err), 687 }, nil 688 } 689 690 return res, nil 691 } 692 693 func (s *svc) Move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) { 694 c, sourceProviderInfo, sref, err := s.findAndUnwrap(ctx, req.Source) 695 if err != nil { 696 return &provider.MoveResponse{ 697 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Source), err), 698 }, nil 699 } 700 701 _, destProviderInfo, dref, err := s.findAndUnwrap(ctx, req.Destination) 702 if err != nil { 703 return &provider.MoveResponse{ 704 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Source), err), 705 }, nil 706 } 707 708 if sourceProviderInfo.Address != destProviderInfo.Address { 709 return &provider.MoveResponse{ 710 Status: status.NewUnimplemented(ctx, nil, "cross storage moves are not supported, use copy and delete"), 711 }, nil 712 } 713 714 req.Source = sref 715 req.Destination = dref 716 return c.Move(ctx, req) 717 } 718 719 func (s *svc) SetArbitraryMetadata(ctx context.Context, req *provider.SetArbitraryMetadataRequest) (*provider.SetArbitraryMetadataResponse, error) { 720 // TODO(ishank011): enable for references spread across storage providers, eg. /eos 721 var c provider.ProviderAPIClient 722 var err error 723 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 724 if err != nil { 725 return &provider.SetArbitraryMetadataResponse{ 726 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 727 }, nil 728 } 729 730 res, err := c.SetArbitraryMetadata(ctx, req) 731 if err != nil { 732 if gstatus.Code(err) == codes.PermissionDenied { 733 return &provider.SetArbitraryMetadataResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil 734 } 735 return nil, errors.Wrap(err, "gateway: error calling SetArbitraryMetadata") 736 } 737 738 return res, nil 739 } 740 741 func (s *svc) UnsetArbitraryMetadata(ctx context.Context, req *provider.UnsetArbitraryMetadataRequest) (*provider.UnsetArbitraryMetadataResponse, error) { 742 // TODO(ishank011): enable for references spread across storage providers, eg. /eos 743 var c provider.ProviderAPIClient 744 var err error 745 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 746 if err != nil { 747 return &provider.UnsetArbitraryMetadataResponse{ 748 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 749 }, nil 750 } 751 752 res, err := c.UnsetArbitraryMetadata(ctx, req) 753 if err != nil { 754 if gstatus.Code(err) == codes.PermissionDenied { 755 return &provider.UnsetArbitraryMetadataResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil 756 } 757 return nil, errors.Wrap(err, "gateway: error calling UnsetArbitraryMetadata") 758 } 759 760 return res, nil 761 } 762 763 // SetLock puts a lock on the given reference 764 func (s *svc) SetLock(ctx context.Context, req *provider.SetLockRequest) (*provider.SetLockResponse, error) { 765 var c provider.ProviderAPIClient 766 var err error 767 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 768 if err != nil { 769 return &provider.SetLockResponse{ 770 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 771 }, nil 772 } 773 774 res, err := c.SetLock(ctx, req) 775 if err != nil { 776 if gstatus.Code(err) == codes.PermissionDenied { 777 return &provider.SetLockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil 778 } 779 return nil, errors.Wrap(err, "gateway: error calling SetLock") 780 } 781 782 return res, nil 783 } 784 785 // GetLock returns an existing lock on the given reference 786 func (s *svc) GetLock(ctx context.Context, req *provider.GetLockRequest) (*provider.GetLockResponse, error) { 787 c, _, err := s.find(ctx, req.Ref) 788 if err != nil { 789 return &provider.GetLockResponse{ 790 Status: status.NewStatusFromErrType(ctx, "GetLock ref="+req.Ref.String(), err), 791 }, nil 792 } 793 794 res, err := c.GetLock(ctx, req) 795 if err != nil { 796 if gstatus.Code(err) == codes.PermissionDenied { 797 return &provider.GetLockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil 798 } 799 return nil, errors.Wrap(err, "gateway: error calling GetLock") 800 } 801 802 return res, nil 803 } 804 805 // RefreshLock refreshes an existing lock on the given reference 806 func (s *svc) RefreshLock(ctx context.Context, req *provider.RefreshLockRequest) (*provider.RefreshLockResponse, error) { 807 c, _, err := s.find(ctx, req.Ref) 808 if err != nil { 809 return &provider.RefreshLockResponse{ 810 Status: status.NewStatusFromErrType(ctx, "RefreshLock ref="+req.Ref.String(), err), 811 }, nil 812 } 813 814 res, err := c.RefreshLock(ctx, req) 815 if err != nil { 816 if gstatus.Code(err) == codes.PermissionDenied { 817 return &provider.RefreshLockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil 818 } 819 return nil, errors.Wrap(err, "gateway: error calling RefreshLock") 820 } 821 822 return res, nil 823 } 824 825 // Unlock removes an existing lock from the given reference 826 func (s *svc) Unlock(ctx context.Context, req *provider.UnlockRequest) (*provider.UnlockResponse, error) { 827 c, _, err := s.find(ctx, req.Ref) 828 if err != nil { 829 return &provider.UnlockResponse{ 830 Status: status.NewStatusFromErrType(ctx, "Unlock ref="+req.Ref.String(), err), 831 }, nil 832 } 833 834 res, err := c.Unlock(ctx, req) 835 if err != nil { 836 if gstatus.Code(err) == codes.PermissionDenied { 837 return &provider.UnlockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil 838 } 839 return nil, errors.Wrap(err, "gateway: error calling Unlock") 840 } 841 842 return res, nil 843 } 844 845 // Stat returns the Resoure info for a given resource by forwarding the request to the responsible provider. 846 func (s *svc) Stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) { 847 c, _, ref, err := s.findAndUnwrap(ctx, req.Ref) 848 if err != nil { 849 return &provider.StatResponse{ 850 Status: status.NewNotFound(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref)), 851 }, nil 852 } 853 854 return c.Stat(ctx, &provider.StatRequest{ 855 Opaque: req.Opaque, 856 Ref: ref, 857 ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, 858 FieldMask: req.FieldMask, 859 }) 860 } 861 862 func (s *svc) ListContainerStream(_ *provider.ListContainerStreamRequest, _ gateway.GatewayAPI_ListContainerStreamServer) error { 863 return errtypes.NotSupported("Unimplemented") 864 } 865 866 // ListContainer lists the Resoure infos for a given resource by forwarding the request to the responsible provider. 867 func (s *svc) ListContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) { 868 c, _, ref, err := s.findAndUnwrap(ctx, req.Ref) 869 if err != nil { 870 // we have no provider -> not found 871 return &provider.ListContainerResponse{ 872 Status: status.NewNotFound(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref)), 873 }, nil 874 } 875 876 return c.ListContainer(ctx, &provider.ListContainerRequest{ 877 Opaque: req.Opaque, 878 Ref: ref, 879 ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, 880 FieldMask: req.FieldMask, 881 }) 882 } 883 884 func (s *svc) CreateSymlink(ctx context.Context, req *provider.CreateSymlinkRequest) (*provider.CreateSymlinkResponse, error) { 885 return &provider.CreateSymlinkResponse{ 886 Status: status.NewUnimplemented(ctx, errtypes.NotSupported("CreateSymlink not implemented"), "CreateSymlink not implemented"), 887 }, nil 888 } 889 890 func (s *svc) ListFileVersions(ctx context.Context, req *provider.ListFileVersionsRequest) (*provider.ListFileVersionsResponse, error) { 891 var c provider.ProviderAPIClient 892 var err error 893 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 894 if err != nil { 895 return &provider.ListFileVersionsResponse{ 896 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 897 }, nil 898 } 899 900 return c.ListFileVersions(ctx, req) 901 } 902 903 func (s *svc) RestoreFileVersion(ctx context.Context, req *provider.RestoreFileVersionRequest) (*provider.RestoreFileVersionResponse, error) { 904 var c provider.ProviderAPIClient 905 var err error 906 c, _, req.Ref, err = s.findAndUnwrap(ctx, req.Ref) 907 if err != nil { 908 return &provider.RestoreFileVersionResponse{ 909 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 910 }, nil 911 } 912 913 res, err := c.RestoreFileVersion(ctx, req) 914 if err != nil { 915 return &provider.RestoreFileVersionResponse{ 916 Status: status.NewStatusFromErrType(ctx, "gateway could not call RestoreFileVersion", err), 917 }, nil 918 } 919 920 return res, nil 921 } 922 923 func (s *svc) ListRecycleStream(_ *provider.ListRecycleStreamRequest, _ gateway.GatewayAPI_ListRecycleStreamServer) error { 924 return errtypes.NotSupported("ListRecycleStream unimplemented") 925 } 926 927 // TODO use the ListRecycleRequest.Ref to only list the trash of a specific storage 928 func (s *svc) ListRecycle(ctx context.Context, req *provider.ListRecycleRequest) (*provider.ListRecycleResponse, error) { 929 c, _, ref, err := s.findAndUnwrap(ctx, req.Ref) 930 if err != nil { 931 return &provider.ListRecycleResponse{ 932 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 933 }, nil 934 } 935 return c.ListRecycle(ctx, &provider.ListRecycleRequest{ 936 Opaque: req.Opaque, 937 FromTs: req.FromTs, 938 ToTs: req.ToTs, 939 Ref: ref, 940 Key: req.Key, 941 }) 942 } 943 944 func (s *svc) RestoreRecycleItem(ctx context.Context, req *provider.RestoreRecycleItemRequest) (*provider.RestoreRecycleItemResponse, error) { 945 c, si, ref, err := s.findAndUnwrap(ctx, req.Ref) 946 if err != nil { 947 return &provider.RestoreRecycleItemResponse{ 948 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 949 }, nil 950 } 951 952 _, di, rref, err := s.findAndUnwrap(ctx, req.RestoreRef) 953 if err != nil { 954 return &provider.RestoreRecycleItemResponse{ 955 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 956 }, nil 957 } 958 959 if si.Address != di.Address { 960 return &provider.RestoreRecycleItemResponse{ 961 // TODO in Move() we return an unimplemented / supported ... align? 962 Status: status.NewPermissionDenied(ctx, err, "gateway: cross-storage restores are forbidden"), 963 }, nil 964 } 965 966 req.Ref = ref 967 req.RestoreRef = rref 968 res, err := c.RestoreRecycleItem(ctx, req) 969 if err != nil { 970 return &provider.RestoreRecycleItemResponse{ 971 Status: status.NewStatusFromErrType(ctx, "gateway could not call RestoreRecycleItem", err), 972 }, nil 973 } 974 975 return res, nil 976 } 977 978 func (s *svc) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) { 979 c, _, relativeReference, err := s.findAndUnwrap(ctx, req.Ref) 980 if err != nil { 981 return &provider.PurgeRecycleResponse{ 982 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 983 }, nil 984 } 985 986 res, err := c.PurgeRecycle(ctx, &provider.PurgeRecycleRequest{ 987 Opaque: req.GetOpaque(), 988 Ref: relativeReference, 989 Key: req.Key, 990 }) 991 if err != nil { 992 return &provider.PurgeRecycleResponse{ 993 Status: status.NewStatusFromErrType(ctx, "gateway could not call PurgeRecycle", err), 994 }, nil 995 } 996 997 return res, nil 998 } 999 1000 func (s *svc) GetQuota(ctx context.Context, req *gateway.GetQuotaRequest) (*provider.GetQuotaResponse, error) { 1001 c, _, relativeReference, err := s.findAndUnwrap(ctx, req.Ref) 1002 if err != nil { 1003 return &provider.GetQuotaResponse{ 1004 Status: status.NewStatusFromErrType(ctx, fmt.Sprintf("gateway could not find space for ref=%+v", req.Ref), err), 1005 }, nil 1006 } 1007 1008 res, err := c.GetQuota(ctx, &provider.GetQuotaRequest{ 1009 Opaque: req.GetOpaque(), 1010 Ref: relativeReference, 1011 }) 1012 if err != nil { 1013 return &provider.GetQuotaResponse{ 1014 Status: status.NewStatusFromErrType(ctx, "gateway could not call GetQuota", err), 1015 }, nil 1016 } 1017 return res, nil 1018 } 1019 1020 // find looks up the provider that is responsible for the given request 1021 // It will return a client that the caller can use to make the call, as well as the ProviderInfo. It: 1022 // - contains the provider path, which is the mount point of the provider 1023 // - may contain a list of storage spaces with their id and space path 1024 func (s *svc) find(ctx context.Context, ref *provider.Reference) (provider.ProviderAPIClient, *registry.ProviderInfo, error) { 1025 p, err := s.findSingleSpace(ctx, ref) 1026 if err != nil { 1027 return nil, nil, err 1028 } 1029 1030 client, err := s.getStorageProviderClient(ctx, p[0]) 1031 return client, p[0], err 1032 } 1033 1034 // findSpacesProvider looks up the spaces provider that is responsible for the given request 1035 // It will return a client that the caller can use to make the call, as well as the ProviderInfo. It: 1036 // - contains the provider path, which is the mount point of the provider 1037 // - may contain a list of storage spaces with their id and space path 1038 func (s *svc) findSpacesProvider(ctx context.Context, ref *provider.Reference) (provider.SpacesAPIClient, *registry.ProviderInfo, error) { 1039 p, err := s.findSingleSpace(ctx, ref) 1040 if err != nil { 1041 return nil, nil, err 1042 } 1043 1044 client, err := s.getSpacesProviderClient(ctx, p[0]) 1045 return client, p[0], err 1046 } 1047 1048 func (s *svc) findAndUnwrap(ctx context.Context, ref *provider.Reference) (provider.ProviderAPIClient, *registry.ProviderInfo, *provider.Reference, error) { 1049 c, p, err := s.find(ctx, ref) 1050 if err != nil { 1051 return nil, nil, nil, err 1052 } 1053 1054 var ( 1055 root *provider.ResourceId 1056 mountPath string 1057 ) 1058 for _, space := range decodeSpaces(p) { 1059 mountPath = decodePath(space) 1060 root = space.Root 1061 break // TODO can there be more than one space for a path? 1062 } 1063 1064 relativeReference := unwrap(ref, mountPath, root) 1065 1066 return c, p, relativeReference, nil 1067 } 1068 1069 func (s *svc) getSpacesProviderClient(_ context.Context, p *registry.ProviderInfo) (provider.SpacesAPIClient, error) { 1070 c, err := pool.GetSpacesProviderServiceClient(p.Address) 1071 if err != nil { 1072 return nil, err 1073 } 1074 1075 return &cachedSpacesAPIClient{ 1076 c: c, 1077 createPersonalSpaceCache: s.createPersonalSpaceCache, 1078 }, nil 1079 } 1080 1081 func (s *svc) getStorageProviderClient(_ context.Context, p *registry.ProviderInfo) (provider.ProviderAPIClient, error) { 1082 c, err := pool.GetStorageProviderServiceClient(p.Address) 1083 if err != nil { 1084 return nil, err 1085 } 1086 1087 return &cachedAPIClient{ 1088 c: c, 1089 createPersonalSpaceCache: s.createPersonalSpaceCache, 1090 }, nil 1091 } 1092 1093 func (s *svc) getStorageRegistryClient(_ context.Context, address string) (registry.RegistryAPIClient, error) { 1094 c, err := pool.GetStorageRegistryClient(address) 1095 if err != nil { 1096 return nil, err 1097 } 1098 return &cachedRegistryClient{ 1099 c: c, 1100 cache: s.providerCache, 1101 }, nil 1102 } 1103 1104 func (s *svc) findSingleSpace(ctx context.Context, ref *provider.Reference) ([]*registry.ProviderInfo, error) { 1105 switch { 1106 case ref == nil: 1107 return nil, errtypes.BadRequest("missing reference") 1108 case ref.ResourceId != nil: 1109 if ref.ResourceId.OpaqueId == "" { 1110 ref.ResourceId.OpaqueId = ref.ResourceId.SpaceId 1111 } 1112 case ref.Path != "": // TODO implement a mount path cache in the registry? 1113 // nothing to do here either 1114 default: 1115 return nil, errtypes.BadRequest("invalid reference, at least path or id must be set") 1116 } 1117 1118 filters := map[string]string{ 1119 "mask": "root", // FIXME replace with fieldmask, here we only want to get the root resourceid 1120 "path": ref.Path, 1121 "unique": "true", 1122 } 1123 if ref.ResourceId != nil { 1124 filters["storage_id"] = ref.ResourceId.StorageId 1125 filters["space_id"] = ref.ResourceId.SpaceId 1126 filters["opaque_id"] = ref.ResourceId.OpaqueId 1127 } 1128 1129 listReq := ®istry.ListStorageProvidersRequest{ 1130 Opaque: &typesv1beta1.Opaque{}, 1131 } 1132 sdk.EncodeOpaqueMap(listReq.Opaque, filters) 1133 1134 return s.findProvider(ctx, listReq) 1135 } 1136 1137 func (s *svc) findProvider(ctx context.Context, listReq *registry.ListStorageProvidersRequest) ([]*registry.ProviderInfo, error) { 1138 // lookup 1139 c, err := pool.GetStorageRegistryClient(s.c.StorageRegistryEndpoint) 1140 if err != nil { 1141 return nil, errors.Wrap(err, "gateway: error getting storage registry client") 1142 } 1143 res, err := c.ListStorageProviders(ctx, listReq) 1144 if err != nil { 1145 return nil, errors.Wrap(err, "gateway: error calling ListStorageProviders") 1146 } 1147 1148 if res.Status.Code != rpc.Code_CODE_OK { 1149 switch res.Status.Code { 1150 case rpc.Code_CODE_NOT_FOUND: 1151 // TODO use tombstone cache item? 1152 return nil, errtypes.NotFound("gateway: storage provider not found for reference:" + listReq.String()) 1153 case rpc.Code_CODE_PERMISSION_DENIED: 1154 return nil, errtypes.PermissionDenied("gateway: " + res.Status.Message + " for " + listReq.String() + " with code " + res.Status.Code.String()) 1155 case rpc.Code_CODE_INVALID_ARGUMENT, rpc.Code_CODE_FAILED_PRECONDITION, rpc.Code_CODE_OUT_OF_RANGE: 1156 return nil, errtypes.BadRequest("gateway: " + res.Status.Message + " for " + listReq.String() + " with code " + res.Status.Code.String()) 1157 case rpc.Code_CODE_UNIMPLEMENTED: 1158 return nil, errtypes.NotSupported("gateway: " + res.Status.Message + " for " + listReq.String() + " with code " + res.Status.Code.String()) 1159 default: 1160 return nil, status.NewErrorFromCode(res.Status.Code, "gateway") 1161 } 1162 } 1163 1164 if res.Providers == nil { 1165 return nil, errtypes.NotFound("gateway: provider is nil") 1166 } 1167 1168 return res.Providers, nil 1169 } 1170 1171 // unwrap takes a reference and builds a reference for the provider. can be absolute or relative to a root node 1172 func unwrap(ref *provider.Reference, mountPoint string, root *provider.ResourceId) *provider.Reference { 1173 if utils.IsAbsolutePathReference(ref) { 1174 providerRef := &provider.Reference{ 1175 Path: strings.TrimPrefix(ref.Path, mountPoint), 1176 } 1177 // if we have a root use it and make the path relative 1178 if root != nil { 1179 providerRef.ResourceId = root 1180 providerRef.Path = utils.MakeRelativePath(providerRef.Path) 1181 } 1182 return providerRef 1183 } 1184 1185 return &provider.Reference{ 1186 ResourceId: &provider.ResourceId{ 1187 StorageId: ref.GetResourceId().GetStorageId(), 1188 SpaceId: ref.GetResourceId().GetSpaceId(), 1189 OpaqueId: ref.GetResourceId().GetOpaqueId(), 1190 }, 1191 Path: ref.GetPath(), 1192 } 1193 } 1194 1195 func decodeSpaces(r *registry.ProviderInfo) []*provider.StorageSpace { 1196 spaces := []*provider.StorageSpace{} 1197 if r.Opaque != nil { 1198 if entry, ok := r.Opaque.Map["spaces"]; ok { 1199 switch entry.Decoder { 1200 case "json": 1201 _ = json.Unmarshal(entry.Value, &spaces) 1202 case "toml": 1203 _ = toml.Unmarshal(entry.Value, &spaces) 1204 case "xml": 1205 _ = xml.Unmarshal(entry.Value, &spaces) 1206 } 1207 } 1208 } 1209 if len(spaces) == 0 { 1210 // we need to convert the provider into a space, needed for the static registry 1211 spaces = append(spaces, &provider.StorageSpace{ 1212 Opaque: &typesv1beta1.Opaque{Map: map[string]*typesv1beta1.OpaqueEntry{ 1213 "path": { 1214 Decoder: "plain", 1215 Value: []byte(r.ProviderPath), 1216 }, 1217 }}, 1218 }) 1219 } 1220 return spaces 1221 } 1222 1223 func decodePath(s *provider.StorageSpace) (path string) { 1224 if s.Opaque != nil { 1225 if entry, ok := s.Opaque.Map["path"]; ok { 1226 switch entry.Decoder { 1227 case "plain": 1228 path = string(entry.Value) 1229 case "json": 1230 _ = json.Unmarshal(entry.Value, &path) 1231 case "toml": 1232 _ = toml.Unmarshal(entry.Value, &path) 1233 case "xml": 1234 _ = xml.Unmarshal(entry.Value, &path) 1235 } 1236 } 1237 } 1238 return 1239 }