Skip to content

Commit ca877a2

Browse files
feat(ngwaf/rules): adds account level and workspace level operations for ngwaf rules using json file inputs
1 parent 91fb167 commit ca877a2

File tree

26 files changed

+1933
-12
lines changed

26 files changed

+1933
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
### Enhancements:
88
- feat(rust): Allow testing with prerelease Rust versions ([#1604](https://github.com/fastly/cli/pull/1604))
9+
- feat(commands/ngwaf/rules): add support for CRUD operations for NGWAF rules ([#1578](https://github.com/fastly/cli/pull/1605))
910

1011
### Bug fixes:
1112

pkg/commands/commands.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import (
5959
"github.com/fastly/cli/pkg/commands/ngwaf/countrylist"
6060
"github.com/fastly/cli/pkg/commands/ngwaf/customsignal"
6161
"github.com/fastly/cli/pkg/commands/ngwaf/iplist"
62+
"github.com/fastly/cli/pkg/commands/ngwaf/rule"
6263
"github.com/fastly/cli/pkg/commands/ngwaf/signallist"
6364
"github.com/fastly/cli/pkg/commands/ngwaf/stringlist"
6465
"github.com/fastly/cli/pkg/commands/ngwaf/wildcardlist"
@@ -76,6 +77,7 @@ import (
7677
wscustomsignal "github.com/fastly/cli/pkg/commands/ngwaf/workspace/customsignal"
7778
wsiplist "github.com/fastly/cli/pkg/commands/ngwaf/workspace/iplist"
7879
"github.com/fastly/cli/pkg/commands/ngwaf/workspace/redaction"
80+
workspaceRule "github.com/fastly/cli/pkg/commands/ngwaf/workspace/rule"
7981
wssignallistlist "github.com/fastly/cli/pkg/commands/ngwaf/workspace/signallist"
8082
wsstringlistlist "github.com/fastly/cli/pkg/commands/ngwaf/workspace/stringlist"
8183
"github.com/fastly/cli/pkg/commands/ngwaf/workspace/threshold"
@@ -450,6 +452,12 @@ func Define( // nolint:revive // function-length
450452
ngwafIPListGet := iplist.NewGetCommand(ngwafIPListRoot.CmdClause, data)
451453
ngwafIPListList := iplist.NewListCommand(ngwafIPListRoot.CmdClause, data)
452454
ngwafIPListUpdate := iplist.NewUpdateCommand(ngwafIPListRoot.CmdClause, data)
455+
ngwafRuleRoot := rule.NewRootCommand(ngwafRoot.CmdClause, data)
456+
ngwafRuleCreate := rule.NewCreateCommand(ngwafRuleRoot.CmdClause, data)
457+
ngwafRuleDelete := rule.NewDeleteCommand(ngwafRuleRoot.CmdClause, data)
458+
ngwafRuleGet := rule.NewGetCommand(ngwafRuleRoot.CmdClause, data)
459+
ngwafRuleList := rule.NewListCommand(ngwafRuleRoot.CmdClause, data)
460+
ngwafRuleUpdate := rule.NewUpdateCommand(ngwafRuleRoot.CmdClause, data)
453461
ngwafSignalListRoot := signallist.NewRootCommand(ngwafRoot.CmdClause, data)
454462
ngwafSignalListCreate := signallist.NewCreateCommand(ngwafSignalListRoot.CmdClause, data)
455463
ngwafSignalListDelete := signallist.NewDeleteCommand(ngwafSignalListRoot.CmdClause, data)
@@ -486,6 +494,12 @@ func Define( // nolint:revive // function-length
486494
ngwafWorkspaceIPListGet := wsiplist.NewGetCommand(ngwafWorkspaceIPListRoot.CmdClause, data)
487495
ngwafWorkspaceIPListList := wsiplist.NewListCommand(ngwafWorkspaceIPListRoot.CmdClause, data)
488496
ngwafWorkspaceIPListUpdate := wsiplist.NewUpdateCommand(ngwafWorkspaceIPListRoot.CmdClause, data)
497+
ngwafWorkspaceRuleRoot := workspaceRule.NewRootCommand(ngwafWorkspaceRoot.CmdClause, data)
498+
ngwafWorkspaceRuleCreate := workspaceRule.NewCreateCommand(ngwafWorkspaceRuleRoot.CmdClause, data)
499+
ngwafWorkspaceRuleDelete := workspaceRule.NewDeleteCommand(ngwafWorkspaceRuleRoot.CmdClause, data)
500+
ngwafWorkspaceRuleGet := workspaceRule.NewGetCommand(ngwafWorkspaceRuleRoot.CmdClause, data)
501+
ngwafWorkspaceRuleList := workspaceRule.NewListCommand(ngwafWorkspaceRuleRoot.CmdClause, data)
502+
ngwafWorkspaceRuleUpdate := workspaceRule.NewUpdateCommand(ngwafWorkspaceRuleRoot.CmdClause, data)
489503
ngwafWorkspaceSignalListRoot := wssignallistlist.NewRootCommand(ngwafWorkspaceRoot.CmdClause, data)
490504
ngwafWorkspaceSignalListCreate := wssignallistlist.NewCreateCommand(ngwafWorkspaceSignalListRoot.CmdClause, data)
491505
ngwafWorkspaceSignalListDelete := wssignallistlist.NewDeleteCommand(ngwafWorkspaceSignalListRoot.CmdClause, data)
@@ -1009,6 +1023,12 @@ func Define( // nolint:revive // function-length
10091023
ngwafIPListGet,
10101024
ngwafIPListList,
10111025
ngwafIPListUpdate,
1026+
ngwafRuleRoot,
1027+
ngwafRuleCreate,
1028+
ngwafRuleDelete,
1029+
ngwafRuleGet,
1030+
ngwafRuleList,
1031+
ngwafRuleUpdate,
10121032
ngwafSignalListRoot,
10131033
ngwafSignalListCreate,
10141034
ngwafSignalListDelete,
@@ -1044,6 +1064,12 @@ func Define( // nolint:revive // function-length
10441064
ngwafWorkspaceIPListGet,
10451065
ngwafWorkspaceIPListList,
10461066
ngwafWorkspaceIPListUpdate,
1067+
ngwafWorkspaceRuleRoot,
1068+
ngwafWorkspaceRuleCreate,
1069+
ngwafWorkspaceRuleDelete,
1070+
ngwafWorkspaceRuleGet,
1071+
ngwafWorkspaceRuleList,
1072+
ngwafWorkspaceRuleUpdate,
10471073
ngwafWorkspaceSignalListRoot,
10481074
ngwafWorkspaceSignalListCreate,
10491075
ngwafWorkspaceSignalListDelete,

pkg/commands/ngwaf/rule/create.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package rule
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"log"
10+
"os"
11+
"path/filepath"
12+
13+
"github.com/fastly/go-fastly/v12/fastly"
14+
"github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/rules"
15+
"github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/scope"
16+
17+
"github.com/fastly/cli/pkg/argparser"
18+
fsterr "github.com/fastly/cli/pkg/errors"
19+
"github.com/fastly/cli/pkg/global"
20+
"github.com/fastly/cli/pkg/text"
21+
)
22+
23+
// CreateCommand calls the Fastly API to create account-level rules.
24+
type CreateCommand struct {
25+
argparser.Base
26+
argparser.JSONOutput
27+
28+
// Required.
29+
path string
30+
}
31+
32+
// NewCreateCommand returns a usable command registered under the parent.
33+
func NewCreateCommand(parent argparser.Registerer, g *global.Data) *CreateCommand {
34+
c := CreateCommand{
35+
Base: argparser.Base{
36+
Globals: g,
37+
},
38+
}
39+
c.CmdClause = parent.Command("create", "Create an account-level rule").Alias("add")
40+
41+
// Required.
42+
c.CmdClause.Flag("path", "Path to a json file that contains the rule schema.").Required().StringVar(&c.path)
43+
44+
// Optional.
45+
c.RegisterFlagBool(c.JSONFlag())
46+
47+
return &c
48+
}
49+
50+
// Exec invokes the application logic for the command.
51+
func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error {
52+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
53+
return fsterr.ErrInvalidVerboseJSONCombo
54+
}
55+
var err error
56+
input := &rules.CreateInput{}
57+
if c.path != "" {
58+
path, err := filepath.Abs(c.path)
59+
if err != nil {
60+
return fmt.Errorf("error parsing path '%s': %q", c.path, err)
61+
}
62+
63+
jsonFile, err := os.Open(path)
64+
if err != nil {
65+
return fmt.Errorf("error reading cert-path '%s': %q", c.path, err)
66+
}
67+
defer jsonFile.Close()
68+
69+
byteValue, err := io.ReadAll(jsonFile)
70+
if err != nil {
71+
log.Fatalf("failed to read json file: %v", err)
72+
}
73+
74+
if err := json.Unmarshal(byteValue, input); err != nil {
75+
log.Fatalf("failed to unmarshal json data: %v", err)
76+
}
77+
}
78+
input.Scope = &scope.Scope{
79+
Type: scope.ScopeTypeAccount,
80+
AppliesTo: []string{"*"},
81+
}
82+
83+
fc, ok := c.Globals.APIClient.(*fastly.Client)
84+
if !ok {
85+
return errors.New("failed to convert interface to a fastly client")
86+
}
87+
88+
data, err := rules.Create(context.TODO(), fc, input)
89+
if err != nil {
90+
return err
91+
}
92+
93+
if ok, err := c.WriteJSON(out, data); ok {
94+
return err
95+
}
96+
97+
text.Success(out, "Created account-level rule with ID %s", data.RuleID)
98+
return nil
99+
}

pkg/commands/ngwaf/rule/delete.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package rule
2+
3+
import (
4+
"context"
5+
"errors"
6+
"io"
7+
8+
"github.com/fastly/go-fastly/v12/fastly"
9+
10+
"github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/rules"
11+
"github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/scope"
12+
13+
"github.com/fastly/cli/pkg/argparser"
14+
fsterr "github.com/fastly/cli/pkg/errors"
15+
"github.com/fastly/cli/pkg/global"
16+
"github.com/fastly/cli/pkg/text"
17+
)
18+
19+
// DeleteCommand calls the Fastly API to delete an account-level rule.
20+
type DeleteCommand struct {
21+
argparser.Base
22+
argparser.JSONOutput
23+
24+
// Required.
25+
ruleID string
26+
}
27+
28+
// NewDeleteCommand returns a usable command registered under the parent.
29+
func NewDeleteCommand(parent argparser.Registerer, g *global.Data) *DeleteCommand {
30+
c := DeleteCommand{
31+
Base: argparser.Base{
32+
Globals: g,
33+
},
34+
}
35+
36+
c.CmdClause = parent.Command("delete", "Delete an account-level rule")
37+
38+
// Required.
39+
c.CmdClause.Flag("rule-id", "Rule ID").Required().StringVar(&c.ruleID)
40+
41+
// Optional.
42+
c.RegisterFlagBool(c.JSONFlag())
43+
44+
return &c
45+
}
46+
47+
// Exec invokes the application logic for the command.
48+
func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error {
49+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
50+
return fsterr.ErrInvalidVerboseJSONCombo
51+
}
52+
53+
fc, ok := c.Globals.APIClient.(*fastly.Client)
54+
if !ok {
55+
return errors.New("failed to convert interface to a fastly client")
56+
}
57+
58+
err := rules.Delete(context.TODO(), fc, &rules.DeleteInput{
59+
RuleID: &c.ruleID,
60+
Scope: &scope.Scope{
61+
Type: scope.ScopeTypeAccount,
62+
AppliesTo: []string{"*"},
63+
},
64+
})
65+
if err != nil {
66+
c.Globals.ErrLog.Add(err)
67+
return err
68+
}
69+
70+
if c.JSONOutput.Enabled {
71+
o := struct {
72+
ID string `json:"id"`
73+
Deleted bool `json:"deleted"`
74+
}{
75+
c.ruleID,
76+
true,
77+
}
78+
_, err := c.WriteJSON(out, o)
79+
return err
80+
}
81+
82+
text.Success(out, "Deleted account-level rule with id: %s", c.ruleID)
83+
return nil
84+
}

pkg/commands/ngwaf/rule/doc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package rule contains commands to inspect and manipulate NGWAF account-level rules.
2+
package rule

pkg/commands/ngwaf/rule/get.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package rule
2+
3+
import (
4+
"context"
5+
"errors"
6+
"io"
7+
8+
"github.com/fastly/go-fastly/v12/fastly"
9+
10+
"github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/rules"
11+
"github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/scope"
12+
13+
"github.com/fastly/cli/pkg/argparser"
14+
fsterr "github.com/fastly/cli/pkg/errors"
15+
"github.com/fastly/cli/pkg/global"
16+
"github.com/fastly/cli/pkg/text"
17+
)
18+
19+
// GetCommand calls the Fastly API to get an account-level rule.
20+
type GetCommand struct {
21+
argparser.Base
22+
argparser.JSONOutput
23+
24+
// Required.
25+
ruleID string
26+
}
27+
28+
// NewGetCommand returns a usable command registered under the parent.
29+
func NewGetCommand(parent argparser.Registerer, g *global.Data) *GetCommand {
30+
c := GetCommand{
31+
Base: argparser.Base{
32+
Globals: g,
33+
},
34+
}
35+
36+
c.CmdClause = parent.Command("get", "Get an account-level rule")
37+
38+
// Required.
39+
c.CmdClause.Flag("rule-id", "Rule ID").Required().StringVar(&c.ruleID)
40+
41+
// Optional.
42+
c.RegisterFlagBool(c.JSONFlag())
43+
44+
return &c
45+
}
46+
47+
// Exec invokes the application logic for the command.
48+
func (c *GetCommand) Exec(_ io.Reader, out io.Writer) error {
49+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
50+
return fsterr.ErrInvalidVerboseJSONCombo
51+
}
52+
53+
fc, ok := c.Globals.APIClient.(*fastly.Client)
54+
if !ok {
55+
return errors.New("failed to convert interface to a fastly client")
56+
}
57+
58+
data, err := rules.Get(context.TODO(), fc, &rules.GetInput{
59+
RuleID: &c.ruleID,
60+
Scope: &scope.Scope{
61+
Type: scope.ScopeTypeAccount,
62+
AppliesTo: []string{"*"},
63+
},
64+
})
65+
if err != nil {
66+
c.Globals.ErrLog.Add(err)
67+
return err
68+
}
69+
70+
if ok, err := c.WriteJSON(out, data); ok {
71+
return err
72+
}
73+
74+
text.PrintRule(out, data)
75+
return nil
76+
}

0 commit comments

Comments
 (0)