package container import ( "context" "errors" "fmt" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" containerSvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/container" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" ) var errMissingUserID = errors.New("missing user ID") type morphExecutor struct { rdr Reader wrt Writer } // Reader is an interface of read-only container storage. type Reader interface { containercore.Source containercore.EACLSource // ContainersOf returns a list of container identifiers belonging // to the specified user of FrostFS system. Returns the identifiers // of all FrostFS containers if pointer to owner identifier is nil. ContainersOf(*user.ID) ([]cid.ID, error) } // Writer is an interface of container storage updater. type Writer interface { // Put stores specified container in the side chain. Put(containercore.Container) (*cid.ID, error) // Delete removes specified container from the side chain. Delete(containercore.RemovalWitness) error // PutEACL updates extended ACL table of specified container in the side chain. PutEACL(containercore.EACL) error } func NewExecutor(rdr Reader, wrt Writer) containerSvc.ServiceExecutor { return &morphExecutor{ rdr: rdr, wrt: wrt, } } func (s *morphExecutor) Put(_ context.Context, tokV2 *sessionV2.Token, body *container.PutRequestBody) (*container.PutResponseBody, error) { sigV2 := body.GetSignature() if sigV2 == nil { // TODO(@cthulhu-rider): #468 use "const" error return nil, errors.New("missing signature") } cnrV2 := body.GetContainer() if cnrV2 == nil { return nil, errors.New("missing container field") } var cnr containercore.Container err := cnr.Value.ReadFromV2(*cnrV2) if err != nil { return nil, fmt.Errorf("invalid container: %w", err) } err = cnr.Signature.ReadFromV2(*sigV2) if err != nil { return nil, fmt.Errorf("can't read signature: %w", err) } if tokV2 != nil { cnr.Session = new(session.Container) err := cnr.Session.ReadFromV2(*tokV2) if err != nil { return nil, fmt.Errorf("invalid session token: %w", err) } } idCnr, err := s.wrt.Put(cnr) if err != nil { return nil, err } var idCnrV2 refs.ContainerID idCnr.WriteToV2(&idCnrV2) res := new(container.PutResponseBody) res.SetContainerID(&idCnrV2) return res, nil } func (s *morphExecutor) Delete(_ context.Context, tokV2 *sessionV2.Token, body *container.DeleteRequestBody) (*container.DeleteResponseBody, error) { idV2 := body.GetContainerID() if idV2 == nil { return nil, errors.New("missing container ID") } var id cid.ID err := id.ReadFromV2(*idV2) if err != nil { return nil, fmt.Errorf("invalid container ID: %w", err) } var tok *session.Container if tokV2 != nil { tok = new(session.Container) err := tok.ReadFromV2(*tokV2) if err != nil { return nil, fmt.Errorf("invalid session token: %w", err) } } var rmWitness containercore.RemovalWitness rmWitness.ContainerID = id rmWitness.Signature = body.GetSignature() rmWitness.SessionToken = tok err = s.wrt.Delete(rmWitness) if err != nil { return nil, err } return new(container.DeleteResponseBody), nil } func (s *morphExecutor) Get(_ context.Context, body *container.GetRequestBody) (*container.GetResponseBody, error) { idV2 := body.GetContainerID() if idV2 == nil { return nil, errors.New("missing container ID") } var id cid.ID err := id.ReadFromV2(*idV2) if err != nil { return nil, fmt.Errorf("invalid container ID: %w", err) } cnr, err := s.rdr.Get(id) if err != nil { return nil, err } sigV2 := new(refs.Signature) cnr.Signature.WriteToV2(sigV2) var tokV2 *sessionV2.Token if cnr.Session != nil { tokV2 = new(sessionV2.Token) cnr.Session.WriteToV2(tokV2) } var cnrV2 container.Container cnr.Value.WriteToV2(&cnrV2) res := new(container.GetResponseBody) res.SetContainer(&cnrV2) res.SetSignature(sigV2) res.SetSessionToken(tokV2) return res, nil } func (s *morphExecutor) List(_ context.Context, body *container.ListRequestBody) (*container.ListResponseBody, error) { idV2 := body.GetOwnerID() if idV2 == nil { return nil, errMissingUserID } var id user.ID err := id.ReadFromV2(*idV2) if err != nil { return nil, fmt.Errorf("invalid user ID: %w", err) } cnrs, err := s.rdr.ContainersOf(&id) if err != nil { return nil, err } cidList := make([]refs.ContainerID, len(cnrs)) for i := range cnrs { cnrs[i].WriteToV2(&cidList[i]) } res := new(container.ListResponseBody) res.SetContainerIDs(cidList) return res, nil } func (s *morphExecutor) SetExtendedACL(_ context.Context, tokV2 *sessionV2.Token, body *container.SetExtendedACLRequestBody) (*container.SetExtendedACLResponseBody, error) { sigV2 := body.GetSignature() if sigV2 == nil { // TODO(@cthulhu-rider): #468 use "const" error return nil, errors.New("missing signature") } eaclInfo := containercore.EACL{ Value: eaclSDK.NewTableFromV2(body.GetEACL()), } err := eaclInfo.Signature.ReadFromV2(*sigV2) if err != nil { return nil, fmt.Errorf("can't read signature: %w", err) } if tokV2 != nil { eaclInfo.Session = new(session.Container) err := eaclInfo.Session.ReadFromV2(*tokV2) if err != nil { return nil, fmt.Errorf("invalid session token: %w", err) } } err = s.wrt.PutEACL(eaclInfo) if err != nil { return nil, err } return new(container.SetExtendedACLResponseBody), nil } func (s *morphExecutor) GetExtendedACL(_ context.Context, body *container.GetExtendedACLRequestBody) (*container.GetExtendedACLResponseBody, error) { idV2 := body.GetContainerID() if idV2 == nil { return nil, errors.New("missing container ID") } var id cid.ID err := id.ReadFromV2(*idV2) if err != nil { return nil, fmt.Errorf("invalid container ID: %w", err) } eaclInfo, err := s.rdr.GetEACL(id) if err != nil { return nil, err } var sigV2 refs.Signature eaclInfo.Signature.WriteToV2(&sigV2) var tokV2 *sessionV2.Token if eaclInfo.Session != nil { tokV2 = new(sessionV2.Token) eaclInfo.Session.WriteToV2(tokV2) } res := new(container.GetExtendedACLResponseBody) res.SetEACL(eaclInfo.Value.ToV2()) res.SetSignature(&sigV2) res.SetSessionToken(tokV2) return res, nil }