diff --git a/CHANGELOG.md b/CHANGELOG.md index da388440..5a497595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## To Be Released +* feat(cmd): add `valkey-console` + ## 1.48.0 * feat(pitr): add command to fetch recovery window of a database diff --git a/cmd/addon_providers_list.go b/cmd/addon_providers_list.go index 100577b5..52704b30 100644 --- a/cmd/addon_providers_list.go +++ b/cmd/addon_providers_list.go @@ -10,7 +10,7 @@ import ( ) var ( - AddonProvidersListCommand = cli.Command{ + addonProvidersListCommand = cli.Command{ Name: "addons-list", Category: "Addons - Global", Description: "List all addons you can add to your app", diff --git a/cmd/addons_providers_plans_list.go b/cmd/addons_providers_plans_list.go index 18401a23..6d9fc4fb 100644 --- a/cmd/addons_providers_plans_list.go +++ b/cmd/addons_providers_plans_list.go @@ -10,7 +10,7 @@ import ( ) var ( - AddonProvidersPlansCommand = cli.Command{ + addonProvidersPlansCommand = cli.Command{ Name: "addons-plans", Category: "Addons - Global", ArgsUsage: "addon-id", diff --git a/cmd/commands.go b/cmd/commands.go index 31e86d96..8a4180a6 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -152,12 +152,25 @@ func NewAppCommands() *AppCommands { return &cmds } +const ( + categoryDatabases = "Databases" + + flagSizeName = "size" + flagSizeUsage = "Size of the container" + flagEnvName = "env" + flagEnvUsage = "Environment variable name to use for the connection to the database" + + mySQLConsole = "mysql-console" + mongoConsole = "mongo-console" + pgSQLConsole = "pgsql-console" +) + var ( regionalCommands = []*cli.Command{ // Apps &appsCommand, - &CreateCommand, - &DestroyCommand, + &createCommand, + &destroyCommand, &renameCommand, &appsInfoCommand, &openCommand, @@ -174,7 +187,7 @@ var ( // Apps Process Actions &psCommand, &scaleCommand, - &RestartCommand, + &restartCommand, &sendSignalCommand, // Routing Settings @@ -185,7 +198,7 @@ var ( &unsetCanonicalDomainCommand, // Events - &UserTimelineCommand, + &userTimelineCommand, &timelineCommand, // Environment @@ -195,10 +208,10 @@ var ( &envUnsetCommand, // Domains - &DomainsListCommand, - &DomainsAddCommand, - &DomainsRemoveCommand, - &DomainsSSLCommand, + &domainsListCommand, + &domainsAddCommand, + &domainsRemoveCommand, + &domainsSSLCommand, // Deployments &deploymentsListCommand, @@ -218,8 +231,8 @@ var ( &stacksSetCommand, // Addons - &AddonProvidersListCommand, - &AddonProvidersPlansCommand, + &addonProvidersListCommand, + &addonProvidersPlansCommand, &addonsListCommand, &addonsAddCommand, &addonsRemoveCommand, @@ -239,18 +252,19 @@ var ( &reviewAppsShowCommand, // Notifiers - &NotifiersListCommand, - &NotifiersDetailsCommand, - &NotifiersAddCommand, - &NotifiersUpdateCommand, - &NotifiersRemoveCommand, + ¬ifiersListCommand, + ¬ifiersDetailsCommand, + ¬ifiersAddCommand, + ¬ifiersUpdateCommand, + ¬ifiersRemoveCommand, // Notification platforms - &NotificationPlatformListCommand, + ¬ificationPlatformListCommand, // DB Access - &DbTunnelCommand, - &RedisConsoleCommand, + &dbTunnelCommand, + &redisConsoleCommand, + &valkeyConsoleCommand, &MongoConsoleCommand, &MySQLConsoleCommand, &PgSQLConsoleCommand, @@ -288,7 +302,7 @@ var ( &alertsRemoveCommand, // Stats - &StatsCommand, + &statsCommand, // Autoscalers &autoscalersListCommand, diff --git a/cmd/create.go b/cmd/create.go index 580c66d7..cb1bd966 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -11,7 +11,7 @@ import ( ) var ( - CreateCommand = cli.Command{ + createCommand = cli.Command{ Name: "create", Aliases: []string{"c"}, Category: "Global", diff --git a/cmd/db_tunnel.go b/cmd/db_tunnel.go index 24c9e6b4..f59cf7c6 100644 --- a/cmd/db_tunnel.go +++ b/cmd/db_tunnel.go @@ -16,7 +16,7 @@ import ( ) var ( - DbTunnelCommand = cli.Command{ + dbTunnelCommand = cli.Command{ Name: "db-tunnel", Category: "App Management", Usage: "Create an encrypted connection to access your database", diff --git a/cmd/deployments.go b/cmd/deployments.go index 60b56cb1..b12f352f 100644 --- a/cmd/deployments.go +++ b/cmd/deployments.go @@ -125,7 +125,7 @@ The version reference is optional (generated from timestamp if none). It is a reference to the code you are deploying, version, commit SHA, etc.`, Examples: []string{ "scalingo --app my-app deploy archive.tar.gz v1.0.0", - "scalingo --app my-app deploy http://example.com/archive.tar.gz v1.0.0", + "scalingo --app my-app deploy https://example.com/archive.tar.gz v1.0.0", "scalingo --app my-app deploy --no-follow archive.tar.gz v1.0.0", "scalingo --app my-app deployment-follow", }, diff --git a/cmd/destroy.go b/cmd/destroy.go index 661e4961..76866eb7 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -12,7 +12,7 @@ import ( ) var ( - DestroyCommand = cli.Command{ + destroyCommand = cli.Command{ Name: "destroy", Category: "App Management", Flags: []cli.Flag{&appFlag, diff --git a/cmd/domains.go b/cmd/domains.go index 482d2ae5..3211d403 100644 --- a/cmd/domains.go +++ b/cmd/domains.go @@ -15,7 +15,7 @@ import ( ) var ( - DomainsListCommand = cli.Command{ + domainsListCommand = cli.Command{ Name: "domains", Category: "Custom Domains", Flags: []cli.Flag{&appFlag}, @@ -47,7 +47,7 @@ var ( }, } - DomainsAddCommand = cli.Command{ + domainsAddCommand = cli.Command{ Name: "domains-add", Category: "Custom Domains", Usage: "Add a custom domain to an application", @@ -106,7 +106,7 @@ var ( }, } - DomainsRemoveCommand = cli.Command{ + domainsRemoveCommand = cli.Command{ Name: "domains-remove", Category: "Custom Domains", Flags: []cli.Flag{&appFlag}, @@ -140,7 +140,7 @@ var ( } // TODO: Split the two operations (enable/disable) into two subcommands - DomainsSSLCommand = cli.Command{ + domainsSSLCommand = cli.Command{ Name: "domains-ssl", Category: "Custom Domains", Usage: "Enable or disable SSL for your custom domains", diff --git a/cmd/env.go b/cmd/env.go index 1633a0cb..5168673e 100644 --- a/cmd/env.go +++ b/cmd/env.go @@ -14,7 +14,7 @@ import ( var ( envCommand = cli.Command{ - Name: "env", + Name: flagEnvName, Category: "Environment", Flags: []cli.Flag{&appFlag, databaseFlag()}, Usage: "Display the environment variables of your apps", @@ -28,7 +28,7 @@ var ( currentApp := detect.GetCurrentResource(ctx, c) var err error if c.Args().Len() != 0 { - _ = cli.ShowCommandHelp(ctx, c, "env") + _ = cli.ShowCommandHelp(ctx, c, flagEnvName) return nil } @@ -42,7 +42,7 @@ var ( return nil }, ShellComplete: func(_ context.Context, c *cli.Command) { - _ = autocomplete.CmdFlagsAutoComplete(c, "env") + _ = autocomplete.CmdFlagsAutoComplete(c, flagEnvName) }, } @@ -55,12 +55,12 @@ var ( Description: CommandDescription{ Description: "Get the value of a specific environment variable", Examples: []string{"scalingo --app my-app env-get VAR1"}, - SeeAlso: []string{"env", "env-set", "env-unset"}, + SeeAlso: []string{flagEnvName, "env-set", "env-unset"}, }.Render(), Action: func(ctx context.Context, c *cli.Command) error { if c.Args().Len() != 1 { - _ = cli.ShowCommandHelp(ctx, c, "env") + _ = cli.ShowCommandHelp(ctx, c, flagEnvName) return nil } @@ -76,7 +76,7 @@ var ( return nil }, ShellComplete: func(_ context.Context, c *cli.Command) { - _ = autocomplete.CmdFlagsAutoComplete(c, "env") + _ = autocomplete.CmdFlagsAutoComplete(c, flagEnvName) }, } @@ -96,7 +96,7 @@ var ( "scalingo --app my-app env-set --file - < .env", "scalingo --app my-app env-set --file .env VAR2=VAL2", }, - SeeAlso: []string{"env", "env-get", "env-unset"}, + SeeAlso: []string{flagEnvName, "env-get", "env-unset"}, }.Render(), Action: func(ctx context.Context, c *cli.Command) error { if c.Args().Len() == 0 && c.String("f") == "" { @@ -125,7 +125,7 @@ var ( Description: CommandDescription{ Description: "Unset variables", Examples: []string{"scalingo --app my-app env-unset VAR1 VAR2"}, - SeeAlso: []string{"env", "env-get", "env-set"}, + SeeAlso: []string{flagEnvName, "env-get", "env-set"}, }.Render(), Action: func(ctx context.Context, c *cli.Command) error { diff --git a/cmd/influxdb.go b/cmd/influxdb.go index 8be8c011..8050ca2f 100644 --- a/cmd/influxdb.go +++ b/cmd/influxdb.go @@ -15,11 +15,11 @@ var ( InfluxDBConsoleCommand = cli.Command{ Name: "influxdb-console", Aliases: []string{"influx-console"}, - Category: "Databases", + Category: categoryDatabases, Usage: "Run an interactive console with your InfluxDB addon", Flags: []cli.Flag{&appFlag, - &cli.StringFlag{Name: "size", Aliases: []string{"s"}, Value: "", Usage: "Size of the container"}, - &cli.StringFlag{Name: "env", Aliases: []string{"e"}, Value: "", Usage: "Environment variable name to use for the connection to the database"}, + &cli.StringFlag{Name: flagSizeName, Aliases: []string{"s"}, Value: "", Usage: flagSizeUsage}, + &cli.StringFlag{Name: flagEnvName, Aliases: []string{"e"}, Value: "", Usage: flagEnvUsage}, }, Description: CommandDescription{ Description: `Run an interactive console with your InfluxDB addon @@ -27,13 +27,13 @@ var ( The --size flag makes it easy to specify the size of the container executing the InfluxDB console. Each container size has different price and performance. You can read more about container sizes here: -http://doc.scalingo.com/internals/container-sizes.html`, +https://doc.scalingo.com/platform/internals/container-sizes`, Examples: []string{ "scalingo --app my-app influxdb-console", "scalingo --app my-app influxdb-console --size L", "scalingo --app my-app influxdb-console --env MY_INFLUXDB_URL", }, - SeeAlso: []string{"mongo-console", "mysql-console"}, + SeeAlso: []string{mongoConsole, mySQLConsole}, }.Render(), Action: func(ctx context.Context, c *cli.Command) error { if c.Args().Len() != 0 { diff --git a/cmd/mongo.go b/cmd/mongo.go index aefce4d9..7ab03d24 100644 --- a/cmd/mongo.go +++ b/cmd/mongo.go @@ -13,30 +13,30 @@ import ( var ( MongoConsoleCommand = cli.Command{ - Name: "mongo-console", + Name: mongoConsole, Aliases: []string{"mongodb-console"}, - Category: "Databases", + Category: categoryDatabases, Usage: "Run an interactive console with your MongoDB addon", Flags: []cli.Flag{&appFlag, - &cli.StringFlag{Name: "size", Aliases: []string{"s"}, Value: "", Usage: "Size of the container"}, - &cli.StringFlag{Name: "env", Aliases: []string{"e"}, Value: "", Usage: "Environment variable name to use for the connection to the database"}, + &cli.StringFlag{Name: flagSizeName, Aliases: []string{"s"}, Value: "", Usage: flagSizeUsage}, + &cli.StringFlag{Name: flagEnvName, Aliases: []string{"e"}, Value: "", Usage: flagEnvUsage}, }, Description: CommandDescription{ Description: `Run an interactive console with your MongoDB addon The --size flag makes it easy to specify the size of the container executing the MongoDB console. Each container size has different price and performance. You can read more about container sizes here: -http://doc.scalingo.com/internals/container-sizes.html`, +https://doc.scalingo.com/platform/internals/container-sizes`, Examples: []string{ "scalingo --app my-app mongo-console", "scalingo --app my-app mongo-console --size L", "scalingo --app my-app mongo-console --env MY_MONGO_URL", }, - SeeAlso: []string{"redis-console", "mysql-console"}, + SeeAlso: []string{"redis-console", mySQLConsole}, }.Render(), Action: func(ctx context.Context, c *cli.Command) error { if c.Args().Len() != 0 { - _ = cli.ShowCommandHelp(ctx, c, "mongo-console") + _ = cli.ShowCommandHelp(ctx, c, mongoConsole) return nil } @@ -54,7 +54,7 @@ http://doc.scalingo.com/internals/container-sizes.html`, return nil }, ShellComplete: func(_ context.Context, c *cli.Command) { - _ = autocomplete.CmdFlagsAutoComplete(c, "mongo-console") + _ = autocomplete.CmdFlagsAutoComplete(c, mongoConsole) }, } ) diff --git a/cmd/mysql.go b/cmd/mysql.go index 91f34290..f248d2e6 100644 --- a/cmd/mysql.go +++ b/cmd/mysql.go @@ -13,12 +13,12 @@ import ( var ( MySQLConsoleCommand = cli.Command{ - Name: "mysql-console", - Category: "Databases", + Name: mySQLConsole, + Category: categoryDatabases, Usage: "Run an interactive console with your MySQL addon", Flags: []cli.Flag{&appFlag, - &cli.StringFlag{Name: "size", Aliases: []string{"s"}, Value: "", Usage: "Size of the container"}, - &cli.StringFlag{Name: "env", Aliases: []string{"e"}, Value: "", Usage: "Environment variable name to use for the connection to the database"}, + &cli.StringFlag{Name: flagSizeName, Aliases: []string{"s"}, Value: "", Usage: flagSizeUsage}, + &cli.StringFlag{Name: flagEnvName, Aliases: []string{"e"}, Value: "", Usage: flagEnvUsage}, }, Description: CommandDescription{ Description: `Run an interactive console with your MySQL addon @@ -26,17 +26,17 @@ var ( The --size flag makes it easy to specify the size of the container executing the MySQL console. Each container size has different price and performance. You can read more about container sizes here: -http://doc.scalingo.com/internals/container-sizes.html`, +https://doc.scalingo.com/platform/internals/container-sizes`, Examples: []string{ "scalingo --app my-app mysql-console", "scalingo --app my-app mysql-console --size L", "scalingo --app my-app mysql-console --env MY_MYSQL_URL", }, - SeeAlso: []string{"mongo-console", "pgsql-console"}, + SeeAlso: []string{mongoConsole, pgSQLConsole}, }.Render(), Action: func(ctx context.Context, c *cli.Command) error { if c.Args().Len() != 0 { - _ = cli.ShowCommandHelp(ctx, c, "mysql-console") + _ = cli.ShowCommandHelp(ctx, c, mySQLConsole) return nil } @@ -54,7 +54,7 @@ http://doc.scalingo.com/internals/container-sizes.html`, return nil }, ShellComplete: func(_ context.Context, c *cli.Command) { - _ = autocomplete.CmdFlagsAutoComplete(c, "mysql-console") + _ = autocomplete.CmdFlagsAutoComplete(c, mySQLConsole) }, } ) diff --git a/cmd/notification_platforms.go b/cmd/notification_platforms.go index 01df61ea..9b660dd5 100644 --- a/cmd/notification_platforms.go +++ b/cmd/notification_platforms.go @@ -10,7 +10,7 @@ import ( ) var ( - NotificationPlatformListCommand = cli.Command{ + notificationPlatformListCommand = cli.Command{ Name: "notification-platforms", Category: "Notifiers - Global", Description: "List all notification platforms you can use with a notifier.", diff --git a/cmd/notifiers.go b/cmd/notifiers.go index abfdf1e7..84cdc585 100644 --- a/cmd/notifiers.go +++ b/cmd/notifiers.go @@ -13,7 +13,7 @@ import ( ) var ( - NotifiersListCommand = cli.Command{ + notifiersListCommand = cli.Command{ Name: "notifiers", Category: "Notifiers", Usage: "List your notifiers", @@ -42,7 +42,7 @@ var ( }, } - NotifiersDetailsCommand = cli.Command{ + notifiersDetailsCommand = cli.Command{ Name: "notifiers-details", Category: "Notifiers", Usage: "Show details of a notifier", @@ -73,7 +73,7 @@ var ( }, } - NotifiersAddCommand = cli.Command{ + notifiersAddCommand = cli.Command{ Name: "notifiers-add", Category: "Notifiers", Flags: []cli.Flag{ @@ -141,7 +141,7 @@ var ( }, } - NotifiersUpdateCommand = cli.Command{ + notifiersUpdateCommand = cli.Command{ Name: "notifiers-update", Category: "Notifiers", Flags: []cli.Flag{ @@ -218,7 +218,7 @@ var ( }, } - NotifiersRemoveCommand = cli.Command{ + notifiersRemoveCommand = cli.Command{ Name: "notifiers-remove", Category: "Notifiers", Flags: []cli.Flag{&appFlag}, diff --git a/cmd/pgsql.go b/cmd/pgsql.go index 85a0d32e..0243e49a 100644 --- a/cmd/pgsql.go +++ b/cmd/pgsql.go @@ -13,13 +13,13 @@ import ( var ( PgSQLConsoleCommand = cli.Command{ - Name: "pgsql-console", + Name: pgSQLConsole, Aliases: []string{"psql-console", "postgresql-console"}, - Category: "Databases", + Category: categoryDatabases, Usage: "Run an interactive console with your PostgreSQL addon", Flags: []cli.Flag{&appFlag, - &cli.StringFlag{Name: "size", Aliases: []string{"s"}, Value: "", Usage: "Size of the container"}, - &cli.StringFlag{Name: "env", Aliases: []string{"e"}, Value: "", Usage: "Environment variable name to use for the connection to the database"}, + &cli.StringFlag{Name: flagSizeName, Aliases: []string{"s"}, Value: "", Usage: flagSizeUsage}, + &cli.StringFlag{Name: flagEnvName, Aliases: []string{"e"}, Value: "", Usage: flagEnvUsage}, }, Description: CommandDescription{ Description: `Run an interactive console with your PostgreSQL addon @@ -27,18 +27,18 @@ var ( The --size flag makes it easy to specify the size of the container executing the PostgreSQL console. Each container size has different price and performance. You can read more about container sizes here: -http://doc.scalingo.com/internals/container-sizes.html`, +https://doc.scalingo.com/platform/internals/container-sizes`, Examples: []string{ "scalingo --app my-app pgsql-console", "scalingo --app my-app pgsql-console --size L", "scalingo --app my-app pgsql-console --env MY_PSQL_URL", }, - SeeAlso: []string{"mongo-console", "mysql-console"}, + SeeAlso: []string{mongoConsole, mySQLConsole}, }.Render(), Action: func(ctx context.Context, c *cli.Command) error { if c.Args().Len() != 0 { - _ = cli.ShowCommandHelp(ctx, c, "pgsql-console") + _ = cli.ShowCommandHelp(ctx, c, pgSQLConsole) return nil } @@ -56,7 +56,7 @@ http://doc.scalingo.com/internals/container-sizes.html`, return nil }, ShellComplete: func(_ context.Context, c *cli.Command) { - _ = autocomplete.CmdFlagsAutoComplete(c, "pgsql-console") + _ = autocomplete.CmdFlagsAutoComplete(c, pgSQLConsole) }, } ) diff --git a/cmd/redis.go b/cmd/redis.go index 23632420..88d872b8 100644 --- a/cmd/redis.go +++ b/cmd/redis.go @@ -12,13 +12,13 @@ import ( ) var ( - RedisConsoleCommand = cli.Command{ + redisConsoleCommand = cli.Command{ Name: "redis-console", - Category: "Databases", + Category: categoryDatabases, Usage: "Run an interactive console with your Redis addon", Flags: []cli.Flag{&appFlag, - &cli.StringFlag{Name: "size", Aliases: []string{"s"}, Value: "", Usage: "Size of the container"}, - &cli.StringFlag{Name: "env", Aliases: []string{"e"}, Value: "", Usage: "Environment variable name to use for the connection to the database"}, + &cli.StringFlag{Name: flagSizeName, Aliases: []string{"s"}, Value: "", Usage: flagSizeUsage}, + &cli.StringFlag{Name: flagEnvName, Aliases: []string{"e"}, Value: "", Usage: flagEnvUsage}, }, Description: CommandDescription{ Description: `Run an interactive console with your Redis addon. @@ -26,13 +26,13 @@ var ( The --size flag makes it easy to specify the size of the container executing the Redis console. Each container size has different price and performance. You can read more about container sizes here: -http://doc.scalingo.com/internals/container-sizes.html`, +https://doc.scalingo.com/platform/internals/container-sizes`, Examples: []string{ "scalingo --app my-app redis-console", "scalingo --app my-app redis-console --size L", "scalingo --app my-app redis-console --env MY_REDIS_URL", }, - SeeAlso: []string{"mongo-console", "mysql-console"}, + SeeAlso: []string{mongoConsole, mySQLConsole}, }.Render(), Action: func(ctx context.Context, c *cli.Command) error { diff --git a/cmd/restart.go b/cmd/restart.go index 1eaa154e..5a8a06aa 100644 --- a/cmd/restart.go +++ b/cmd/restart.go @@ -12,7 +12,7 @@ import ( ) var ( - RestartCommand = cli.Command{ + restartCommand = cli.Command{ Name: "restart", Category: "App Management", Usage: "Restart processes of your app", diff --git a/cmd/run.go b/cmd/run.go index 94e37684..14d67dff 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -22,9 +22,9 @@ var ( ArgsUsage: "command-to-execute", Flags: []cli.Flag{&appFlag, &cli.BoolFlag{Name: "detached", Aliases: []string{"d"}, Usage: "Run a detached container"}, - &cli.StringFlag{Name: "size", Aliases: []string{"s"}, Value: "", Usage: "Size of the container"}, + &cli.StringFlag{Name: flagSizeName, Aliases: []string{"s"}, Value: "", Usage: flagSizeUsage}, &cli.StringFlag{Name: "type", Aliases: []string{"t"}, Value: "", Usage: "Procfile Type"}, - &cli.StringSliceFlag{Name: "env", Aliases: []string{"e"}, Usage: "Environment variables"}, + &cli.StringSliceFlag{Name: flagEnvName, Aliases: []string{"e"}, Usage: "Environment variables"}, &cli.StringSliceFlag{Name: "file", Aliases: []string{"f"}, Usage: "Files to upload"}, &cli.BoolFlag{Name: "silent", Usage: "Do not output anything on stderr"}, }, @@ -47,7 +47,7 @@ var ( The --size flag makes it easy to specify the size of the container you want to run. Each container size has different price and performance. You can read more about container sizes here: - http://doc.scalingo.com/internals/container-sizes.html + https://doc.scalingo.com/platform/internals/container-sizes The --silent flag makes that the only output of the command will be the output of the one-off container. There won't be any noise from the command tool itself. @@ -83,9 +83,9 @@ var ( opts := apps.RunOpts{ App: detect.GetCurrentResource(ctx, c), Cmd: c.Args().Slice(), - Size: c.String("size"), + Size: c.String(flagSizeName), Type: c.String("type"), - CmdEnv: c.StringSlice("env"), + CmdEnv: c.StringSlice(flagEnvName), Files: c.StringSlice("file"), Silent: c.Bool("silent"), Detached: c.Bool("detached"), @@ -112,8 +112,8 @@ var ( Usage: "Run bash for your app", ArgsUsage: "bash-arguments", Flags: []cli.Flag{&appFlag, - &cli.StringFlag{Name: "size", Aliases: []string{"s"}, Value: "", Usage: "Size of the container"}, - &cli.StringSliceFlag{Name: "env", Aliases: []string{"e"}, Usage: "Environment variables"}, + &cli.StringFlag{Name: flagSizeName, Aliases: []string{"s"}, Value: "", Usage: flagSizeUsage}, + &cli.StringSliceFlag{Name: flagEnvName, Aliases: []string{"e"}, Usage: "Environment variables"}, &cli.StringSliceFlag{Name: "file", Aliases: []string{"f"}, Usage: "Files to upload"}, &cli.BoolFlag{Name: "silent", Usage: "Do not output anything on stderr"}, }, @@ -126,8 +126,8 @@ var ( return runOneOffCommand(ctx, apps.RunOpts{ App: detect.GetCurrentResource(ctx, c), Cmd: append([]string{"bash"}, c.Args().Slice()...), - Size: c.String("size"), - CmdEnv: c.StringSlice("env"), + Size: c.String(flagSizeName), + CmdEnv: c.StringSlice(flagEnvName), Files: c.StringSlice("file"), Silent: c.Bool("silent"), }) diff --git a/cmd/stats.go b/cmd/stats.go index 75f57dda..45e4e888 100644 --- a/cmd/stats.go +++ b/cmd/stats.go @@ -10,7 +10,7 @@ import ( ) var ( - StatsCommand = cli.Command{ + statsCommand = cli.Command{ Name: "stats", Category: "Display metrics of the running containers", Usage: "Display metrics of the currently running containers", diff --git a/cmd/user-timeline.go b/cmd/user-timeline.go index 1ab8a52d..1ef6d378 100644 --- a/cmd/user-timeline.go +++ b/cmd/user-timeline.go @@ -11,7 +11,7 @@ import ( ) var ( - UserTimelineCommand = cli.Command{ + userTimelineCommand = cli.Command{ Name: "user-timeline", Category: "Events", Flags: []cli.Flag{ diff --git a/cmd/valkey.go b/cmd/valkey.go new file mode 100644 index 00000000..e38c256c --- /dev/null +++ b/cmd/valkey.go @@ -0,0 +1,67 @@ +package cmd + +import ( + "context" + + "github.com/urfave/cli/v3" + + "github.com/Scalingo/cli/cmd/autocomplete" + "github.com/Scalingo/cli/db" + "github.com/Scalingo/cli/detect" + "github.com/Scalingo/cli/utils" +) + +var ( + valkeyConsoleCommand = cli.Command{ + Name: "valkey-console", + Category: categoryDatabases, + Usage: "Run an interactive console with your Valkey addon", + Flags: []cli.Flag{&appFlag, + &cli.StringFlag{ + Name: flagSizeName, Aliases: []string{"s"}, Value: "", + Usage: flagSizeUsage, + }, + &cli.StringFlag{ + Name: flagEnvName, Aliases: []string{"e"}, Value: "", + Usage: flagEnvUsage, + }, + }, + Description: CommandDescription{ + Description: `Run an interactive console with your Valkey addon. + +The --size flag makes it easy to specify the size of the container executing +the Valkey console. Each container size has different price and performance. +You can read more about container sizes here: +https://doc.scalingo.com/platform/internals/container-sizes`, + Examples: []string{ + "scalingo --app my-app valkey-console", + "scalingo --app my-app valkey-console --size L", + "scalingo --app my-app valkey-console --env MY_VALKEY_URL", + }, + SeeAlso: []string{mySQLConsole, pgSQLConsole}, + }.Render(), + + Action: func(ctx context.Context, c *cli.Command) error { + if c.Args().Len() != 0 { + _ = cli.ShowCommandHelp(ctx, c, "valkey-console") + return nil + } + currentApp := detect.CurrentApp(ctx, c) + + utils.CheckForConsent(ctx, currentApp, utils.ConsentTypeDBs) + + err := db.ValkeyConsole(ctx, db.ValkeyConsoleOpts{ + App: currentApp, + Size: c.String("s"), + VariableName: c.String("e"), + }) + if err != nil { + errorQuit(ctx, err) + } + return nil + }, + ShellComplete: func(_ context.Context, c *cli.Command) { + _ = autocomplete.CmdFlagsAutoComplete(c, "valkey-console") + }, + } +) diff --git a/db/constants.go b/db/constants.go new file mode 100644 index 00000000..ac72a62c --- /dev/null +++ b/db/constants.go @@ -0,0 +1,5 @@ +package db + +const ( + dbClientFetcher = "dbclient-fetcher" +) diff --git a/db/influxdb_console.go b/db/influxdb_console.go index ded76da3..1382648c 100644 --- a/db/influxdb_console.go +++ b/db/influxdb_console.go @@ -29,7 +29,7 @@ func InfluxDBConsole(ctx context.Context, opts InfluxDBConsoleOpts) error { return errors.Newf(ctx, "%v has an invalid host", influxdbURL) } - cmd := []string{"dbclient-fetcher", "influxdb", "&&", "influx"} + cmd := []string{dbClientFetcher, "influxdb", "&&", "influx"} if influxdbURL.Scheme == "https" { cmd = append(cmd, "-ssl", "-unsafeSsl") diff --git a/db/mongo_console.go b/db/mongo_console.go index 67526fb2..b013268b 100644 --- a/db/mongo_console.go +++ b/db/mongo_console.go @@ -22,7 +22,7 @@ func MongoConsole(ctx context.Context, opts MongoConsoleOpts) error { return errors.Wrapf(ctx, err, "resolve MongoDB URL from %s", opts.VariableName) } - command := []string{"dbclient-fetcher", "mongo", "&&", "mongo"} + command := []string{dbClientFetcher, "mongo", "&&", "mongo"} if mongoURL.Query().Get("ssl") == "true" { command = append(command, "--ssl", "--sslAllowInvalidCertificates") } diff --git a/db/mysql_console.go b/db/mysql_console.go index 06192ad3..becb1c7d 100644 --- a/db/mysql_console.go +++ b/db/mysql_console.go @@ -32,7 +32,7 @@ func MySQLConsole(ctx context.Context, opts MySQLConsoleOpts) error { runOpts := apps.RunOpts{ DisplayCmd: "mysql-console " + user, App: opts.App, - Cmd: []string{"dbclient-fetcher", "mysql", "&&", "mysql", "-h", host, "-P", port, fmt.Sprintf("--password=%v", password), "-u", user, user}, + Cmd: []string{dbClientFetcher, "mysql", "&&", "mysql", "-h", host, "-P", port, fmt.Sprintf("--password=%v", password), "-u", user, user}, Size: opts.Size, } diff --git a/db/pgsql_console.go b/db/pgsql_console.go index a9ea5277..aec1bf15 100644 --- a/db/pgsql_console.go +++ b/db/pgsql_console.go @@ -25,7 +25,7 @@ func PgSQLConsole(ctx context.Context, opts PgSQLConsoleOpts) error { runOpts := apps.RunOpts{ DisplayCmd: "pgsql-console " + user, App: opts.App, - Cmd: []string{"dbclient-fetcher", "pgsql", "&&", "psql", "'" + postgreSQLURL.String() + "'"}, + Cmd: []string{dbClientFetcher, "pgsql", "&&", "psql", "'" + postgreSQLURL.String() + "'"}, Size: opts.Size, } diff --git a/db/redis_console.go b/db/redis_console.go index 37a8c893..e8ae619a 100644 --- a/db/redis_console.go +++ b/db/redis_console.go @@ -1,10 +1,8 @@ package db import ( - "bytes" "context" "fmt" - "io" "net" "strings" @@ -39,9 +37,9 @@ func RedisConsole(ctx context.Context, opts RedisConsoleOpts) error { runOpts := apps.RunOpts{ DisplayCmd: "redis-console " + strings.Split(host, ".")[0], App: opts.App, - Cmd: []string{"dbclient-fetcher", "redis", "&&", "redis-cli", "-h", host, "-p", port, "-a", password}, + Cmd: []string{dbClientFetcher, "redis", "&&", "redis-cli", "-h", host, "-p", port, "-a", password}, Size: opts.Size, - StdinCopyFunc: redisStdinCopy, + StdinCopyFunc: valkeyStdinCopy, } err = apps.Run(ctx, runOpts) @@ -51,32 +49,3 @@ func RedisConsole(ctx context.Context, opts RedisConsoleOpts) error { return nil } - -func redisStdinCopy(dst io.Writer, src io.Reader) (int64, error) { - var written int64 - buf := make([]byte, 2*1024) - for { - nr, er := src.Read(buf) - if nr > 0 { - toWrite := bytes.ReplaceAll(buf[0:nr], []byte{'\n'}, []byte{'\r', '\n'}) - nr = len(toWrite) - nw, ew := dst.Write(toWrite) - if nw > 0 { - written += int64(nw) - } - if ew != nil { - return written, ew - } - if nr != nw { - return written, io.ErrShortWrite - } - } - if er == io.EOF { - break - } - if er != nil { - return written, er - } - } - return written, nil -} diff --git a/db/valkey_console.go b/db/valkey_console.go new file mode 100644 index 00000000..5abe1171 --- /dev/null +++ b/db/valkey_console.go @@ -0,0 +1,80 @@ +package db + +import ( + "bytes" + "context" + "fmt" + "io" + "net" + "strings" + + "github.com/Scalingo/cli/apps" + "github.com/Scalingo/go-utils/errors/v3" +) + +type ValkeyConsoleOpts struct { + App string + Size string + VariableName string +} + +func ValkeyConsole(ctx context.Context, opts ValkeyConsoleOpts) error { + if opts.VariableName == "" { + opts.VariableName = "SCALINGO_VALKEY" + } + valkeyURL, _, password, err := dbURL(ctx, opts.App, opts.VariableName, []string{"valkey", "valkeys"}) + if err != nil { + return errors.Wrapf(ctx, err, "resolve Valkey URL from %s", opts.VariableName) + } + + if valkeyURL.Scheme == "valkeys" { + return errors.New(ctx, "Valkey console is not available when TLS connections are enforced") + } + + host, port, err := net.SplitHostPort(valkeyURL.Host) + if err != nil { + return fmt.Errorf("%v has an invalid host", valkeyURL) + } + + err = apps.Run(ctx, apps.RunOpts{ + DisplayCmd: "valkey-console " + strings.Split(host, ".")[0], + App: opts.App, + Cmd: []string{dbClientFetcher, "valkey", "&&", "valkey-cli", "-h", host, "-p", port, "-a", password}, + Size: opts.Size, + StdinCopyFunc: valkeyStdinCopy, + }) + if err != nil { + return fmt.Errorf("run Valkey console: %v", err) + } + + return nil +} + +func valkeyStdinCopy(dst io.Writer, src io.Reader) (int64, error) { + var written int64 + buf := make([]byte, 2*1024) + for { + nr, er := src.Read(buf) + if nr > 0 { + toWrite := bytes.ReplaceAll(buf[0:nr], []byte{'\n'}, []byte{'\r', '\n'}) + nr = len(toWrite) + nw, ew := dst.Write(toWrite) + if nw > 0 { + written += int64(nw) + } + if ew != nil { + return written, ew + } + if nr != nw { + return written, io.ErrShortWrite + } + } + if er == io.EOF { + break + } + if er != nil { + return written, er + } + } + return written, nil +}