diff --git a/CHANGELOG.md b/CHANGELOG.md index b7b593635d1..28f56c16a34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ minor release, the component will be purged, so be prepared (see `Updating` sect - SN's version and capacity is announced via the attributes automatically but can be overwritten explicitly (#2455, #602) - `peapod` command for `neofs-lens` (#2507) - New CLI exit code for awaiting timeout (#2380) +- To CLI commands: `bearer create`,`object put` added `--lifetime` flag; to CLI commands `session create`,`storagegroup put` added `--expired-at` flag (#1574) ### Fixed - `neo-go` RPC connection loss handling (#1337) diff --git a/cmd/neofs-cli/modules/bearer/create.go b/cmd/neofs-cli/modules/bearer/create.go index c826e3f5c7e..acfe47dd7f4 100644 --- a/cmd/neofs-cli/modules/bearer/create.go +++ b/cmd/neofs-cli/modules/bearer/create.go @@ -3,6 +3,7 @@ package bearer import ( "context" "encoding/json" + "errors" "fmt" "os" "time" @@ -46,27 +47,33 @@ func init() { createCmd.Flags().String(outFlag, "", "File to write token to") createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON") createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage) + createCmd.Flags().Uint64P(commonflags.Lifetime, "l", 0, "Number of epochs for token to stay valid") _ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), issuedAtFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), notValidBeforeFlag) - _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt) _ = cobra.MarkFlagRequired(createCmd.Flags(), ownerFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag) + createCmd.MarkFlagsMutuallyExclusive(commonflags.ExpireAt, commonflags.Lifetime) } func createToken(cmd *cobra.Command, _ []string) { + lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) + exp, expRelative, err := common.ParseEpoch(cmd, commonflags.ExpireAt) + if err != nil { + common.ExitOnErr(cmd, "can't parse --"+commonflags.ExpireAt+" flag: %w", err) + } + if exp == 0 && lifetime == 0 { + common.ExitOnErr(cmd, "", errors.New("expiration epoch or lifetime period is required")) + } iat, iatRelative, err := common.ParseEpoch(cmd, issuedAtFlag) common.ExitOnErr(cmd, "can't parse --"+issuedAtFlag+" flag: %w", err) - exp, expRelative, err := common.ParseEpoch(cmd, commonflags.ExpireAt) - common.ExitOnErr(cmd, "can't parse --"+commonflags.ExpireAt+" flag: %w", err) - nvb, nvbRelative, err := common.ParseEpoch(cmd, notValidBeforeFlag) common.ExitOnErr(cmd, "can't parse --"+notValidBeforeFlag+" flag: %w", err) - if iatRelative || expRelative || nvbRelative { + if iatRelative || expRelative || nvbRelative || lifetime != 0 { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() @@ -83,6 +90,9 @@ func createToken(cmd *cobra.Command, _ []string) { if nvbRelative { nvb += currEpoch } + if lifetime != 0 { + exp = currEpoch + lifetime + } } if exp < nvb { common.ExitOnErr(cmd, "", diff --git a/cmd/neofs-cli/modules/object/lock.go b/cmd/neofs-cli/modules/object/lock.go index fd0f602fd6b..c825b4928ee 100644 --- a/cmd/neofs-cli/modules/object/lock.go +++ b/cmd/neofs-cli/modules/object/lock.go @@ -53,7 +53,7 @@ var objectLockCmd = &cobra.Command{ exp, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) if exp == 0 && lifetime == 0 { // mutual exclusion is ensured by cobra - common.ExitOnErr(cmd, "", errors.New("either expiration epoch of a lifetime is required")) + common.ExitOnErr(cmd, "", errors.New("expiration epoch or lifetime period is required")) } if lifetime != 0 { diff --git a/cmd/neofs-cli/modules/object/put.go b/cmd/neofs-cli/modules/object/put.go index 38a875a1627..2067f44247a 100644 --- a/cmd/neofs-cli/modules/object/put.go +++ b/cmd/neofs-cli/modules/object/put.go @@ -52,10 +52,12 @@ func initObjectPutCmd() { flags.Bool("disable-filename", false, "Do not set well-known filename attribute") flags.Bool("disable-timestamp", false, "Do not set well-known timestamp attribute") flags.Uint64VarP(&putExpiredOn, commonflags.ExpireAt, "e", 0, "The last active epoch in the life of the object") + flags.Uint64P(commonflags.Lifetime, "l", 0, "Number of epochs for object to stay valid") flags.Bool(noProgressFlag, false, "Do not show progress bar") flags.String(notificationFlag, "", "Object notification in the form of *epoch*:*topic*; '-' topic means using default") flags.Bool(binaryFlag, false, "Deserialize object structure from given file.") + objectPutCmd.MarkFlagsMutuallyExclusive(commonflags.ExpireAt, commonflags.Lifetime) } func putObject(cmd *cobra.Command, _ []string) { @@ -99,6 +101,15 @@ func putObject(cmd *cobra.Command, _ []string) { common.ExitOnErr(cmd, "can't parse object attributes: %w", err) expiresOn, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) + lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) + if lifetime > 0 { + endpoint, _ := cmd.Flags().GetString(commonflags.RPC) + currEpoch, err := internalclient.GetCurrentEpoch(ctx, endpoint) + common.ExitOnErr(cmd, "Request current epoch: %w", err) + + expiresOn = currEpoch + lifetime + } + if expiresOn > 0 { var expAttrFound bool expAttrValue := strconv.FormatUint(expiresOn, 10) diff --git a/cmd/neofs-cli/modules/session/create.go b/cmd/neofs-cli/modules/session/create.go index ca280b6117f..e24ec9f3328 100644 --- a/cmd/neofs-cli/modules/session/create.go +++ b/cmd/neofs-cli/modules/session/create.go @@ -43,10 +43,12 @@ func init() { createCmd.Flags().String(outFlag, "", "File to write session token to") createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON") createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage) + createCmd.Flags().Uint64P(commonflags.ExpireAt, "e", 0, "The last active epoch for token to stay valid") _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.WalletPath) _ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.RPC) + createCmd.MarkFlagsMutuallyExclusive(commonflags.ExpireAt, commonflags.Lifetime) } func createSession(cmd *cobra.Command, _ []string) { @@ -65,7 +67,17 @@ func createSession(cmd *cobra.Command, _ []string) { if lfArg, _ := cmd.Flags().GetUint64(commonflags.Lifetime); lfArg != 0 { lifetime = lfArg } - + exp, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) + if exp != 0 { + var netInfoPrm internalclient.NetworkInfoPrm + netInfoPrm.SetClient(c) + ni, err := internalclient.NetworkInfo(ctx, netInfoPrm) + common.ExitOnErr(cmd, "can't get current epoch: %w", err) + currEpoch := ni.NetworkInfo().CurrentEpoch() + if exp < currEpoch { + lifetime = exp - currEpoch + } + } var tok session.Object err = CreateSession(ctx, &tok, c, *privKey, lifetime) diff --git a/cmd/neofs-cli/modules/storagegroup/put.go b/cmd/neofs-cli/modules/storagegroup/put.go index 50fdffd5b90..fac669a425e 100644 --- a/cmd/neofs-cli/modules/storagegroup/put.go +++ b/cmd/neofs-cli/modules/storagegroup/put.go @@ -43,10 +43,18 @@ func initSGPutCmd() { _ = sgPutCmd.MarkFlagRequired(sgMembersFlag) flags.Uint64(commonflags.Lifetime, 0, "Storage group lifetime in epochs") - _ = sgPutCmd.MarkFlagRequired(commonflags.Lifetime) + flags.Uint64P(commonflags.ExpireAt, "e", 0, "The last active epoch of the storage group") + sgPutCmd.MarkFlagsMutuallyExclusive(commonflags.ExpireAt, commonflags.Lifetime) } func putSG(cmd *cobra.Command, _ []string) { + // with 1.8.0 cobra release we can use this instead of below + // sgPutCmd.MarkFlagsOneRequired("expire-at", "lifetime") + exp, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) + lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) + if exp == 0 && lifetime == 0 { // mutual exclusion is ensured by cobra + common.ExitOnErr(cmd, "", errors.New("expiration epoch or lifetime period is required")) + } ctx, cancel := commonflags.GetCommandContext(cmd) defer cancel() @@ -100,14 +108,17 @@ func putSG(cmd *cobra.Command, _ []string) { }, cnr, members, !resGetCnr.Container().IsHomomorphicHashingDisabled()) common.ExitOnErr(cmd, "could not collect storage group members: %w", err) - var netInfoPrm internalclient.NetworkInfoPrm - netInfoPrm.SetClient(cli) + if lifetime != 0 { + var netInfoPrm internalclient.NetworkInfoPrm + netInfoPrm.SetClient(cli) - ni, err := internalclient.NetworkInfo(ctx, netInfoPrm) - common.ExitOnErr(cmd, "can't fetch network info: %w", err) + ni, err := internalclient.NetworkInfo(ctx, netInfoPrm) + common.ExitOnErr(cmd, "can't fetch network info: %w", err) + currEpoch := ni.NetworkInfo().CurrentEpoch() + exp = currEpoch + lifetime + } - lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) - sg.SetExpirationEpoch(ni.NetworkInfo().CurrentEpoch() + lifetime) + sg.SetExpirationEpoch(exp) obj := object.New() obj.SetContainerID(cnr)