2020-08-04 14:46:12 +00:00
package cmd
import (
2020-10-20 12:21:17 +00:00
"bytes"
"encoding/json"
2021-05-18 08:12:51 +00:00
"errors"
2020-08-04 14:46:12 +00:00
"fmt"
2020-10-14 12:51:22 +00:00
"os"
"strconv"
"strings"
"time"
2020-08-04 14:46:12 +00:00
2020-10-14 12:51:22 +00:00
"github.com/google/uuid"
2021-10-28 14:48:46 +00:00
internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client"
2021-07-02 05:47:00 +00:00
"github.com/nspcc-dev/neofs-node/pkg/core/version"
2021-11-10 07:08:33 +00:00
"github.com/nspcc-dev/neofs-sdk-go/acl"
"github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/object"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/policy"
"github.com/nspcc-dev/neofs-sdk-go/session"
2021-11-26 17:46:07 +00:00
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
2021-11-10 07:08:33 +00:00
versionSDK "github.com/nspcc-dev/neofs-sdk-go/version"
2020-08-04 14:46:12 +00:00
"github.com/spf13/cobra"
)
2020-10-14 12:51:22 +00:00
const (
2020-10-16 09:29:57 +00:00
attributeDelimiter = "="
2020-10-14 12:51:22 +00:00
awaitTimeout = 120 // in seconds
)
2021-05-19 13:12:01 +00:00
// keywords of predefined basic ACL values
const (
basicACLPrivate = "private"
2021-05-19 13:16:05 +00:00
basicACLReadOnly = "public-read"
basicACLPublic = "public-read-write"
2021-12-30 09:07:52 +00:00
basicACLAppend = "public-append"
basicACLNoFinalPrivate = "eacl-private"
basicACLNoFinalReadOnly = "eacl-public-read"
basicACLNoFinalPublic = "eacl-public-read-write"
basicACLNoFinalAppend = "eacl-public-append"
2021-05-19 13:12:01 +00:00
)
2022-01-13 15:01:50 +00:00
var wellKnownBasicACL = map [ string ] acl . BasicACL {
2021-12-30 09:07:52 +00:00
basicACLPublic : acl . PublicBasicRule ,
basicACLPrivate : acl . PrivateBasicRule ,
basicACLReadOnly : acl . ReadOnlyBasicRule ,
basicACLAppend : acl . PublicAppendRule ,
basicACLNoFinalPublic : acl . EACLPublicBasicRule ,
basicACLNoFinalPrivate : acl . EACLPrivateBasicRule ,
basicACLNoFinalReadOnly : acl . EACLReadOnlyBasicRule ,
basicACLNoFinalAppend : acl . EACLPublicAppendRule ,
}
2021-05-28 13:28:50 +00:00
const sessionTokenFlag = "session"
// path to a file with encoded session token
var sessionTokenPath string
2020-10-14 12:51:22 +00:00
var (
containerOwner string
2020-11-03 13:44:50 +00:00
containerACL string
containerNonce string
containerPolicy string
containerAttributes [ ] string
containerAwait bool
containerName string
containerNoTimestamp bool
2021-11-26 17:46:07 +00:00
containerSubnet string
2020-10-14 12:51:22 +00:00
containerID string
2020-10-15 15:00:53 +00:00
containerPathFrom string
containerPathTo string
2020-10-20 12:21:17 +00:00
containerJSON bool
eaclPathFrom string
2020-10-14 12:51:22 +00:00
)
2021-07-06 12:27:54 +00:00
var (
2021-07-08 17:16:14 +00:00
errDeleteTimeout = errors . New ( "timeout: container has not been removed from sidechain" )
errCreateTimeout = errors . New ( "timeout: container has not been persisted on sidechain" )
errSetEACLTimeout = errors . New ( "timeout: EACL has not been persisted on sidechain" )
errUnsupportedEACLFormat = errors . New ( "unsupported eACL format" )
2021-07-06 12:27:54 +00:00
)
2020-08-04 14:46:12 +00:00
// containerCmd represents the container command
var containerCmd = & cobra . Command {
Use : "container" ,
2020-10-14 12:51:22 +00:00
Short : "Operations with containers" ,
Long : "Operations with containers" ,
2021-09-27 15:36:14 +00:00
PersistentPreRun : func ( cmd * cobra . Command , args [ ] string ) {
// bind exactly that cmd's flags to
// the viper before execution
bindCommonFlags ( cmd )
2021-10-07 10:37:57 +00:00
bindAPIFlags ( cmd )
2021-09-27 15:36:14 +00:00
} ,
2020-10-14 12:51:22 +00:00
}
var listContainersCmd = & cobra . Command {
Use : "list" ,
Short : "List all created containers" ,
Long : "List all created containers" ,
2021-06-20 23:02:56 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2021-10-28 14:48:46 +00:00
var oid * owner . ID
2020-10-14 12:51:22 +00:00
2021-04-21 12:27:32 +00:00
key , err := getKey ( )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-14 12:51:22 +00:00
2021-04-21 12:27:32 +00:00
if containerOwner == "" {
2022-01-21 12:15:10 +00:00
oid = owner . NewIDFromPublicKey ( & key . PublicKey )
2021-03-15 11:23:04 +00:00
} else {
oid , err = ownerFromString ( containerOwner )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-14 12:51:22 +00:00
}
2021-10-28 14:48:46 +00:00
var prm internalclient . ListContainersPrm
prepareAPIClientWithKey ( cmd , key , & prm )
2022-01-21 16:53:06 +00:00
prm . SetAccount ( * oid )
2021-10-28 14:48:46 +00:00
res , err := internalclient . ListContainers ( prm )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "rpc error: %w" , err ) )
2020-10-14 12:51:22 +00:00
// print to stdout
2021-10-28 14:48:46 +00:00
prettyPrintContainerList ( cmd , res . IDList ( ) )
2020-10-14 12:51:22 +00:00
} ,
}
var createContainerCmd = & cobra . Command {
Use : "create" ,
Short : "Create new container" ,
Long : ` Create new container and register it in the NeoFS .
It will be stored in sidechain when inner ring will accepts it . ` ,
2021-06-20 23:02:56 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2020-10-14 12:51:22 +00:00
placementPolicy , err := parseContainerPolicy ( containerPolicy )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-14 12:51:22 +00:00
2021-11-26 17:46:07 +00:00
subnetID , err := parseSubnetID ( containerSubnet )
exitOnErr ( cmd , errf ( "could not parse subnetID: %w" , err ) )
placementPolicy . SetSubnetID ( subnetID )
2020-10-14 12:51:22 +00:00
attributes , err := parseAttributes ( containerAttributes )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-14 12:51:22 +00:00
basicACL , err := parseBasicACL ( containerACL )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-14 12:51:22 +00:00
nonce , err := parseNonce ( containerNonce )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-14 12:51:22 +00:00
2021-06-03 14:38:33 +00:00
tok , err := getSessionToken ( sessionTokenPath )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2021-05-28 13:28:50 +00:00
2022-01-21 16:53:06 +00:00
key , err := getKey ( )
exitOnErr ( cmd , err )
var idOwner * owner . ID
if idOwner = tok . OwnerID ( ) ; idOwner == nil {
idOwner = owner . NewIDFromPublicKey ( & key . PublicKey )
}
2020-10-14 12:51:22 +00:00
cnr := container . New ( )
2022-01-21 16:53:06 +00:00
cnr . SetVersion ( versionSDK . Current ( ) )
2020-11-16 09:43:52 +00:00
cnr . SetPlacementPolicy ( placementPolicy )
2020-10-14 12:51:22 +00:00
cnr . SetBasicACL ( basicACL )
cnr . SetAttributes ( attributes )
2020-12-24 10:20:20 +00:00
cnr . SetNonceUUID ( nonce )
2021-05-28 13:28:50 +00:00
cnr . SetSessionToken ( tok )
2022-01-21 16:53:06 +00:00
cnr . SetOwnerID ( idOwner )
2020-10-14 12:51:22 +00:00
2021-10-28 14:48:46 +00:00
var (
putPrm internalclient . PutContainerPrm
getPrm internalclient . GetContainerPrm
)
2022-01-21 16:53:06 +00:00
prepareAPIClientWithKey ( cmd , key , & putPrm , & getPrm )
putPrm . SetContainer ( * cnr )
if tok != nil {
putPrm . SetSessionToken ( * tok )
}
2021-10-28 14:48:46 +00:00
res , err := internalclient . PutContainer ( putPrm )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "rpc error: %w" , err ) )
2020-10-14 12:51:22 +00:00
2021-10-28 14:48:46 +00:00
id := res . ID ( )
2021-06-20 23:02:56 +00:00
cmd . Println ( "container ID:" , id )
2020-10-14 12:51:22 +00:00
if containerAwait {
2021-06-20 23:02:56 +00:00
cmd . Println ( "awaiting..." )
2020-10-14 12:51:22 +00:00
2022-01-21 16:53:06 +00:00
getPrm . SetContainer ( * id )
2021-10-28 14:48:46 +00:00
2020-10-14 12:51:22 +00:00
for i := 0 ; i < awaitTimeout ; i ++ {
time . Sleep ( 1 * time . Second )
2021-10-28 14:48:46 +00:00
_ , err := internalclient . GetContainer ( getPrm )
2020-10-14 12:51:22 +00:00
if err == nil {
2021-06-20 23:02:56 +00:00
cmd . Println ( "container has been persisted on sidechain" )
return
2020-10-14 12:51:22 +00:00
}
}
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errCreateTimeout )
2020-10-14 12:51:22 +00:00
}
} ,
}
var deleteContainerCmd = & cobra . Command {
Use : "delete" ,
Short : "Delete existing container" ,
Long : ` Delete existing container .
Only owner of the container has a permission to remove container . ` ,
2021-06-20 23:02:56 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2020-10-14 12:51:22 +00:00
id , err := parseContainerID ( containerID )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-14 12:51:22 +00:00
2021-06-03 14:38:33 +00:00
tok , err := getSessionToken ( sessionTokenPath )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2021-05-28 13:28:50 +00:00
2021-10-28 14:48:46 +00:00
var (
delPrm internalclient . DeleteContainerPrm
getPrm internalclient . GetContainerPrm
)
2021-05-28 13:28:50 +00:00
2021-10-28 14:48:46 +00:00
prepareAPIClient ( cmd , & delPrm , & getPrm )
2022-01-21 16:53:06 +00:00
delPrm . SetContainer ( * id )
if tok != nil {
delPrm . SetSessionToken ( * tok )
}
2021-05-28 13:28:50 +00:00
2021-10-28 14:48:46 +00:00
_ , err = internalclient . DeleteContainer ( delPrm )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "rpc error: %w" , err ) )
2020-10-14 12:51:22 +00:00
2021-06-20 23:02:56 +00:00
cmd . Println ( "container delete method invoked" )
2020-10-14 12:51:22 +00:00
if containerAwait {
2021-06-20 23:02:56 +00:00
cmd . Println ( "awaiting..." )
2020-10-14 12:51:22 +00:00
2022-01-21 16:53:06 +00:00
getPrm . SetContainer ( * id )
2021-10-28 14:48:46 +00:00
2020-10-14 12:51:22 +00:00
for i := 0 ; i < awaitTimeout ; i ++ {
time . Sleep ( 1 * time . Second )
2021-10-28 14:48:46 +00:00
_ , err := internalclient . GetContainer ( getPrm )
2020-10-14 12:51:22 +00:00
if err != nil {
2021-06-20 23:02:56 +00:00
cmd . Println ( "container has been removed:" , containerID )
return
2020-10-14 12:51:22 +00:00
}
}
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errDeleteTimeout )
2020-10-14 12:51:22 +00:00
}
2020-08-04 14:46:12 +00:00
} ,
}
2020-10-15 14:54:33 +00:00
var listContainerObjectsCmd = & cobra . Command {
Use : "list-objects" ,
Short : "List existing objects in container" ,
Long : ` List existing objects in container ` ,
2021-06-20 23:02:56 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2020-10-15 14:54:33 +00:00
id , err := parseContainerID ( containerID )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-15 14:54:33 +00:00
filters := new ( object . SearchFilters )
filters . AddRootFilter ( ) // search only user created objects
2021-10-28 14:48:46 +00:00
var prm internalclient . SearchObjectsPrm
2020-10-15 14:54:33 +00:00
2021-10-28 14:48:46 +00:00
prepareSessionPrm ( cmd , & prm )
prepareObjectPrm ( cmd , & prm )
prm . SetContainerID ( id )
prm . SetFilters ( * filters )
res , err := internalclient . SearchObjects ( prm )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "rpc error: %w" , err ) )
2020-10-15 14:54:33 +00:00
2021-10-28 14:48:46 +00:00
objectIDs := res . IDList ( )
2020-10-15 14:54:33 +00:00
for i := range objectIDs {
2021-06-20 23:02:56 +00:00
cmd . Println ( objectIDs [ i ] )
2020-10-15 14:54:33 +00:00
}
} ,
}
2020-10-15 15:00:53 +00:00
var getContainerInfoCmd = & cobra . Command {
Use : "get" ,
Short : "Get container field info" ,
Long : ` Get container field info ` ,
2021-06-20 23:02:56 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2021-10-28 14:48:46 +00:00
var cnr * container . Container
2020-10-15 15:00:53 +00:00
if containerPathFrom != "" {
2021-06-28 14:01:31 +00:00
data , err := os . ReadFile ( containerPathFrom )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "can't read file: %w" , err ) )
2020-10-15 15:00:53 +00:00
2020-11-16 10:26:35 +00:00
cnr = container . New ( )
2021-07-06 12:27:54 +00:00
err = cnr . Unmarshal ( data )
exitOnErr ( cmd , errf ( "can't unmarshal container: %w" , err ) )
2020-10-15 15:00:53 +00:00
} else {
2021-10-28 14:48:46 +00:00
id , err := parseContainerID ( containerID )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2021-04-21 12:27:32 +00:00
2021-10-28 14:48:46 +00:00
var prm internalclient . GetContainerPrm
2020-10-15 15:00:53 +00:00
2021-10-28 14:48:46 +00:00
prepareAPIClient ( cmd , & prm )
2022-01-21 16:53:06 +00:00
prm . SetContainer ( * id )
2020-10-15 15:00:53 +00:00
2021-10-28 14:48:46 +00:00
res , err := internalclient . GetContainer ( prm )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "rpc error: %w" , err ) )
2021-10-28 14:48:46 +00:00
cnr = res . Container ( )
2020-10-15 15:00:53 +00:00
}
2021-06-20 23:02:56 +00:00
prettyPrintContainer ( cmd , cnr , containerJSON )
2020-10-15 15:00:53 +00:00
if containerPathTo != "" {
2020-10-20 12:34:13 +00:00
var (
data [ ] byte
err error
)
if containerJSON {
2020-11-16 09:43:52 +00:00
data , err = cnr . MarshalJSON ( )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "can't JSON encode container: %w" , err ) )
2020-10-20 12:34:13 +00:00
} else {
2020-11-16 10:26:35 +00:00
data , err = cnr . Marshal ( )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "can't binary encode container: %w" , err ) )
2020-10-15 15:00:53 +00:00
}
2021-06-28 14:01:31 +00:00
err = os . WriteFile ( containerPathTo , data , 0644 )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "can't write container to file: %w" , err ) )
2020-10-15 15:00:53 +00:00
}
} ,
}
2020-10-20 12:21:17 +00:00
var getExtendedACLCmd = & cobra . Command {
Use : "get-eacl" ,
Short : "Get extended ACL table of container" ,
Long : ` Get extended ACL talbe of container ` ,
2021-06-20 23:02:56 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2021-10-28 14:48:46 +00:00
id , err := parseContainerID ( containerID )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2021-04-21 12:27:32 +00:00
2021-10-28 14:48:46 +00:00
var eaclPrm internalclient . EACLPrm
2020-10-20 12:21:17 +00:00
2021-10-28 14:48:46 +00:00
prepareAPIClient ( cmd , & eaclPrm )
2022-01-21 16:53:06 +00:00
eaclPrm . SetContainer ( * id )
2020-10-20 12:21:17 +00:00
2021-10-28 14:48:46 +00:00
res , err := internalclient . EACL ( eaclPrm )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "rpc error: %w" , err ) )
2020-10-20 12:21:17 +00:00
2020-11-24 13:06:02 +00:00
eaclTable := res . EACL ( )
2021-10-28 14:48:46 +00:00
2021-05-31 11:03:17 +00:00
sig := eaclTable . Signature ( )
2020-11-24 13:06:02 +00:00
2020-10-20 12:21:17 +00:00
if containerPathTo == "" {
2021-06-20 23:02:56 +00:00
cmd . Println ( "eACL: " )
prettyPrintEACL ( cmd , eaclTable )
2020-11-24 13:06:02 +00:00
2021-06-20 23:02:56 +00:00
cmd . Println ( "Signature:" )
printJSONMarshaler ( cmd , sig , "signature" )
2020-11-24 13:06:02 +00:00
2021-06-20 23:02:56 +00:00
return
2020-10-20 12:21:17 +00:00
}
var data [ ] byte
if containerJSON {
2020-11-16 09:43:52 +00:00
data , err = eaclTable . MarshalJSON ( )
2021-09-01 16:33:48 +00:00
exitOnErr ( cmd , errf ( "can't encode to JSON: %w" , err ) )
2020-10-20 12:21:17 +00:00
} else {
2020-11-16 10:26:35 +00:00
data , err = eaclTable . Marshal ( )
2021-09-01 16:33:48 +00:00
exitOnErr ( cmd , errf ( "can't encode to binary: %w" , err ) )
2020-10-20 12:21:17 +00:00
}
2021-06-20 23:02:56 +00:00
cmd . Println ( "dumping data to file:" , containerPathTo )
2020-10-20 12:21:17 +00:00
2021-06-20 23:02:56 +00:00
cmd . Println ( "Signature:" )
printJSONMarshaler ( cmd , sig , "signature" )
2020-11-24 13:06:02 +00:00
2021-07-06 12:27:54 +00:00
err = os . WriteFile ( containerPathTo , data , 0644 )
2021-09-01 16:33:48 +00:00
exitOnErr ( cmd , errf ( "could not write eACL to file: %w" , err ) )
2020-10-20 12:21:17 +00:00
} ,
}
var setExtendedACLCmd = & cobra . Command {
Use : "set-eacl" ,
Short : "Set new extended ACL table for container" ,
Long : ` Set new extended ACL table for container .
Container ID in EACL table will be substituted with ID from the CLI . ` ,
2021-06-20 23:02:56 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2020-10-20 12:21:17 +00:00
id , err := parseContainerID ( containerID )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-20 12:21:17 +00:00
eaclTable , err := parseEACL ( eaclPathFrom )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2020-10-20 12:21:17 +00:00
2021-06-03 14:38:33 +00:00
tok , err := getSessionToken ( sessionTokenPath )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , err )
2021-05-28 13:28:50 +00:00
2020-10-20 12:21:17 +00:00
eaclTable . SetCID ( id )
2021-05-28 13:28:50 +00:00
eaclTable . SetSessionToken ( tok )
2020-10-20 12:21:17 +00:00
2021-10-28 14:48:46 +00:00
var (
setEACLPrm internalclient . SetEACLPrm
getEACLPrm internalclient . EACLPrm
)
prepareAPIClient ( cmd , & setEACLPrm , & getEACLPrm )
2022-01-21 16:53:06 +00:00
setEACLPrm . SetTable ( * eaclTable )
if tok != nil {
setEACLPrm . SetSessionToken ( * tok )
}
2021-10-28 14:48:46 +00:00
_ , err = internalclient . SetEACL ( setEACLPrm )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "rpc error: %w" , err ) )
2020-10-20 12:21:17 +00:00
if containerAwait {
2020-11-16 10:26:35 +00:00
exp , err := eaclTable . Marshal ( )
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errf ( "broken EACL table: %w" , err ) )
2020-10-20 12:21:17 +00:00
2021-06-20 23:02:56 +00:00
cmd . Println ( "awaiting..." )
2020-10-20 12:21:17 +00:00
2022-01-21 16:53:06 +00:00
getEACLPrm . SetContainer ( * id )
2021-10-28 14:48:46 +00:00
2020-10-20 12:21:17 +00:00
for i := 0 ; i < awaitTimeout ; i ++ {
time . Sleep ( 1 * time . Second )
2021-10-28 14:48:46 +00:00
res , err := internalclient . EACL ( getEACLPrm )
2020-10-20 12:21:17 +00:00
if err == nil {
// compare binary values because EACL could have been set already
2021-10-28 14:48:46 +00:00
got , err := res . EACL ( ) . Marshal ( )
2020-10-20 12:21:17 +00:00
if err != nil {
continue
}
if bytes . Equal ( exp , got ) {
2021-06-20 23:02:56 +00:00
cmd . Println ( "EACL has been persisted on sidechain" )
return
2020-10-20 12:21:17 +00:00
}
}
}
2021-07-06 12:27:54 +00:00
exitOnErr ( cmd , errSetEACLTimeout )
2020-10-20 12:21:17 +00:00
}
} ,
}
2021-09-27 15:36:14 +00:00
func initContainerListContainersCmd ( ) {
initCommonFlags ( listContainersCmd )
flags := listContainersCmd . Flags ( )
flags . StringVar ( & containerOwner , "owner" , "" , "owner of containers (omit to use owner from private key)" )
}
func initContainerCreateCmd ( ) {
initCommonFlags ( createContainerCmd )
flags := createContainerCmd . Flags ( )
2021-12-30 09:07:52 +00:00
flags . StringVar ( & containerACL , "basic-acl" , basicACLPrivate , fmt . Sprintf ( "hex encoded basic ACL value or keywords like '%s', '%s', '%s'" , basicACLPublic , basicACLPrivate , basicACLNoFinalReadOnly ) )
2021-09-27 15:36:14 +00:00
flags . StringVarP ( & containerPolicy , "policy" , "p" , "" , "QL-encoded or JSON-encoded placement policy or path to file with it" )
flags . StringSliceVarP ( & containerAttributes , "attributes" , "a" , nil , "comma separated pairs of container attributes in form of Key1=Value1,Key2=Value2" )
flags . StringVarP ( & containerNonce , "nonce" , "n" , "" , "UUIDv4 nonce value for container" )
flags . BoolVar ( & containerAwait , "await" , false , "block execution until container is persisted" )
flags . StringVar ( & containerName , "name" , "" , "container name attribute" )
flags . BoolVar ( & containerNoTimestamp , "disable-timestamp" , false , "disable timestamp container attribute" )
2021-11-26 17:46:07 +00:00
flags . StringVar ( & containerSubnet , "subnet" , "" , "string representation of container subnetwork" )
2021-09-27 15:36:14 +00:00
}
func initContainerDeleteCmd ( ) {
initCommonFlags ( deleteContainerCmd )
flags := deleteContainerCmd . Flags ( )
flags . StringVar ( & containerID , "cid" , "" , "container ID" )
flags . BoolVar ( & containerAwait , "await" , false , "block execution until container is removed" )
}
func initContainerListObjectsCmd ( ) {
initCommonFlags ( listContainerObjectsCmd )
flags := listContainerObjectsCmd . Flags ( )
flags . StringVar ( & containerID , "cid" , "" , "container ID" )
}
func initContainerInfoCmd ( ) {
initCommonFlags ( getContainerInfoCmd )
flags := getContainerInfoCmd . Flags ( )
flags . StringVar ( & containerID , "cid" , "" , "container ID" )
flags . StringVar ( & containerPathTo , "to" , "" , "path to dump encoded container" )
flags . StringVar ( & containerPathFrom , "from" , "" , "path to file with encoded container" )
flags . BoolVar ( & containerJSON , "json" , false , "print or dump container in JSON format" )
}
func initContainerGetEACLCmd ( ) {
initCommonFlags ( getExtendedACLCmd )
flags := getExtendedACLCmd . Flags ( )
flags . StringVar ( & containerID , "cid" , "" , "container ID" )
flags . StringVar ( & containerPathTo , "to" , "" , "path to dump encoded container (default: binary encoded)" )
flags . BoolVar ( & containerJSON , "json" , false , "encode EACL table in json format" )
}
func initContainerSetEACLCmd ( ) {
initCommonFlags ( setExtendedACLCmd )
flags := setExtendedACLCmd . Flags ( )
flags . StringVar ( & containerID , "cid" , "" , "container ID" )
flags . StringVar ( & eaclPathFrom , "table" , "" , "path to file with JSON or binary encoded EACL table" )
flags . BoolVar ( & containerAwait , "await" , false , "block execution until EACL is persisted" )
}
2020-08-04 14:46:12 +00:00
func init ( ) {
2021-09-27 15:36:14 +00:00
containerChildCommand := [ ] * cobra . Command {
listContainersCmd ,
createContainerCmd ,
deleteContainerCmd ,
listContainerObjectsCmd ,
getContainerInfoCmd ,
getExtendedACLCmd ,
setExtendedACLCmd ,
}
2020-08-04 14:46:12 +00:00
rootCmd . AddCommand ( containerCmd )
2021-09-27 15:36:14 +00:00
containerCmd . AddCommand ( containerChildCommand ... )
2020-08-04 14:46:12 +00:00
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// containerCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// containerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
2020-10-14 12:51:22 +00:00
2021-09-27 15:36:14 +00:00
initContainerListContainersCmd ( )
initContainerCreateCmd ( )
initContainerDeleteCmd ( )
initContainerListObjectsCmd ( )
initContainerInfoCmd ( )
initContainerGetEACLCmd ( )
initContainerSetEACLCmd ( )
for _ , containerCommand := range containerChildCommand {
flags := containerCommand . Flags ( )
flags . StringSliceVarP ( & xHeaders , xHeadersKey , xHeadersShorthand , xHeadersDefault , xHeadersUsage )
flags . Uint32P ( ttl , ttlShorthand , ttlDefault , ttlUsage )
}
2021-05-28 13:28:50 +00:00
for _ , cmd := range [ ] * cobra . Command {
createContainerCmd ,
deleteContainerCmd ,
setExtendedACLCmd ,
} {
cmd . Flags ( ) . StringVar (
& sessionTokenPath ,
sessionTokenFlag ,
"" ,
"path to a JSON-encoded container session token" ,
)
}
}
2021-06-03 14:38:33 +00:00
// getSessionToken reads `<path>` as JSON file with session token and parses it.
func getSessionToken ( path string ) ( * session . Token , error ) {
2021-05-28 13:28:50 +00:00
// try to read session token from file
var tok * session . Token
2021-06-03 14:38:33 +00:00
if path != "" {
2021-06-28 14:01:31 +00:00
data , err := os . ReadFile ( path )
2021-05-28 13:28:50 +00:00
if err != nil {
2021-09-01 16:33:48 +00:00
return nil , fmt . Errorf ( "could not open file with session token: %w" , err )
2021-05-28 13:28:50 +00:00
}
tok = session . NewToken ( )
if err = tok . UnmarshalJSON ( data ) ; err != nil {
2021-09-01 16:33:48 +00:00
return nil , fmt . Errorf ( "could not ummarshal session token from file: %w" , err )
2021-05-28 13:28:50 +00:00
}
}
return tok , nil
2020-10-14 12:51:22 +00:00
}
2021-06-20 23:02:56 +00:00
func prettyPrintContainerList ( cmd * cobra . Command , list [ ] * cid . ID ) {
2020-10-14 12:51:22 +00:00
for i := range list {
2021-06-20 23:02:56 +00:00
cmd . Println ( list [ i ] )
2020-10-14 12:51:22 +00:00
}
}
2021-11-26 17:46:07 +00:00
func parseSubnetID ( val string ) ( sub * subnetid . ID , err error ) {
sub = & subnetid . ID { }
if val != "" {
err = sub . UnmarshalText ( [ ] byte ( val ) )
}
return
}
2020-10-14 12:51:22 +00:00
func parseContainerPolicy ( policyString string ) ( * netmap . PlacementPolicy , error ) {
_ , err := os . Stat ( policyString ) // check if `policyString` is a path to file with placement policy
if err == nil {
printVerbose ( "Reading placement policy from file: %s" , policyString )
2021-06-28 14:01:31 +00:00
data , err := os . ReadFile ( policyString )
2020-10-14 12:51:22 +00:00
if err != nil {
return nil , fmt . Errorf ( "can't read file with placement policy: %w" , err )
}
policyString = string ( data )
}
result , err := policy . Parse ( policyString )
if err == nil {
printVerbose ( "Parsed QL encoded policy" )
return result , nil
}
2020-11-16 09:43:52 +00:00
result = netmap . NewPlacementPolicy ( )
if err = result . UnmarshalJSON ( [ ] byte ( policyString ) ) ; err == nil {
2020-10-14 12:51:22 +00:00
printVerbose ( "Parsed JSON encoded policy" )
return result , nil
}
return nil , errors . New ( "can't parse placement policy" )
}
2020-11-16 09:43:52 +00:00
func parseAttributes ( attributes [ ] string ) ( [ ] * container . Attribute , error ) {
result := make ( [ ] * container . Attribute , 0 , len ( attributes ) + 2 ) // name + timestamp attributes
2020-10-14 12:51:22 +00:00
for i := range attributes {
kvPair := strings . Split ( attributes [ i ] , attributeDelimiter )
if len ( kvPair ) != 2 {
return nil , errors . New ( "invalid container attribute" )
}
2020-11-16 09:43:52 +00:00
parsedAttribute := container . NewAttribute ( )
2020-10-14 12:51:22 +00:00
parsedAttribute . SetKey ( kvPair [ 0 ] )
parsedAttribute . SetValue ( kvPair [ 1 ] )
result = append ( result , parsedAttribute )
}
2020-11-03 13:44:50 +00:00
if ! containerNoTimestamp {
2020-11-16 09:43:52 +00:00
timestamp := container . NewAttribute ( )
2020-11-03 13:44:50 +00:00
timestamp . SetKey ( container . AttributeTimestamp )
timestamp . SetValue ( strconv . FormatInt ( time . Now ( ) . Unix ( ) , 10 ) )
result = append ( result , timestamp )
}
if containerName != "" {
2020-11-16 09:43:52 +00:00
cnrName := container . NewAttribute ( )
2020-11-03 13:44:50 +00:00
cnrName . SetKey ( container . AttributeName )
cnrName . SetValue ( containerName )
result = append ( result , cnrName )
}
2020-10-14 12:51:22 +00:00
return result , nil
}
2022-01-13 15:01:50 +00:00
func parseBasicACL ( basicACL string ) ( acl . BasicACL , error ) {
2021-12-30 09:07:52 +00:00
if value , ok := wellKnownBasicACL [ basicACL ] ; ok {
return value , nil
}
basicACL = strings . Trim ( strings . ToLower ( basicACL ) , "0x" )
2020-10-14 12:51:22 +00:00
2021-12-30 09:07:52 +00:00
value , err := strconv . ParseUint ( basicACL , 16 , 32 )
if err != nil {
return 0 , fmt . Errorf ( "can't parse basic ACL: %s" , basicACL )
2020-10-14 12:51:22 +00:00
}
2021-12-30 09:07:52 +00:00
2022-01-13 15:01:50 +00:00
return acl . BasicACL ( value ) , nil
2020-10-14 12:51:22 +00:00
}
func parseNonce ( nonce string ) ( uuid . UUID , error ) {
if nonce == "" {
result := uuid . New ( )
printVerbose ( "Generating container nonce: %s" , result )
return result , nil
}
2021-09-01 16:33:48 +00:00
uid , err := uuid . Parse ( nonce )
if err != nil {
return uuid . UUID { } , fmt . Errorf ( "could not parse nonce: %w" , err )
}
return uid , nil
2020-10-14 12:51:22 +00:00
}
2021-05-31 11:03:17 +00:00
func parseContainerID ( idStr string ) ( * cid . ID , error ) {
if idStr == "" {
2020-10-15 14:54:33 +00:00
return nil , errors . New ( "container ID is not set" )
}
2021-05-31 11:03:17 +00:00
id := cid . New ( )
2020-10-15 08:45:00 +00:00
2021-05-31 11:03:17 +00:00
err := id . Parse ( idStr )
2020-10-15 08:45:00 +00:00
if err != nil {
2020-10-14 12:51:22 +00:00
return nil , errors . New ( "can't decode container ID value" )
}
return id , nil
2020-08-04 14:46:12 +00:00
}
2020-10-15 15:00:53 +00:00
2021-06-20 23:02:56 +00:00
func prettyPrintContainer ( cmd * cobra . Command , cnr * container . Container , jsonEncoding bool ) {
2020-10-15 15:00:53 +00:00
if cnr == nil {
return
}
2020-10-20 12:34:13 +00:00
if jsonEncoding {
2020-11-16 09:43:52 +00:00
data , err := cnr . MarshalJSON ( )
2020-10-29 16:36:59 +00:00
if err != nil {
printVerbose ( "Can't convert container to json: %w" , err )
return
}
2020-10-20 12:34:13 +00:00
buf := new ( bytes . Buffer )
if err := json . Indent ( buf , data , "" , " " ) ; err != nil {
printVerbose ( "Can't pretty print json: %w" , err )
}
2021-06-20 23:02:56 +00:00
cmd . Println ( buf )
2020-10-20 12:34:13 +00:00
return
}
2020-10-15 15:00:53 +00:00
id := container . CalculateID ( cnr )
2021-06-20 23:02:56 +00:00
cmd . Println ( "container ID:" , id )
2020-10-15 15:00:53 +00:00
2020-11-16 09:43:52 +00:00
version := cnr . Version ( )
2021-06-20 23:02:56 +00:00
cmd . Printf ( "version: %d.%d\n" , version . Major ( ) , version . Minor ( ) )
2020-10-15 15:00:53 +00:00
2021-06-20 23:02:56 +00:00
cmd . Println ( "owner ID:" , cnr . OwnerID ( ) )
2020-10-15 15:00:53 +00:00
2020-11-16 09:43:52 +00:00
basicACL := cnr . BasicACL ( )
2022-01-13 15:01:50 +00:00
prettyPrintBasicACL ( cmd , acl . BasicACL ( basicACL ) )
2020-10-15 15:00:53 +00:00
2020-11-16 09:43:52 +00:00
for _ , attribute := range cnr . Attributes ( ) {
if attribute . Key ( ) == container . AttributeTimestamp {
2021-06-20 23:02:56 +00:00
cmd . Printf ( "attribute: %s=%s (%s)\n" ,
2020-11-16 09:43:52 +00:00
attribute . Key ( ) ,
attribute . Value ( ) ,
prettyPrintUnixTime ( attribute . Value ( ) ) )
2020-11-03 13:44:50 +00:00
continue
}
2021-06-20 23:02:56 +00:00
cmd . Printf ( "attribute: %s=%s\n" , attribute . Key ( ) , attribute . Value ( ) )
2020-10-15 15:00:53 +00:00
}
2020-12-24 10:20:20 +00:00
nonce , err := cnr . NonceUUID ( )
2020-10-15 15:00:53 +00:00
if err == nil {
2021-06-20 23:02:56 +00:00
cmd . Println ( "nonce:" , nonce )
2020-12-24 10:20:20 +00:00
} else {
2021-06-20 23:02:56 +00:00
cmd . Println ( "invalid nonce:" , err )
2020-10-15 15:00:53 +00:00
}
2021-06-20 23:02:56 +00:00
cmd . Println ( "placement policy:" )
cmd . Println ( strings . Join ( policy . Encode ( cnr . PlacementPolicy ( ) ) , "\n" ) )
2020-10-15 15:00:53 +00:00
}
2020-10-20 12:21:17 +00:00
func parseEACL ( eaclPath string ) ( * eacl . Table , error ) {
_ , err := os . Stat ( eaclPath ) // check if `eaclPath` is an existing file
if err != nil {
return nil , errors . New ( "incorrect path to file with EACL" )
}
printVerbose ( "Reading EACL from file: %s" , eaclPath )
2021-06-28 14:01:31 +00:00
data , err := os . ReadFile ( eaclPath )
2020-10-20 12:21:17 +00:00
if err != nil {
return nil , fmt . Errorf ( "can't read file with EACL: %w" , err )
}
2020-11-16 09:43:52 +00:00
table := eacl . NewTable ( )
2020-11-25 10:57:19 +00:00
2021-07-08 17:16:14 +00:00
if err = table . UnmarshalJSON ( data ) ; err == nil {
validateAndFixEACLVersion ( table )
2020-10-20 12:21:17 +00:00
printVerbose ( "Parsed JSON encoded EACL table" )
2020-11-16 09:43:52 +00:00
return table , nil
2020-10-20 12:21:17 +00:00
}
2021-07-08 17:16:14 +00:00
if err = table . Unmarshal ( data ) ; err == nil {
validateAndFixEACLVersion ( table )
printVerbose ( "Parsed binary encoded EACL table" )
return table , nil
}
return nil , errUnsupportedEACLFormat
}
func validateAndFixEACLVersion ( table * eacl . Table ) {
v := table . Version ( )
if ! version . IsValid ( v ) {
2021-11-10 07:08:33 +00:00
table . SetVersion ( * versionSDK . Current ( ) )
2021-07-08 17:16:14 +00:00
}
2020-10-20 12:21:17 +00:00
}
2021-06-20 23:02:56 +00:00
func prettyPrintEACL ( cmd * cobra . Command , table * eacl . Table ) {
printJSONMarshaler ( cmd , table , "eACL" )
2020-11-24 13:06:02 +00:00
}
2021-06-20 23:02:56 +00:00
func printJSONMarshaler ( cmd * cobra . Command , j json . Marshaler , entity string ) {
2020-11-24 13:06:02 +00:00
data , err := j . MarshalJSON ( )
2020-10-29 16:36:59 +00:00
if err != nil {
2020-11-24 13:06:02 +00:00
printVerbose ( "Can't convert %s to json: %w" , entity , err )
2020-10-29 16:36:59 +00:00
return
}
2020-10-20 12:21:17 +00:00
buf := new ( bytes . Buffer )
if err := json . Indent ( buf , data , "" , " " ) ; err != nil {
printVerbose ( "Can't pretty print json: %w" , err )
2020-10-29 16:36:59 +00:00
return
2020-10-20 12:21:17 +00:00
}
2021-06-20 23:02:56 +00:00
cmd . Println ( buf )
2020-10-20 12:21:17 +00:00
}
2021-12-30 09:07:52 +00:00
2022-01-13 15:01:50 +00:00
func prettyPrintBasicACL ( cmd * cobra . Command , basicACL acl . BasicACL ) {
2021-12-30 09:07:52 +00:00
cmd . Printf ( "basic ACL: %.8x" , basicACL )
for k , v := range wellKnownBasicACL {
if v == basicACL {
cmd . Printf ( " (%s)\n" , k )
return
}
}
cmd . Println ( )
}