|
| 1 | +package workspaces |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "errors" |
| 6 | + "io" |
| 7 | + "strconv" |
| 8 | + "strings" |
| 9 | + |
| 10 | + "github.com/fastly/go-fastly/v12/fastly" |
| 11 | + "github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/workspaces" |
| 12 | + |
| 13 | + "github.com/fastly/cli/pkg/argparser" |
| 14 | + "github.com/fastly/cli/pkg/global" |
| 15 | + "github.com/fastly/cli/pkg/text" |
| 16 | +) |
| 17 | + |
| 18 | +// CreateCommand calls the Fastly API to create domains. |
| 19 | +type CreateCommand struct { |
| 20 | + argparser.Base |
| 21 | + argparser.JSONOutput |
| 22 | + |
| 23 | + // Required. |
| 24 | + description string |
| 25 | + blockingMode string |
| 26 | + name string |
| 27 | + |
| 28 | + // Optional. |
| 29 | + attackThresholds argparser.OptionalString |
| 30 | + defaultBlockingCode argparser.OptionalInt |
| 31 | + defaultRedirectURL argparser.OptionalString |
| 32 | + clientIPHeaders argparser.OptionalString |
| 33 | + ipAnonimization argparser.OptionalString |
| 34 | +} |
| 35 | + |
| 36 | +// NewCreateCommand returns a usable command registered under the parent. |
| 37 | +func NewCreateCommand(parent argparser.Registerer, g *global.Data) *CreateCommand { |
| 38 | + c := CreateCommand{ |
| 39 | + Base: argparser.Base{ |
| 40 | + Globals: g, |
| 41 | + }, |
| 42 | + } |
| 43 | + c.CmdClause = parent.Command("create", "Create a workspace").Alias("add") |
| 44 | + |
| 45 | + // Required. |
| 46 | + c.CmdClause.Flag("description", "User submitted description of a workspace.").Required().StringVar(&c.description) |
| 47 | + c.CmdClause.Flag("blockingMode", "User configured mode blocking mode.").Required().StringVar(&c.blockingMode) |
| 48 | + c.CmdClause.Flag("name", "User submitted display name of a workspace.").Required().StringVar(&c.name) |
| 49 | + |
| 50 | + // Optional. |
| 51 | + c.CmdClause.Flag("attackThresholds", "Attack threshold parameters for system site alerts. Each threshold value is the number of attack signals per IP address that must be detected during the interval before the related IP address is flagged. Input accepted as colon separated string: Immediate:OneMinute:TenMinutes:OneHour").Action(c.attackThresholds.Set).StringVar(&c.attackThresholds.Value) |
| 52 | + c.CmdClause.Flag("clientIPHeaders", "Specify the request header containing the client IP address. Input accepted as comma separated string.").Action(c.clientIPHeaders.Set).StringVar(&c.clientIPHeaders.Value) |
| 53 | + c.CmdClause.Flag("defaultBlockingCode", "Default status code that is returned when a request to your web application is blocked.").Action(c.defaultBlockingCode.Set).IntVar(&c.defaultBlockingCode.Value) |
| 54 | + c.CmdClause.Flag("defaultRedirectURL", "Redirect url to be used if code 301 or 302 is used.").Action(c.defaultRedirectURL.Set).StringVar(&c.defaultRedirectURL.Value) |
| 55 | + c.CmdClause.Flag("ipAnonimization", "Agents will anonymize IP addresses according to the option selected.").Action(c.ipAnonimization.Set).StringVar(&c.ipAnonimization.Value) |
| 56 | + c.RegisterFlagBool(c.JSONFlag()) |
| 57 | + |
| 58 | + return &c |
| 59 | +} |
| 60 | + |
| 61 | +// Exec invokes the application logic for the command. |
| 62 | +func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { |
| 63 | + var err error |
| 64 | + input := &workspaces.CreateInput{ |
| 65 | + Description: &c.description, |
| 66 | + Mode: &c.blockingMode, |
| 67 | + Name: &c.name, |
| 68 | + } |
| 69 | + if c.attackThresholds.WasSet { |
| 70 | + input.AttackSignalThresholds, err = parseAttackSignalThresholds(c.attackThresholds.Value) |
| 71 | + if err != nil { |
| 72 | + return err |
| 73 | + } |
| 74 | + } |
| 75 | + if c.clientIPHeaders.WasSet { |
| 76 | + input.ClientIPHeaders = strings.Split(c.clientIPHeaders.Value, ",") |
| 77 | + } |
| 78 | + if c.defaultBlockingCode.WasSet { |
| 79 | + input.DefaultBlockingResponseCode = &c.defaultBlockingCode.Value |
| 80 | + } |
| 81 | + if c.defaultRedirectURL.WasSet { |
| 82 | + input.DefaultRedirectURL = &c.defaultRedirectURL.Value |
| 83 | + } |
| 84 | + if c.ipAnonimization.WasSet { |
| 85 | + input.IPAnonymization = &c.ipAnonimization.Value |
| 86 | + } |
| 87 | + |
| 88 | + fc, ok := c.Globals.APIClient.(*fastly.Client) |
| 89 | + if !ok { |
| 90 | + return errors.New("failed to convert interface to a fastly client") |
| 91 | + } |
| 92 | + |
| 93 | + data, err := workspaces.Create(context.TODO(), fc, input) |
| 94 | + if err != nil { |
| 95 | + return err |
| 96 | + } |
| 97 | + |
| 98 | + if ok, err := c.WriteJSON(out, data); ok { |
| 99 | + return err |
| 100 | + } |
| 101 | + |
| 102 | + text.Success(out, "Created workspace '%s' (workspace-id: %s)", data.Name, data.WorkspaceID) |
| 103 | + return nil |
| 104 | +} |
| 105 | + |
| 106 | +func parseAttackSignalThresholds(thresholds string) (*workspaces.AttackSignalThresholdsCreateInput, error) { |
| 107 | + thresholdsArray := strings.Split(thresholds, ":") |
| 108 | + immediate, err := strconv.ParseBool(thresholdsArray[0]) |
| 109 | + if err != nil { |
| 110 | + return nil, err |
| 111 | + } |
| 112 | + oneMinute, err := strconv.Atoi(thresholdsArray[1]) |
| 113 | + if err != nil { |
| 114 | + return nil, err |
| 115 | + } |
| 116 | + tenMinutes, err := strconv.Atoi(thresholdsArray[2]) |
| 117 | + if err != nil { |
| 118 | + return nil, err |
| 119 | + } |
| 120 | + oneHour, err := strconv.Atoi(thresholdsArray[3]) |
| 121 | + if err != nil { |
| 122 | + return nil, err |
| 123 | + } |
| 124 | + |
| 125 | + return &workspaces.AttackSignalThresholdsCreateInput{ |
| 126 | + OneMinute: &oneMinute, |
| 127 | + TenMinutes: &tenMinutes, |
| 128 | + OneHour: &oneHour, |
| 129 | + Immediate: &immediate, |
| 130 | + }, nil |
| 131 | +} |
0 commit comments