|
package main |
|
|
|
import ( |
|
"bytes" |
|
"errors" |
|
"fmt" |
|
"io" |
|
"log" |
|
"net/http" |
|
"strings" |
|
|
|
"github.com/spf13/cobra" |
|
) |
|
|
|
func ServeCommand() *cobra.Command { |
|
cmd := cobra.Command{ |
|
Use: "serve", |
|
RunE: func(_ *cobra.Command, args []string) error { |
|
log.Println("listening on port 8080...") |
|
return http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
// Positional arguments are implicit in the path. |
|
// Flags (both single our double dashed) are sent via query params. Dash(es) needed. |
|
// Example: myservice:8080/ban/30.40.50.120?-v=true&--dry-run=true |
|
args := strings.Split(strings.Trim(r.URL.Path, "/"), "/") |
|
// append query params as simulating flags |
|
for k, vs := range r.URL.Query() { |
|
for _, v := range vs { |
|
if !strings.HasPrefix(k, "-") { |
|
w.WriteHeader(http.StatusBadRequest) |
|
w.Write([]byte("Please add -- or - to the flags")) |
|
return |
|
} |
|
args = append(args, fmt.Sprintf("%s=%s", k, v)) |
|
|
|
} |
|
} |
|
buf := new(bytes.Buffer) |
|
rootCmd := RootCmd() |
|
rootCmd.DisableSuggestions = true |
|
rootCmd.SetFlagErrorFunc(func(command *cobra.Command, err error) error { |
|
w.WriteHeader(http.StatusBadRequest) |
|
log.Printf("malformed query params. Err: %s", err) |
|
w.Write([]byte(err.Error())) |
|
|
|
return err |
|
}) |
|
|
|
// TODO There are more improvements we can do around returning 400. |
|
|
|
rootCmd.SetOut(buf) // capture the output in order to properly set the headers before writing the response. Can be improved i guess. |
|
rootCmd.SetArgs(args) |
|
c, err := rootCmd.ExecuteC() |
|
if err != nil { |
|
if c == nil || c.Name() == rootCmd.Name() { // command not found |
|
w.WriteHeader(http.StatusBadRequest) |
|
log.Printf("command not found. Err: %s", err) |
|
} else { |
|
w.WriteHeader(http.StatusInternalServerError) |
|
log.Printf("error executing command. Command: %s, Err: %s", c.UseLine(), err) |
|
} |
|
|
|
w.Write([]byte(err.Error())) |
|
return |
|
} |
|
|
|
log.Println("Executed command:", c.UseLine()) |
|
|
|
io.Copy(w, buf) |
|
})) |
|
}, |
|
} |
|
|
|
return &cmd |
|
} |
|
|
|
func BanIPCommand() *cobra.Command { |
|
var verbose, dryRun bool |
|
cmd := cobra.Command{ |
|
Use: "ban", |
|
Args: cobra.ExactArgs(1), |
|
RunE: func(c *cobra.Command, args []string) error { |
|
if args[0] != "34.55.123.16" { // Just to force an err the example |
|
return errors.New("this is just a demo. Only 34.55.123.16 ip is allowed") |
|
} |
|
if verbose { |
|
c.Printf("IP %s got banned\n", args[0]) |
|
} |
|
|
|
// obviously this is just a simulation... |
|
if dryRun { |
|
c.Println("No operation performed as dry-run") |
|
} |
|
|
|
return nil |
|
}, |
|
} |
|
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output") |
|
cmd.PersistentFlags().BoolVarP(&dryRun, "dry-run", "d", false, "dry-run meaning no operation will be performed") |
|
|
|
return &cmd |
|
} |
|
|
|
func UnbanIPCommand() *cobra.Command { |
|
var verbose, dryRun bool |
|
cmd := cobra.Command{ |
|
Use: "unban", |
|
Args: cobra.ExactArgs(1), |
|
RunE: func(c *cobra.Command, args []string) error { |
|
if args[0] != "34.55.123.16" { // Just to force an err the example |
|
return errors.New("this is just a demo. Only 34.55.123.16 ip is allowed") |
|
} |
|
if verbose { |
|
c.Printf("IP %s got unbanned\n", args[0]) |
|
} |
|
|
|
// obviously this is just a simulation... |
|
if dryRun { |
|
c.Println("No operation performed as dry-run") |
|
} |
|
|
|
return nil |
|
}, |
|
} |
|
|
|
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output") |
|
cmd.PersistentFlags().BoolVarP(&dryRun, "dry-run", "d", false, "dry-run meaning no operation will be performed") |
|
|
|
return &cmd |
|
} |
|
|
|
func RootCmd() *cobra.Command { |
|
rootCmd := cobra.Command{ |
|
Use: "dd8", |
|
} |
|
rootCmd.AddCommand(BanIPCommand(), UnbanIPCommand(), ServeCommand()) |
|
|
|
return &rootCmd |
|
} |
|
|
|
func main() { |
|
rootCmd := RootCmd() |
|
if err := rootCmd.Execute(); err != nil { |
|
log.Fatalf("Failed to start: %v", err) |
|
} |
|
} |