trash-bin cli has been exteneded by the list and restore commands (#7917)

* trash-bin cli has been exteneded by the list and restore commands

* v4 to v5 changes

---------

Co-authored-by: Roman Perekhod <rperekhod@owncloud.com>
This commit is contained in:
Roman Perekhod
2023-12-19 15:44:28 +01:00
committed by GitHub
parent c3422e68c8
commit 367f1ff9e8
7 changed files with 579 additions and 6 deletions

View File

@@ -0,0 +1,7 @@
Enhancement: Add cli commands for trash-bin
We added the `list` and `restore` commands to the trash-bin items to the CLI
https://github.com/owncloud/ocis/pull/7917
https://github.com/cs3org/reva/pull/4392
https://github.com/owncloud/ocis/issues/7845

View File

@@ -70,7 +70,7 @@ type Debug struct {
}
type GRPCConfig struct {
Addr string `yaml:"addr" env:"GATEWAY_GRPC_ADDR" desc:"The bind address of the GRPC service."`
Addr string `yaml:"addr" env:"OCIS_GATEWAY_GRPC_ADDR;GATEWAY_GRPC_ADDR" desc:"The bind address of the GRPC service."`
TLS *shared.GRPCServiceTLS `yaml:"tls"`
Namespace string `yaml:"-"`
Protocol string `yaml:"protocol" env:"GATEWAY_GRPC_PROTOCOL" desc:"The transport protocol of the GRPC service."`

View File

@@ -78,12 +78,13 @@ Cleaned uploads:
<!-- referencing: https://github.com/owncloud/ocis/pull/5500 -->
This command is about purging old trash-bin items of `project` spaces (spaces that have been created manually) and `personal` spaces.
This command is about the trash-bin to get an overview of items, restore items and purging old items of `project` spaces (spaces that have been created manually) and `personal` spaces.
```bash
ocis storage-users trash-bin <command>
```
#### Purge-expired
```plaintext
COMMANDS:
purge-expired Purge all expired items from the trashbin
@@ -97,6 +98,66 @@ The configuration for the `purge-expired` command is done by using the following
* `STORAGE_USERS_PURGE_TRASH_BIN_PROJECT_DELETE_BEFORE` has a default value of `30 days`, which means the command will delete all files older than `30 days`. The value is human-readable, valid values are `24h`, `60m`, `60s` etc. `0` is equivalent to disable and prevents the deletion of `project space` trash-bin files.
#### List and Restore Trash-Bins Items
To authenticate the cli command use `OCIS_SERVICE_ACCOUNT_SECRET=<acc-secret>` and `OCIS_SERVICE_ACCOUNT_ID=<acc-id>`. The `storage-users` cli tool uses the default address to establish the connection to the `gateway` service. If the connection is failed check your custom `gateway`
service `GATEWAY_GRPC_ADDR` configuration and set the same address to `storage-users` variable `OCIS_GATEWAY_GRPC_ADDR` or `STORAGE_USERS_GATEWAY_GRPC_ADDR`. The variable `STORAGE_USERS_CLI_MAX_ATTEMPTS_RENAME_FILE`
defines a maximum number of attempts to rename a file when the user restores the file with `--option keep-both` to existing destination with the same name.
The ID sources:
- personal 'spaceID' in a `https://{host}/graph/v1.0/me/drives?$filter=driveType+eq+personal`
- project 'spaceID' in a `https://{host}/graph/v1.0/me/drives?$filter=driveType+eq+project`
```bash
NAME:
ocis storage-users trash-bin list - Print a list of all trash-bin items of a space.
USAGE:
ocis storage-users trash-bin list command [command options] ['spaceID' required]
COMMANDS:
help, h Shows a list of commands or help for one command
OPTIONS:
--verbose, -v Get more verbose output (default: false)
--help, -h show help
```
```bash
NAME:
ocis storage-users trash-bin restore-all - Restore all trash-bin items for a space.
USAGE:
ocis storage-users trash-bin restore-all command [command options] ['spaceID' required]
COMMANDS:
help, h Shows a list of commands or help for one command
OPTIONS:
--option value, -o value The restore option defines the behavior for a file to be restored, where the file name already already exists in the target space. Supported values are: 'skip', 'replace' and 'keep-both'. (default: The default value is 'skip' overwriting an existing file)
--verbose, -v Get more verbose output (default: false)
--yes, -y Automatic yes to prompts. Assume 'yes' as answer to all prompts and run non-interactively. (default: false)
--help, -h show help
```
```bash
NAME:
ocis storage-users trash-bin restore - Restore a trash-bin item by ID.
USAGE:
ocis storage-users trash-bin restore command [command options] ['spaceID' required] ['itemID' required]
COMMANDS:
help, h Shows a list of commands or help for one command
OPTIONS:
--option value, -o value The restore option defines the behavior for a file to be restored, where the file name already already exists in the target space. Supported values are: 'skip', 'replace' and 'keep-both'. (default: The default value is 'skip' overwriting an existing file)
--verbose, -v Get more verbose output (default: false)
--help, -h show help
```
## Caching
The `storage-users` service caches stat, metadata and uuids of files and folders via the configured store in `STORAGE_USERS_STAT_CACHE_STORE`, `STORAGE_USERS_FILEMETADATA_CACHE_STORE` and `STORAGE_USERS_ID_CACHE_STORE`. Possible stores are:

View File

@@ -1,16 +1,59 @@
package command
import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/mohae/deepcopy"
tw "github.com/olekukonko/tablewriter"
"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
zlog "github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/storage-users/pkg/config"
"github.com/owncloud/ocis/v2/services/storage-users/pkg/config/parser"
"github.com/owncloud/ocis/v2/services/storage-users/pkg/event"
"github.com/rs/zerolog"
"github.com/urfave/cli/v2"
)
const (
SKIP = iota
REPLACE
KEEP_BOTH
)
var _optionFlagTmpl = cli.StringFlag{
Name: "option",
Value: "skip",
Aliases: []string{"o"},
Usage: "The restore option defines the behavior for a file to be restored, where the file name already already exists in the target space. Supported values are: 'skip', 'replace' and 'keep-both'.",
DefaultText: "The default value is 'skip' overwriting an existing file",
}
var _verboseFlagTmpl = cli.BoolFlag{
Name: "verbose",
Aliases: []string{"v"},
Usage: "Get more verbose output",
}
var _applyYesFlagTmpl = cli.BoolFlag{
Name: "yes",
Aliases: []string{"y"},
Usage: "Automatic yes to prompts. Assume 'yes' as answer to all prompts and run non-interactively.",
}
// TrashBin wraps trash-bin related sub-commands.
func TrashBin(cfg *config.Config) *cli.Command {
return &cli.Command{
@@ -18,6 +61,9 @@ func TrashBin(cfg *config.Config) *cli.Command {
Usage: "manage trash-bin's",
Subcommands: []*cli.Command{
PurgeExpiredResources(cfg),
listTrashBinItems(cfg),
restoreAllTrashBinItems(cfg),
restoreTrashBindItem(cfg),
},
}
}
@@ -53,3 +99,390 @@ func PurgeExpiredResources(cfg *config.Config) *cli.Command {
},
}
}
func listTrashBinItems(cfg *config.Config) *cli.Command {
var verboseVal bool
verboseFlag := _verboseFlagTmpl
verboseFlag.Destination = &verboseVal
return &cli.Command{
Name: "list",
Usage: "Print a list of all trash-bin items of a space.",
ArgsUsage: "['spaceID' required]",
Flags: []cli.Flag{
&verboseFlag,
},
Before: func(c *cli.Context) error {
return configlog.ReturnFatal(parser.ParseConfig(cfg))
},
Action: func(c *cli.Context) error {
log := cliLogger(verboseVal)
var spaceID string
if c.NArg() > 0 {
spaceID = c.Args().Get(0)
}
if spaceID == "" {
_ = cli.ShowSubcommandHelp(c)
return fmt.Errorf("spaceID is requered")
}
log.Info().Msgf("Getting trash-bin items for spaceID: '%s' ...", spaceID)
ref, err := storagespace.ParseReference(spaceID)
if err != nil {
return err
}
client, err := pool.GetGatewayServiceClient(cfg.RevaGatewayGRPCAddr)
if err != nil {
return fmt.Errorf("error selecting gateway client %w", err)
}
ctx, err := utils.GetServiceUserContext(cfg.ServiceAccount.ServiceAccountID, client, cfg.ServiceAccount.ServiceAccountSecret)
if err != nil {
return fmt.Errorf("could not get service user context %w", err)
}
res, err := listRecycle(ctx, client, ref)
if err != nil {
return err
}
table := itemsTable(len(res.GetRecycleItems()))
for _, item := range res.GetRecycleItems() {
table.Append([]string{item.GetKey(), item.GetRef().GetPath(), itemType(item.GetType()), utils.TSToTime(item.GetDeletionTime()).UTC().Format(time.RFC3339)})
}
table.Render()
fmt.Println("Use an itemID to restore an item.")
return nil
},
}
}
func restoreAllTrashBinItems(cfg *config.Config) *cli.Command {
var optionFlagVal string
var overwriteOption int
optionFlag := _optionFlagTmpl
optionFlag.Destination = &optionFlagVal
var verboseVal bool
verboseFlag := _verboseFlagTmpl
verboseFlag.Destination = &verboseVal
var applyYesVal bool
applyYesFlag := _applyYesFlagTmpl
applyYesFlag.Destination = &applyYesVal
return &cli.Command{
Name: "restore-all",
Usage: "Restore all trash-bin items for a space.",
ArgsUsage: "['spaceID' required]",
Flags: []cli.Flag{
&optionFlag,
&verboseFlag,
&applyYesFlag,
},
Before: func(c *cli.Context) error {
return configlog.ReturnFatal(parser.ParseConfig(cfg))
},
Action: func(c *cli.Context) error {
log := cliLogger(verboseVal)
var spaceID string
if c.NArg() > 0 {
spaceID = c.Args().Get(0)
}
if spaceID == "" {
_ = cli.ShowSubcommandHelp(c)
return cli.Exit("The spaceID is required", 1)
}
switch optionFlagVal {
case "skip":
overwriteOption = SKIP
case "replace":
overwriteOption = REPLACE
case "keep-both":
overwriteOption = KEEP_BOTH
default:
_ = cli.ShowSubcommandHelp(c)
return cli.Exit("The option flag is invalid", 1)
}
log.Info().Msgf("Restoring trash-bin items for spaceID: '%s' ...", spaceID)
ref, err := storagespace.ParseReference(spaceID)
if err != nil {
return err
}
client, err := pool.GetGatewayServiceClient(cfg.RevaGatewayGRPCAddr)
if err != nil {
return fmt.Errorf("error selecting gateway client %w", err)
}
ctx, err := utils.GetServiceUserContext(cfg.ServiceAccount.ServiceAccountID, client, cfg.ServiceAccount.ServiceAccountSecret)
if err != nil {
return fmt.Errorf("could not get service user context %w", err)
}
res, err := listRecycle(ctx, client, ref)
if err != nil {
return err
}
if !applyYesVal {
for {
fmt.Printf("Found %d items that could be restored, continue (Y/n), show the items list (s): ", len(res.GetRecycleItems()))
var i string
_, err := fmt.Scanf("%s", &i)
if err != nil {
log.Err(err).Send()
continue
}
if strings.ToLower(i) == "y" {
break
} else if strings.ToLower(i) == "n" {
return nil
} else if strings.ToLower(i) == "s" {
table := itemsTable(len(res.GetRecycleItems()))
for _, item := range res.GetRecycleItems() {
table.Append([]string{item.GetKey(), item.GetRef().GetPath(), itemType(item.GetType()), utils.TSToTime(item.GetDeletionTime()).UTC().Format(time.RFC3339)})
}
table.Render()
}
}
}
log.Info().Msgf("Run restoring-all with option=%s", optionFlagVal)
for _, item := range res.GetRecycleItems() {
log.Info().Msgf("restoring itemID: '%s', path: '%s', type: '%s'", item.GetKey(), item.GetRef().GetPath(), itemType(item.GetType()))
dstRes, err := restore(ctx, client, ref, item, overwriteOption, cfg.CliMaxAttemptsRenameFile, log)
if err != nil {
log.Err(err).Msg("trash-bin item restoring error")
continue
}
fmt.Printf("itemID: '%s', path: '%s', restored as '%s'\n", item.GetKey(), item.GetRef().GetPath(), dstRes.GetPath())
}
return nil
},
}
}
func restoreTrashBindItem(cfg *config.Config) *cli.Command {
var optionFlagVal string
var overwriteOption int
optionFlag := _optionFlagTmpl
optionFlag.Destination = &optionFlagVal
var verboseVal bool
verboseFlag := _verboseFlagTmpl
verboseFlag.Destination = &verboseVal
return &cli.Command{
Name: "restore",
Usage: "Restore a trash-bin item by ID.",
ArgsUsage: "['spaceID' required] ['itemID' required]",
Flags: []cli.Flag{
&optionFlag,
&verboseFlag,
},
Before: func(c *cli.Context) error {
return configlog.ReturnFatal(parser.ParseConfig(cfg))
},
Action: func(c *cli.Context) error {
log := cliLogger(verboseVal)
var spaceID, itemID string
if c.NArg() > 1 {
spaceID = c.Args().Get(0)
itemID = c.Args().Get(1)
}
if spaceID == "" {
_ = cli.ShowSubcommandHelp(c)
return fmt.Errorf("spaceID is requered")
}
if itemID == "" {
_ = cli.ShowSubcommandHelp(c)
return fmt.Errorf("itemID is requered")
}
switch optionFlagVal {
case "skip":
overwriteOption = SKIP
case "replace":
overwriteOption = REPLACE
case "keep-both":
overwriteOption = KEEP_BOTH
default:
_ = cli.ShowSubcommandHelp(c)
return cli.Exit("The option flag is invalid", 1)
}
log.Info().Msgf("Restoring trash-bin item for spaceID: '%s' itemID: '%s' ...", spaceID, itemID)
ref, err := storagespace.ParseReference(spaceID)
if err != nil {
return err
}
client, err := pool.GetGatewayServiceClient(cfg.RevaGatewayGRPCAddr)
if err != nil {
return fmt.Errorf("error selecting gateway client %w", err)
}
ctx, err := utils.GetServiceUserContext(cfg.ServiceAccount.ServiceAccountID, client, cfg.ServiceAccount.ServiceAccountSecret)
if err != nil {
return fmt.Errorf("could not get service user context %w", err)
}
res, err := listRecycle(ctx, client, ref)
if err != nil {
return err
}
var found bool
var itemRef *provider.RecycleItem
for _, item := range res.GetRecycleItems() {
if item.GetKey() == itemID {
itemRef = item
found = true
break
}
}
if !found {
return fmt.Errorf("itemID '%s' not found", itemID)
}
log.Info().Msgf("Run restoring with option=%s", optionFlagVal)
log.Info().Msgf("restoring itemID: '%s', path: '%s', type: '%s", itemRef.GetKey(), itemRef.GetRef().GetPath(), itemType(itemRef.GetType()))
dstRes, err := restore(ctx, client, ref, itemRef, overwriteOption, cfg.CliMaxAttemptsRenameFile, log)
if err != nil {
return err
}
fmt.Printf("itemID: '%s', path: '%s', restored as '%s'\n", itemRef.GetKey(), itemRef.GetRef().GetPath(), dstRes.GetPath())
return nil
},
}
}
func listRecycle(ctx context.Context, client gateway.GatewayAPIClient, ref provider.Reference) (*provider.ListRecycleResponse, error) {
_retrievingErrorMsg := "trash-bin items retrieving error"
res, err := client.ListRecycle(ctx, &provider.ListRecycleRequest{Ref: &ref, Key: "/"})
if err != nil {
return nil, fmt.Errorf("%s %w", _retrievingErrorMsg, err)
}
if res.Status.Code != rpc.Code_CODE_OK {
return nil, fmt.Errorf("%s %s", _retrievingErrorMsg, res.Status.Code)
}
if len(res.GetRecycleItems()) == 0 {
return res, cli.Exit("The trash-bin is empty. Nothing to restore", 0)
}
return res, nil
}
func restore(ctx context.Context, client gateway.GatewayAPIClient, ref provider.Reference, item *provider.RecycleItem, overwriteOption int, maxRenameAttempt int, log zlog.Logger) (*provider.Reference, error) {
dst, _ := deepcopy.Copy(ref).(provider.Reference)
dst.Path = utils.MakeRelativePath(item.GetRef().GetPath())
// Restore request
req := &provider.RestoreRecycleItemRequest{
Ref: &ref,
Key: path.Join(item.GetKey(), "/"),
RestoreRef: &dst,
}
exists, dstStatRes, err := isDestinationExists(ctx, client, dst)
if err != nil {
return &dst, err
}
if exists {
log.Info().Msgf("destination '%s' exists.", dstStatRes.GetInfo().GetPath())
switch overwriteOption {
case SKIP:
return &dst, nil
case REPLACE:
// delete existing tree
delReq := &provider.DeleteRequest{Ref: &dst}
delRes, err := client.Delete(ctx, delReq)
if err != nil {
return &dst, fmt.Errorf("error sending grpc delete request %w", err)
}
if delRes.Status.Code != rpc.Code_CODE_OK && delRes.Status.Code != rpc.Code_CODE_NOT_FOUND {
return &dst, fmt.Errorf("deleting error %w", err)
}
case KEEP_BOTH:
// modify the file name
req.RestoreRef, err = resolveDestination(ctx, client, dst, maxRenameAttempt)
if err != nil {
return &dst, err
}
}
}
res, err := client.RestoreRecycleItem(ctx, req)
if err != nil {
return req.RestoreRef, fmt.Errorf("restoring error %w", err)
}
if res.Status.Code != rpc.Code_CODE_OK {
return req.RestoreRef, fmt.Errorf("can not restore %s", res.Status.Code)
}
return req.RestoreRef, nil
}
func resolveDestination(ctx context.Context, client gateway.GatewayAPIClient, dstRef provider.Reference, maxRenameAttempt int) (*provider.Reference, error) {
dst := dstRef
if maxRenameAttempt < 100 {
maxRenameAttempt = 100
}
for i := 1; i < maxRenameAttempt; i++ {
dst.Path = modifyFilename(dstRef.Path, i)
exists, _, err := isDestinationExists(ctx, client, dst)
if err != nil {
return nil, err
}
if exists {
continue
}
return &dst, nil
}
return nil, fmt.Errorf("too many attempts to resolve the destination")
}
func isDestinationExists(ctx context.Context, client gateway.GatewayAPIClient, dst provider.Reference) (bool, *provider.StatResponse, error) {
dstStatReq := &provider.StatRequest{Ref: &dst}
dstStatRes, err := client.Stat(ctx, dstStatReq)
if err != nil {
return false, nil, fmt.Errorf("error sending grpc stat request %w", err)
}
if dstStatRes.GetStatus().GetCode() == rpc.Code_CODE_OK {
return true, dstStatRes, nil
}
if dstStatRes.GetStatus().GetCode() == rpc.Code_CODE_NOT_FOUND {
return false, dstStatRes, nil
}
return false, dstStatRes, fmt.Errorf("stat request failed %s", dstStatRes.GetStatus())
}
// modify the file name like UI do
func modifyFilename(filename string, mod int) string {
var extension string
var found bool
expected := []string{".tar.gz", ".tar.bz", ".tar.bz2"}
for _, s := range expected {
var prefix string
prefix, found = strings.CutSuffix(strings.ToLower(filename), s)
if found {
extension = strings.TrimPrefix(filename, prefix)
break
}
}
if !found {
extension = filepath.Ext(filename)
}
name := filename[0 : len(filename)-len(extension)]
return fmt.Sprintf("%s (%d)%s", name, mod, extension)
}
func itemType(it provider.ResourceType) string {
var itemType = "file"
if it == provider.ResourceType_RESOURCE_TYPE_CONTAINER {
itemType = "folder"
}
return itemType
}
func itemsTable(total int) *tw.Table {
table := tw.NewWriter(os.Stdout)
table.SetHeader([]string{"itemID", "path", "type", "delete at"})
table.SetAutoFormatHeaders(false)
table.SetFooter([]string{"", "", "", "total count: " + strconv.Itoa(total)})
return table
}
func cliLogger(verbose bool) zlog.Logger {
logLvl := zerolog.ErrorLevel
if verbose {
logLvl = zerolog.InfoLevel
}
zerolog.SetGlobalLevel(zerolog.TraceLevel)
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339, NoColor: true}
return zlog.Logger{zerolog.New(output).With().Timestamp().Logger().Level(logLvl)}
}

View File

@@ -0,0 +1,65 @@
package command
import (
"testing"
)
func Test_modifyFilename(t *testing.T) {
type args struct {
filename string
mod int
}
tests := []struct {
name string
args args
want string
}{
{
name: "file",
args: args{filename: "file.txt", mod: 1},
want: "file (1).txt",
},
{
name: "file with path",
args: args{filename: "./file.txt", mod: 1},
want: "./file (1).txt",
},
{
name: "file with path 2",
args: args{filename: "./subdir/file.tar.gz", mod: 99},
want: "./subdir/file (99).tar.gz",
},
{
name: "file with path 3",
args: args{filename: "./sub dir/new file.tar.gz", mod: 99},
want: "./sub dir/new file (99).tar.gz",
},
{
name: "file without ext",
args: args{filename: "./subdir/file", mod: 2},
want: "./subdir/file (2)",
},
{
name: "file without ext 2",
args: args{filename: "./subdir/file 1", mod: 2},
want: "./subdir/file 1 (2)",
},
{
name: "file with emoji",
args: args{filename: "./subdir/file 🙂.tar.gz", mod: 3},
want: "./subdir/file 🙂 (3).tar.gz",
},
{
name: "file with emoji 2",
args: args{filename: "./subdir/file 🙂", mod: 2},
want: "./subdir/file 🙂 (2)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := modifyFilename(tt.args.filename, tt.args.mod); got != tt.want {
t.Errorf("modifyFilename() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -24,10 +24,11 @@ type Config struct {
SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"STORAGE_USERS_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token."`
GracefulShutdownTimeout int `yaml:"graceful_shutdown_timeout" env:"STORAGE_USERS_GRACEFUL_SHUTDOWN_TIMEOUT" desc:"The number of seconds to wait for the 'storage-users' service to shutdown cleanly before exiting with an error that gets logged. Note: This setting is only applicable when running the 'storage-users' service as a standalone service. See the text description for more details."`
Driver string `yaml:"driver" env:"STORAGE_USERS_DRIVER" desc:"The storage driver which should be used by the service. Defaults to 'ocis', Supported values are: 'ocis', 's3ng' and 'owncloudsql'. The 'ocis' driver stores all data (blob and meta data) in an POSIX compliant volume. The 's3ng' driver stores metadata in a POSIX compliant volume and uploads blobs to the s3 bucket."`
Drivers Drivers `yaml:"drivers"`
DataServerURL string `yaml:"data_server_url" env:"STORAGE_USERS_DATA_SERVER_URL" desc:"URL of the data server, needs to be reachable by the data gateway provided by the frontend service or the user if directly exposed."`
DataGatewayURL string `yaml:"data_gateway_url" env:"STORAGE_USERS_DATA_GATEWAY_URL" desc:"URL of the data gateway server"`
Driver string `yaml:"driver" env:"STORAGE_USERS_DRIVER" desc:"The storage driver which should be used by the service. Defaults to 'ocis', Supported values are: 'ocis', 's3ng' and 'owncloudsql'. The 'ocis' driver stores all data (blob and meta data) in an POSIX compliant volume. The 's3ng' driver stores metadata in a POSIX compliant volume and uploads blobs to the s3 bucket."`
Drivers Drivers `yaml:"drivers"`
DataServerURL string `yaml:"data_server_url" env:"STORAGE_USERS_DATA_SERVER_URL" desc:"URL of the data server, needs to be reachable by the data gateway provided by the frontend service or the user if directly exposed."`
DataGatewayURL string `yaml:"data_gateway_url" env:"STORAGE_USERS_DATA_GATEWAY_URL" desc:"URL of the data gateway server"`
TransferExpires int64 `yaml:"transfer_expires" env:"STORAGE_USERS_TRANSFER_EXPIRES" desc:"the time after which the token for upload postprocessing expires"`
Events Events `yaml:"events"`
StatCache StatCache `yaml:"stat_cache"`
@@ -40,6 +41,11 @@ type Config struct {
Tasks Tasks `yaml:"tasks"`
ServiceAccount ServiceAccount `yaml:"service_account"`
// CLI
RevaGatewayGRPCAddr string `yaml:"gateway_addr" env:"OCIS_GATEWAY_GRPC_ADDR;STORAGE_USERS_GATEWAY_GRPC_ADDR" desc:"The bind address of the gateway GRPC address."`
MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services."`
CliMaxAttemptsRenameFile int `yaml:"max_attempts_rename_file" env:"STORAGE_USERS_CLI_MAX_ATTEMPTS_RENAME_FILE" desc:"The maximum number of attempts to rename a file when a user restores a file to an existing destination with the same name. The minimum value is 100."`
Supervised bool `yaml:"-"`
Context context.Context `yaml:"-"`
}

View File

@@ -44,6 +44,7 @@ func DefaultConfig() *config.Config {
Reva: shared.DefaultRevaConfig(),
DataServerURL: "http://localhost:9158/data",
DataGatewayURL: "https://localhost:9200/data",
RevaGatewayGRPCAddr: "127.0.0.1:9142",
TransferExpires: 86400,
UploadExpiration: 24 * 60 * 60,
GracefulShutdownTimeout: 30,