diff --git a/api/layer/cors.go b/api/layer/cors.go index 9c5137223..2e05f9f2e 100644 --- a/api/layer/cors.go +++ b/api/layer/cors.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/xml" + errorsStd "errors" "fmt" "io" @@ -35,6 +36,11 @@ func (n *layer) PutBucketCORS(ctx context.Context, p *PutCORSParams) error { return err } + ids, nodeIds, err := n.treeService.GetBucketCORS(ctx, &p.BktInfo.CID, false) + if err != nil && !errorsStd.Is(err, ErrNodeNotFound) { + return err + } + s := &PutSystemObjectParams{ BktInfo: p.BktInfo, ObjName: p.BktInfo.CORSObjectName(), @@ -44,10 +50,27 @@ func (n *layer) PutBucketCORS(ctx context.Context, p *PutCORSParams) error { Size: int64(buf.Len()), } - if _, err := n.putSystemObjectIntoNeoFS(ctx, s); err != nil { + obj, err := n.putSystemObjectIntoNeoFS(ctx, s) + if err != nil { return fmt.Errorf("put system object: %w", err) } + if err = n.treeService.PutBucketCORS(ctx, &p.BktInfo.CID, &obj.ID); err != nil { + return err + } + + for i := 0; i < len(ids); i++ { + if err = n.objectDelete(ctx, p.BktInfo, *ids[i]); err != nil { + n.log.Error("couldn't delete cors object", zap.Error(err), + zap.String("cnrID", p.BktInfo.CID.EncodeToString()), + zap.String("bucket name", p.BktInfo.Name), + zap.String("objID", ids[i].EncodeToString())) + } + if err = n.treeService.DeleteBucketCORS(ctx, &p.BktInfo.CID, nodeIds[i]); err != nil { + return err + } + } + if err := n.systemCache.PutCORS(systemObjectKey(p.BktInfo, s.ObjName), cors); err != nil { n.log.Error("couldn't cache system object", zap.Error(err)) } @@ -58,7 +81,7 @@ func (n *layer) PutBucketCORS(ctx context.Context, p *PutCORSParams) error { func (n *layer) GetBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) (*data.CORSConfiguration, error) { cors, err := n.getCORS(ctx, bktInfo, bktInfo.CORSObjectName()) if err != nil { - if errors.IsS3Error(err, errors.ErrNoSuchKey) { + if errorsStd.Is(err, ErrNodeNotFound) { return nil, errors.GetAPIError(errors.ErrNoSuchCORSConfiguration) } return nil, err @@ -68,7 +91,23 @@ func (n *layer) GetBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) (*d } func (n *layer) DeleteBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) error { - return n.DeleteSystemObject(ctx, bktInfo, bktInfo.CORSObjectName()) + ids, nodeIds, err := n.treeService.GetBucketCORS(ctx, &bktInfo.CID, false) + if err != nil && !errorsStd.Is(err, ErrNodeNotFound) { + return err + } + + for i := 0; i < len(ids); i++ { + if err = n.objectDelete(ctx, bktInfo, *ids[i]); err != nil { + return err + } + if err = n.treeService.DeleteBucketCORS(ctx, &bktInfo.CID, nodeIds[i]); err != nil { + return err + } + } + + n.systemCache.Delete(systemObjectKey(bktInfo, bktInfo.CORSObjectName())) + + return nil } func checkCORS(cors *data.CORSConfiguration) error { diff --git a/api/layer/system_object.go b/api/layer/system_object.go index 65dfd6f8f..3530416d7 100644 --- a/api/layer/system_object.go +++ b/api/layer/system_object.go @@ -181,8 +181,15 @@ func (n *layer) getCORS(ctx context.Context, bkt *data.BucketInfo, sysName strin if cors := n.systemCache.GetCORS(systemObjectKey(bkt, sysName)); cors != nil { return cors, nil } + ids, _, err := n.treeService.GetBucketCORS(ctx, &bkt.CID, true) + if err != nil { + return nil, err + } + if len(ids) == 0 { + return nil, errors.GetAPIError(errors.ErrNoSuchCORSConfiguration) + } - obj, err := n.getSystemObjectFromNeoFS(ctx, bkt, sysName) + obj, err := n.objectGet(ctx, bkt, *ids[0]) if err != nil { return nil, err } diff --git a/api/layer/tree_service.go b/api/layer/tree_service.go index 586a8ada5..a0af570cb 100644 --- a/api/layer/tree_service.go +++ b/api/layer/tree_service.go @@ -22,6 +22,10 @@ type TreeService interface { GetNotificationConfigurationNodes(ctx context.Context, cnrID *cid.ID, latestOnly bool) ([]*oid.ID, []uint64, error) PutNotificationConfigurationNode(ctx context.Context, cnrID *cid.ID, objID *oid.ID) error DeleteNotificationConfigurationNode(ctx context.Context, cnrID *cid.ID, nodeID uint64) error + + GetBucketCORS(ctx context.Context, cnrID *cid.ID, latestOnly bool) ([]*oid.ID, []uint64, error) + PutBucketCORS(ctx context.Context, cnrID *cid.ID, objID *oid.ID) error + DeleteBucketCORS(ctx context.Context, cnrID *cid.ID, nodeID uint64) error } // ErrNodeNotFound is returned from Tree service in case of not found error. diff --git a/internal/neofs/tree.go b/internal/neofs/tree.go index 9b1cbe93e..16b322f9e 100644 --- a/internal/neofs/tree.go +++ b/internal/neofs/tree.go @@ -39,8 +39,10 @@ const ( settingsFileName = "bucket-settings" notifConfFileName = "bucket-notifications" + corsFilename = "bucket-cors" notifTreeID = "notifications" + corsTreeID = "cors" ) // NewTreeClient creates instance of TreeClient using provided address and create grpc connection. @@ -159,6 +161,36 @@ func (c *TreeClient) DeleteNotificationConfigurationNode(ctx context.Context, cn return c.removeNode(ctx, cnrID, notifTreeID, nodeID) } +func (c *TreeClient) GetBucketCORS(ctx context.Context, cnrID *cid.ID, latestOnly bool) ([]*oid.ID, []uint64, error) { + nodes, err := c.getSystemNodesWithOID(ctx, cnrID, corsTreeID, corsFilename, []string{}, latestOnly) + if err != nil { + return nil, nil, err + } + + ids := make([]*oid.ID, 0, len(nodes)) + nodeIds := make([]uint64, 0, len(nodes)) + + for _, n := range nodes { + ids = append(ids, n.ObjID) + nodeIds = append(nodeIds, n.ID) + } + + return ids, nodeIds, nil +} + +func (c *TreeClient) PutBucketCORS(ctx context.Context, cnrID *cid.ID, objID *oid.ID) error { + meta := make(map[string]string) + meta[systemNameKV] = corsFilename + meta[oidKv] = objID.EncodeToString() + + _, err := c.addNode(ctx, cnrID, corsTreeID, 0, meta) + return err +} + +func (c *TreeClient) DeleteBucketCORS(ctx context.Context, cnrID *cid.ID, nodeID uint64) error { + return c.removeNode(ctx, cnrID, corsTreeID, nodeID) +} + func (c *TreeClient) Close() error { if c.conn != nil { return c.conn.Close()