Repository navigation
[STORY-3093] feat(dbng): add firewall rules commands #1171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
b4cadcb
feat(dbng): add firewall rules commands for DBNG databases
briceamen 9efbce0
refactor: improve column header clarity in firewall rules list
briceamen c7a5475
feat(dbng): add positional arg support for firewall rules commands
briceamen 27dc5a3
fix: add validation for multiple addons in GetAddonIDFromDatabase
briceamen ea3aaaa
refactor: use switch statements for argument handling in firewall com…
briceamen 66c53d4
fix: improve error messages for remove command argument validation
briceamen 872f60e
docs: clarify addon list comment for DBNG databases
briceamen cf2bad7
fix: improve warning message wording for firewall rules
briceamen 63fd066
refactor: rename databaseID to databaseName and extract GetDatabaseFr…
briceamen f3301ab
refactor: use GetDatabaseFromArgs helper in firewall rules commands
briceamen 6ca3e6e
fix: improve warning message wording for firewall rules
briceamen e7e85e0
refactor: use errors.Is and support labels for managed ranges
briceamen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,229 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
|
|
||
| "github.com/urfave/cli/v3" | ||
|
|
||
| "github.com/Scalingo/cli/cmd/autocomplete" | ||
| "github.com/Scalingo/cli/dbng" | ||
| "github.com/Scalingo/cli/detect" | ||
| "github.com/Scalingo/cli/io" | ||
| "github.com/Scalingo/cli/utils" | ||
| "github.com/Scalingo/go-scalingo/v8" | ||
| ) | ||
|
|
||
| var ( | ||
| databaseFirewallRulesListCommand = cli.Command{ | ||
| Name: "database-firewall-rules", | ||
| Category: "Databases NG", | ||
| Usage: "List firewall rules of a database", | ||
| ArgsUsage: "database-id", | ||
| Flags: []cli.Flag{databaseFlag()}, | ||
| Description: CommandDescription{ | ||
| Description: "List all firewall rules of a database next generation", | ||
| Examples: []string{ | ||
| "scalingo database-firewall-rules my-db-id", | ||
| "scalingo --database my-db database-firewall-rules", | ||
| }, | ||
| SeeAlso: []string{"database-firewall-rules-add", "database-firewall-rules-remove", "database-firewall-managed-ranges"}, | ||
| }.Render(), | ||
| Action: func(ctx context.Context, c *cli.Command) error { | ||
| databaseName, addonID, err := detect.GetDatabaseFromArgs(ctx, c) | ||
| if errors.Is(err, detect.ErrTooManyArguments) { | ||
| io.Error(err) | ||
| return cli.ShowCommandHelp(ctx, c, "database-firewall-rules") | ||
| } | ||
| if err != nil { | ||
| errorQuit(ctx, err) | ||
| } | ||
|
|
||
| utils.CheckForConsent(ctx, databaseName, utils.ConsentTypeDBs) | ||
|
|
||
| err = dbng.FirewallRulesList(ctx, databaseName, addonID) | ||
| if err != nil { | ||
| errorQuit(ctx, err) | ||
| } | ||
|
|
||
| return nil | ||
| }, | ||
| ShellComplete: func(ctx context.Context, c *cli.Command) { | ||
| _ = autocomplete.CmdFlagsAutoComplete(c, "database-firewall-rules") | ||
| _ = autocomplete.DatabasesNgListAutoComplete(ctx) | ||
| }, | ||
| } | ||
|
|
||
| databaseFirewallRulesAddCommand = cli.Command{ | ||
| Name: "database-firewall-rules-add", | ||
| Category: "Databases NG", | ||
| Usage: "Add a firewall rule to a database", | ||
| ArgsUsage: "database-id", | ||
| Flags: []cli.Flag{ | ||
| databaseFlag(), | ||
| &cli.StringFlag{Name: "cidr", Usage: "CIDR range (e.g., 203.0.113.0/24)"}, | ||
| &cli.StringFlag{Name: "label", Usage: "Label for the custom rule"}, | ||
| &cli.StringFlag{Name: "managed-range", Usage: "Managed range ID"}, | ||
| }, | ||
| Description: CommandDescription{ | ||
| Description: "Add a firewall rule to a database next generation. Either --cidr or --managed-range must be specified, but not both.", | ||
| Examples: []string{ | ||
| "scalingo database-firewall-rules-add my-db-id --cidr 203.0.113.0/24 --label \"Office network\"", | ||
| "scalingo --database my-db database-firewall-rules-add --managed-range mr-scalingo-osc-fr1", | ||
| }, | ||
| SeeAlso: []string{"database-firewall-rules", "database-firewall-rules-remove", "database-firewall-managed-ranges"}, | ||
| }.Render(), | ||
| Action: func(ctx context.Context, c *cli.Command) error { | ||
| databaseName, addonID, err := detect.GetDatabaseFromArgs(ctx, c) | ||
| if errors.Is(err, detect.ErrTooManyArguments) { | ||
| io.Error(err) | ||
| return cli.ShowCommandHelp(ctx, c, "database-firewall-rules-add") | ||
| } | ||
| if err != nil { | ||
| errorQuit(ctx, err) | ||
|
Comment on lines
+82
to
+83
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above |
||
| } | ||
|
|
||
| cidr := c.String("cidr") | ||
| label := c.String("label") | ||
| managedRange := c.String("managed-range") | ||
|
|
||
| if cidr == "" && managedRange == "" { | ||
| io.Error("Either --cidr or --managed-range must be specified") | ||
| return cli.ShowCommandHelp(ctx, c, "database-firewall-rules-add") | ||
| } | ||
|
|
||
| if cidr != "" && managedRange != "" { | ||
| io.Error("Cannot specify both --cidr and --managed-range") | ||
| return cli.ShowCommandHelp(ctx, c, "database-firewall-rules-add") | ||
| } | ||
|
|
||
| utils.CheckForConsent(ctx, databaseName, utils.ConsentTypeDBs) | ||
|
|
||
| var params scalingo.FirewallRuleCreateParams | ||
| if cidr != "" { | ||
| params = scalingo.FirewallRuleCreateParams{ | ||
| Type: scalingo.FirewallRuleTypeCustomRange, | ||
| CIDR: cidr, | ||
| Label: label, | ||
| } | ||
| } else { | ||
| params = scalingo.FirewallRuleCreateParams{ | ||
| Type: scalingo.FirewallRuleTypeManagedRange, | ||
| RangeID: managedRange, | ||
|
matthieu526-scalingo marked this conversation as resolved.
Resolved
|
||
| Label: label, | ||
| } | ||
| } | ||
|
|
||
| err = dbng.FirewallRulesAdd(ctx, databaseName, addonID, params) | ||
| if err != nil { | ||
| errorQuit(ctx, err) | ||
| } | ||
|
|
||
| return nil | ||
| }, | ||
| ShellComplete: func(ctx context.Context, c *cli.Command) { | ||
| _ = autocomplete.CmdFlagsAutoComplete(c, "database-firewall-rules-add") | ||
| _ = autocomplete.DatabasesNgListAutoComplete(ctx) | ||
| }, | ||
| } | ||
|
|
||
| databaseFirewallRulesRemoveCommand = cli.Command{ | ||
| Name: "database-firewall-rules-remove", | ||
| Category: "Databases NG", | ||
| Usage: "Remove a firewall rule from a database", | ||
| ArgsUsage: "database-id rule-id", | ||
| Flags: []cli.Flag{databaseFlag()}, | ||
| Description: CommandDescription{ | ||
| Description: "Remove a firewall rule from a database next generation", | ||
| Examples: []string{ | ||
| "scalingo database-firewall-rules-remove my-db-id rule-id", | ||
| "scalingo --database my-db database-firewall-rules-remove rule-id", | ||
| }, | ||
| SeeAlso: []string{"database-firewall-rules", "database-firewall-rules-add", "database-firewall-managed-ranges"}, | ||
| }.Render(), | ||
| Action: func(ctx context.Context, c *cli.Command) error { | ||
| var databaseName, addonID, ruleID string | ||
| var err error | ||
|
|
||
| args := c.Args().Slice() | ||
| switch len(args) { | ||
| case 0: | ||
| // No arguments provided | ||
| io.Error("Missing required argument: rule-id") | ||
| return cli.ShowCommandHelp(ctx, c, "database-firewall-rules-remove") | ||
| case 1: | ||
| // Only rule-id provided, database from --database flag | ||
| databaseName, addonID = detect.GetCurrentDatabase(ctx, c) | ||
| ruleID = args[0] | ||
| case 2: | ||
| // Both database-name and rule-id provided as positional args | ||
| databaseName = args[0] | ||
| ruleID = args[1] | ||
| addonID, err = detect.GetAddonIDFromDatabase(ctx, databaseName) | ||
| if err != nil { | ||
| errorQuit(ctx, err) | ||
| } | ||
| default: | ||
| io.Error("Invalid number of arguments") | ||
| return cli.ShowCommandHelp(ctx, c, "database-firewall-rules-remove") | ||
| } | ||
|
|
||
| if databaseName == "" { | ||
| io.Error("Please provide a database name or use --database flag") | ||
| return cli.ShowCommandHelp(ctx, c, "database-firewall-rules-remove") | ||
| } | ||
|
|
||
| utils.CheckForConsent(ctx, databaseName, utils.ConsentTypeDBs) | ||
|
|
||
| err = dbng.FirewallRulesRemove(ctx, databaseName, addonID, ruleID) | ||
| if err != nil { | ||
| errorQuit(ctx, err) | ||
| } | ||
|
|
||
| return nil | ||
| }, | ||
| ShellComplete: func(ctx context.Context, c *cli.Command) { | ||
| _ = autocomplete.CmdFlagsAutoComplete(c, "database-firewall-rules-remove") | ||
| _ = autocomplete.DatabasesNgListAutoComplete(ctx) | ||
| }, | ||
| } | ||
|
|
||
| databaseFirewallManagedRangesCommand = cli.Command{ | ||
| Name: "database-firewall-managed-ranges", | ||
| Category: "Databases NG", | ||
| Usage: "List available managed ranges for a database", | ||
| ArgsUsage: "database-id", | ||
| Flags: []cli.Flag{databaseFlag()}, | ||
| Description: CommandDescription{ | ||
| Description: "List all available managed ranges for firewall rules of a database next generation", | ||
| Examples: []string{ | ||
| "scalingo database-firewall-managed-ranges my-db-id", | ||
| "scalingo --database my-db database-firewall-managed-ranges", | ||
| }, | ||
| SeeAlso: []string{"database-firewall-rules", "database-firewall-rules-add", "database-firewall-rules-remove"}, | ||
| }.Render(), | ||
| Action: func(ctx context.Context, c *cli.Command) error { | ||
| databaseName, addonID, err := detect.GetDatabaseFromArgs(ctx, c) | ||
| if errors.Is(err, detect.ErrTooManyArguments) { | ||
| io.Error(err) | ||
| return cli.ShowCommandHelp(ctx, c, "database-firewall-managed-ranges") | ||
| } | ||
| if err != nil { | ||
| errorQuit(ctx, err) | ||
| } | ||
|
Comment on lines
+211
to
213
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above |
||
|
|
||
| utils.CheckForConsent(ctx, databaseName, utils.ConsentTypeDBs) | ||
|
|
||
| err = dbng.FirewallManagedRangesList(ctx, databaseName, addonID) | ||
| if err != nil { | ||
| errorQuit(ctx, err) | ||
| } | ||
|
|
||
| return nil | ||
| }, | ||
| ShellComplete: func(ctx context.Context, c *cli.Command) { | ||
| _ = autocomplete.CmdFlagsAutoComplete(c, "database-firewall-managed-ranges") | ||
| _ = autocomplete.DatabasesNgListAutoComplete(ctx) | ||
| }, | ||
| } | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| package dbng | ||
|
|
||
| import ( | ||
| "context" | ||
| "os" | ||
|
|
||
| "github.com/olekukonko/tablewriter" | ||
|
|
||
| "github.com/Scalingo/cli/config" | ||
| "github.com/Scalingo/cli/io" | ||
| "github.com/Scalingo/go-scalingo/v8" | ||
| "github.com/Scalingo/go-utils/errors/v2" | ||
|
sc-david-voisin marked this conversation as resolved.
Resolved
|
||
| ) | ||
|
|
||
| func FirewallRulesList(ctx context.Context, databaseID, addonID string) error { | ||
| c, err := config.ScalingoClient(ctx) | ||
| if err != nil { | ||
| return errors.Wrap(ctx, err, "get Scalingo client") | ||
| } | ||
|
|
||
| rules, err := c.Preview().FirewallRulesList(ctx, databaseID, addonID) | ||
| if err != nil { | ||
| return errors.Wrap(ctx, err, "list firewall rules") | ||
| } | ||
|
|
||
| if len(rules) == 0 { | ||
| io.Status("No firewall rules configured for this database.") | ||
| return nil | ||
| } | ||
|
|
||
| t := tablewriter.NewWriter(os.Stdout) | ||
| t.Header([]string{"ID", "Type", "CIDR or Range ID", "Label"}) | ||
|
|
||
| for _, rule := range rules { | ||
| cidrOrRange := rule.CIDR | ||
| if rule.Type == scalingo.FirewallRuleTypeManagedRange { | ||
| cidrOrRange = rule.RangeID | ||
| } | ||
|
|
||
| _ = t.Append([]string{ | ||
| rule.ID, | ||
| string(rule.Type), | ||
| cidrOrRange, | ||
| rule.Label, | ||
| }) | ||
|
briceamen marked this conversation as resolved.
Resolved
|
||
| } | ||
| _ = t.Render() | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func FirewallRulesAdd(ctx context.Context, databaseID, addonID string, params scalingo.FirewallRuleCreateParams) error { | ||
| c, err := config.ScalingoClient(ctx) | ||
| if err != nil { | ||
| return errors.Wrap(ctx, err, "get Scalingo client") | ||
| } | ||
|
|
||
| rule, err := c.Preview().FirewallRulesCreate(ctx, databaseID, addonID, params) | ||
| if err != nil { | ||
| return errors.Wrap(ctx, err, "add firewall rule") | ||
| } | ||
|
|
||
| io.Statusf("Firewall rule '%s' has been added to database '%s'.\n", rule.ID, databaseID) | ||
| io.Warning("Expect some delay for the firewall rules to be applied.") | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func FirewallRulesRemove(ctx context.Context, databaseID, addonID, ruleID string) error { | ||
| c, err := config.ScalingoClient(ctx) | ||
| if err != nil { | ||
| return errors.Wrap(ctx, err, "get Scalingo client") | ||
| } | ||
|
|
||
| err = c.Preview().FirewallRulesDestroy(ctx, databaseID, addonID, ruleID) | ||
| if err != nil { | ||
| return errors.Wrap(ctx, err, "remove firewall rule") | ||
| } | ||
|
|
||
| io.Statusf("Firewall rule '%s' has been removed from database '%s'.\n", ruleID, databaseID) | ||
| io.Warning("Expect some delay for the firewall rules to be applied.") | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func FirewallManagedRangesList(ctx context.Context, databaseID, addonID string) error { | ||
| c, err := config.ScalingoClient(ctx) | ||
| if err != nil { | ||
| return errors.Wrap(ctx, err, "get Scalingo client") | ||
| } | ||
|
|
||
| ranges, err := c.Preview().FirewallRulesGetManagedRanges(ctx, databaseID, addonID) | ||
| if err != nil { | ||
| return errors.Wrap(ctx, err, "list managed ranges") | ||
| } | ||
|
|
||
| if len(ranges) == 0 { | ||
| io.Status("No managed ranges available.") | ||
| return nil | ||
| } | ||
|
|
||
| t := tablewriter.NewWriter(os.Stdout) | ||
| t.Header([]string{"ID", "Name"}) | ||
|
|
||
| for _, r := range ranges { | ||
| _ = t.Append([]string{ | ||
| r.ID, | ||
| r.Name, | ||
| }) | ||
| } | ||
| _ = t.Render() | ||
|
|
||
| return nil | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Could be simplified by:
In addition, this is a good practice to use
errors.Isinstead of direct comparison.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much cleaner indeed. Thanks.