Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Changelog for NeoFS Node
- SN can respond with `CONTAINER_LOCKED` status now (#3708)
- `session create-v2` cli command to create new session token v2 (#3750)
- SN now support raw GET/HEAD/RANGE requests in EC containers (#3756)
- IR now serves `setAttribute` and `removeAttribute` methods of Container contract (#3733)
- SN now serves `ContainerService`'s `SetAttribute` and `RemoveAttribute` RPC (#3733)
- CLI `set-attribute` and `remove-attribute commands to `container` section (#3733)

### Fixed
- IR panics at graceful shutdown (#3706)
Expand Down Expand Up @@ -44,9 +47,10 @@ Changelog for NeoFS Node
- Graveyard from metabase (#3744)

### Updated
- `github.com/nspcc-dev/neofs-contract` module to `v0.25.2-0.20251219150129-498a820b9d6b` (#3670, #3746)
- `github.com/nspcc-dev/neofs-sdk-go` module to `v1.0.0-rc.16.0.20251222201515-923817fd7d13` (#3711, #3750)
- `github.com/nspcc-dev/neofs-contract` module to `v0.25.2-0.20251223162726-c0cf83ca5e42` (#3670, #3746, #3733)
- `github.com/nspcc-dev/neofs-sdk-go` module to `v1.0.0-rc.16.0.20251224112927-a50d7e9c925a` (#3711, #3750, #3733)
- `github.com/nspcc-dev/locode-db` module to `v0.8.2` (#3729)
- `github.com/nspcc-dev/neo-go` module to `v0.114.1-0.20251222145711-e174185e133e` (#3733)

### Updating from v0.50.2
Please remove the following deprecated configuration options from IR config:
Expand Down
231 changes: 231 additions & 0 deletions cmd/neofs-cli/modules/container/attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package container

import (
"errors"
"fmt"
"time"

internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key"
"github.com/nspcc-dev/neofs-sdk-go/client"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/spf13/cobra"
)

// Set attribute command flags.
const (
setAttributeNameFlag = "attribute"
setAttributeValueFlag = "value"
setAttributeValidForFlag = "valid-for"
)

// Set attribute command defaults.
const (
defaultSetAttributeValidFor = time.Minute
)

var setAttributeFlagVars struct {
id string
attribute string
value string
validFor time.Duration
}

var setAttributeCmd = &cobra.Command{
Use: "set-attribute",
Short: "Set attribute for container",
Long: "Set attribute for container",
Args: cobra.NoArgs,
RunE: setAttribute,
}

func initSetAttributeCmd() {
commonflags.Init(setAttributeCmd)

flags := setAttributeCmd.Flags()
flags.StringVar(&setAttributeFlagVars.id, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
flags.StringVar(&setAttributeFlagVars.attribute, setAttributeNameFlag, "", "attribute to be set")
flags.StringVar(&setAttributeFlagVars.value, setAttributeValueFlag, "", "value for the attribute")
flags.DurationVar(&setAttributeFlagVars.validFor, setAttributeValidForFlag, defaultSetAttributeValidFor, "request validity duration")

for _, f := range []string{
commonflags.CIDFlag,
setAttributeNameFlag,
setAttributeValueFlag,
} {
if err := setAttributeCmd.MarkFlagRequired(f); err != nil {
panic(fmt.Sprintf("failed to mark flag %s required: %v", f, err))
}
}
}

func setAttribute(cmd *cobra.Command, _ []string) error {
if setAttributeFlagVars.validFor <= 0 {
return fmt.Errorf("non-positive request validity duration %d", setAttributeFlagVars.validFor)
}

id, err := cid.DecodeString(setAttributeFlagVars.id)
if err != nil {
return fmt.Errorf("invalid container ID: %w", err)
}

sessionToken, err := getSession(cmd)
if err != nil {
return err
}

pk, err := key.Get(cmd)
if err != nil {
return err
}
signer := (*neofsecdsa.SignerRFC6979)(pk)

ctx, cancel := getAwaitContext(cmd)
defer cancel()

cli, err := internalclient.GetSDKClientByFlag(ctx, commonflags.RPC)
if err != nil {
return err
}
defer cli.Close()

prm := client.SetContainerAttributeParameters{
ID: id,
Attribute: setAttributeFlagVars.attribute,
Value: setAttributeFlagVars.value,
ValidUntil: time.Now().Add(setAttributeFlagVars.validFor),
}

signedPrm := client.GetSignedSetContainerAttributeParameters(prm)

var prmSig neofscrypto.Signature
if err := prmSig.Calculate(signer, signedPrm); err != nil {
return fmt.Errorf("failed to sign request parameters: %w", err)
}

var opts client.SetContainerAttributeOptions
if sessionToken != nil {
opts.AttachSessionTokenV1(*sessionToken)
}

err = cli.SetContainerAttribute(ctx, prm, prmSig, opts)
if err != nil {
if errors.Is(err, apistatus.ErrContainerAwaitTimeout) {
err = common.ErrAwaitTimeout
}
return fmt.Errorf("client error: %w", err)
}

cmd.Println("Attribute successfully set.")

return nil
}

// Remove attribute command flags.
const (
removeAttributeNameFlag = "attribute"
removeAttributeValidForFlag = "valid-for"
)

// Remove attribute command defaults.
const (
defaultRemoveAttributeValidFor = time.Minute
)

var removeAttributeFlagVars struct {
id string
attribute string
validFor time.Duration
}

var removeAttributeCmd = &cobra.Command{
Use: "remove-attribute",
Short: "Remove container attribute",
Long: "Remove container attribute",
Args: cobra.NoArgs,
RunE: removeAttribute,
}

func initRemoveAttributeCmd() {
commonflags.Init(removeAttributeCmd)

flags := removeAttributeCmd.Flags()
flags.StringVar(&removeAttributeFlagVars.id, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
flags.StringVar(&removeAttributeFlagVars.attribute, removeAttributeNameFlag, "", "attribute to be set")
flags.DurationVar(&removeAttributeFlagVars.validFor, removeAttributeValidForFlag, defaultRemoveAttributeValidFor, "request validity duration")

for _, f := range []string{
commonflags.CIDFlag,
removeAttributeNameFlag,
} {
if err := removeAttributeCmd.MarkFlagRequired(f); err != nil {
panic(fmt.Sprintf("failed to mark flag %s required: %v", f, err))
}
}
}

func removeAttribute(cmd *cobra.Command, _ []string) error {
if removeAttributeFlagVars.validFor <= 0 {
return fmt.Errorf("non-positive request validity duration %d", removeAttributeFlagVars.validFor)
}

id, err := cid.DecodeString(removeAttributeFlagVars.id)
if err != nil {
return fmt.Errorf("invalid container ID: %w", err)
}

sessionToken, err := getSession(cmd)
if err != nil {
return err
}

pk, err := key.Get(cmd)
if err != nil {
return err
}
signer := (*neofsecdsa.SignerRFC6979)(pk)

ctx, cancel := getAwaitContext(cmd)
defer cancel()

cli, err := internalclient.GetSDKClientByFlag(ctx, commonflags.RPC)
if err != nil {
return err
}
defer cli.Close()

prm := client.RemoveContainerAttributeParameters{
ID: id,
Attribute: removeAttributeFlagVars.attribute,
ValidUntil: time.Now().Add(removeAttributeFlagVars.validFor),
}

signedPrm := client.GetSignedRemoveContainerAttributeParameters(prm)

var prmSig neofscrypto.Signature
if err := prmSig.Calculate(signer, signedPrm); err != nil {
return fmt.Errorf("failed to sign request parameters: %w", err)
}

var opts client.RemoveContainerAttributeOptions
if sessionToken != nil {
opts.AttachSessionTokenV1(*sessionToken)
}

err = cli.RemoveContainerAttribute(ctx, prm, prmSig, opts)
if err != nil {
if errors.Is(err, apistatus.ErrContainerAwaitTimeout) {
err = common.ErrAwaitTimeout
}
return fmt.Errorf("client error: %w", err)
}

cmd.Println("Attribute successfully removed.")

return nil
}
6 changes: 6 additions & 0 deletions cmd/neofs-cli/modules/container/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func init() {
getExtendedACLCmd,
setExtendedACLCmd,
containerNodesCmd,
setAttributeCmd,
removeAttributeCmd,
}

Cmd.AddCommand(containerChildCommand...)
Expand All @@ -40,6 +42,8 @@ func init() {
initContainerGetEACLCmd()
initContainerSetEACLCmd()
initContainerNodesCmd()
initSetAttributeCmd()
initRemoveAttributeCmd()

for _, containerCommand := range containerChildCommand {
commonflags.InitAPI(containerCommand)
Expand All @@ -52,6 +56,8 @@ func init() {
{createContainerCmd, "PUT"},
{deleteContainerCmd, "DELETE"},
{setExtendedACLCmd, "SETEACL"},
{setAttributeCmd, "SETATTRIBUTE"},
{removeAttributeCmd, "REMOVEATTRIBUTE"},
} {
commonflags.InitSession(el.cmd, "container "+el.verb)
}
Expand Down
18 changes: 18 additions & 0 deletions cmd/neofs-node/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,24 @@ func (x *containersInChain) PutEACL(ctx context.Context, eACL eacl.Table, pub, s
return err
}

func (x *containersInChain) SetAttribute(ctx context.Context, cnr cid.ID, attr, val string, validUntil uint64, pub, sig, sessionToken []byte) error {
err := x.cCli.SetAttribute(ctx, cnr, attr, val, validUntil, pub, sig, sessionToken)
if errors.Is(err, client.ErrTxAwaitTimeout) {
err = apistatus.ErrContainerAwaitTimeout
}

return err
}

func (x *containersInChain) RemoveAttribute(ctx context.Context, cnr cid.ID, attr string, validUntil uint64, pub, sig, sessionToken []byte) error {
err := x.cCli.RemoveAttribute(ctx, cnr, attr, validUntil, pub, sig, sessionToken)
if errors.Is(err, client.ErrTxAwaitTimeout) {
err = apistatus.ErrContainerAwaitTimeout
}

return err
}

type containerPresenceChecker struct{ src containerCore.Source }

// Exists implements [meta.Containers].
Expand Down
2 changes: 2 additions & 0 deletions docs/cli-commands/neofs-cli_container.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ Operations with containers
* [neofs-cli container list](neofs-cli_container_list.md) - List all created containers
* [neofs-cli container list-objects](neofs-cli_container_list-objects.md) - List existing objects in container
* [neofs-cli container nodes](neofs-cli_container_nodes.md) - Show nodes for container
* [neofs-cli container remove-attribute](neofs-cli_container_remove-attribute.md) - Remove container attribute
* [neofs-cli container set-attribute](neofs-cli_container_set-attribute.md) - Set attribute for container
* [neofs-cli container set-eacl](neofs-cli_container_set-eacl.md) - Set new extended ACL table for container

40 changes: 40 additions & 0 deletions docs/cli-commands/neofs-cli_container_remove-attribute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## neofs-cli container remove-attribute

Remove container attribute

### Synopsis

Remove contaier attribute

```
neofs-cli container remove-attribute [flags]
```

### Options

```
--address string Address of wallet account
--attribute string attribute to be set
--cid string Container ID.
-g, --generate-key Generate new private key
-h, --help help for remove-attribute
-r, --rpc-endpoint string Remote node address (as 'multiaddr' or '<host>:<port>')
--session string Filepath to a JSON- or binary-encoded token of the container REMOVEATTRIBUTE session
-t, --timeout duration Timeout for the operation (default 15s)
--ttl uint32 TTL value in request meta header (default 2)
--valid-for duration request validity duration (default 1m0s)
-w, --wallet string Path to the wallet
-x, --xhdr strings Request X-Headers in form of Key=Value
```

### Options inherited from parent commands

```
-c, --config string Config file (default is $HOME/.config/neofs-cli/config.yaml)
-v, --verbose Verbose output
```

### SEE ALSO

* [neofs-cli container](neofs-cli_container.md) - Operations with containers

41 changes: 41 additions & 0 deletions docs/cli-commands/neofs-cli_container_set-attribute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## neofs-cli container set-attribute

Set attribute for container

### Synopsis

Set attribute for container

```
neofs-cli container set-attribute [flags]
```

### Options

```
--address string Address of wallet account
--attribute string attribute to be set
--cid string Container ID.
-g, --generate-key Generate new private key
-h, --help help for set-attribute
-r, --rpc-endpoint string Remote node address (as 'multiaddr' or '<host>:<port>')
--session string Filepath to a JSON- or binary-encoded token of the container SETATTRIBUTE session
-t, --timeout duration Timeout for the operation (default 15s)
--ttl uint32 TTL value in request meta header (default 2)
--valid-for duration request validity duration (default 1m0s)
--value string value for the attribute
-w, --wallet string Path to the wallet
-x, --xhdr strings Request X-Headers in form of Key=Value
```

### Options inherited from parent commands

```
-c, --config string Config file (default is $HOME/.config/neofs-cli/config.yaml)
-v, --verbose Verbose output
```

### SEE ALSO

* [neofs-cli container](neofs-cli_container.md) - Operations with containers

Loading
Loading