Skip to content

Commit 33daf08

Browse files
committed
Adding CLI validation (nginx#8)
* Adding CLI validation Validate input parameter. Users can input entire DOMAIN/NS/NAME path, or leave some elements off. Gateway will require all elements. Created Validator pattern. Each argument requiring Validation should provide a Validator. The callback will be provided a FlagSet for referencing other arguments and the particular flag value for validation.
1 parent 6051cfd commit 33daf08

File tree

6 files changed

+366
-6
lines changed

6 files changed

+366
-6
lines changed

cmd/gateway/gateway_suite_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package main_test
2+
3+
import (
4+
. "github.com/onsi/ginkgo"
5+
. "github.com/onsi/gomega"
6+
7+
"testing"
8+
)
9+
10+
func TestGateway(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "Gateway Suite")
13+
}

cmd/gateway/main.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"fmt"
45
"os"
56

67
"github.com/nginxinc/nginx-gateway-kubernetes/internal/config"
@@ -10,30 +11,38 @@ import (
1011
"sigs.k8s.io/controller-runtime/pkg/log/zap"
1112
)
1213

14+
const (
15+
domain string = "gateway.nginx.org"
16+
)
17+
1318
var (
1419
// Set during go build
1520
version string
1621
commit string
1722
date string
1823

1924
// Command-line flags
20-
gatewayCtlrName = flag.String("gateway-ctlr-name", "", "The name of the Gateway controller")
25+
gatewayCtlrName = flag.String(
26+
"gateway-ctlr-name",
27+
"",
28+
fmt.Sprintf("The name of the Gateway controller. The controller name must be of the form: DOMAIN/NAMESPACE/NAME. The controller's domain is '%s'.", domain),
29+
)
2130
)
2231

2332
func main() {
2433
flag.Parse()
2534

26-
if *gatewayCtlrName == "" {
27-
flag.PrintDefaults()
28-
os.Exit(1)
29-
}
30-
3135
logger := zap.New()
3236
conf := config.Config{
3337
GatewayCtlrName: *gatewayCtlrName,
3438
Logger: logger,
3539
}
3640

41+
MustValidateArguments(
42+
flag.CommandLine,
43+
GatewayControllerParam(domain, "nginx-gateway" /* TODO dynamically set */),
44+
)
45+
3746
logger.Info("Starting NGINX Gateway",
3847
"version", version,
3948
"commit", commit,

cmd/gateway/setup.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"strings"
8+
9+
flag "github.com/spf13/pflag"
10+
)
11+
12+
const (
13+
errTmpl = "failed validation - flag: '--%s' reason: '%s'\n"
14+
)
15+
16+
type Validator func(*flag.FlagSet) error
17+
type ValidatorContext struct {
18+
Key string
19+
V Validator
20+
}
21+
22+
func GatewayControllerParam(domain string, namespace string) ValidatorContext {
23+
name := "gateway-ctlr-name"
24+
return ValidatorContext{
25+
name,
26+
func(flagset *flag.FlagSet) error {
27+
// FIXME(yacobucci) this does not provide the same regex validation as
28+
// GatewayClass.ControllerName. provide equal and then specific validation
29+
param, err := flagset.GetString(name)
30+
if err != nil {
31+
return err
32+
}
33+
34+
if len(param) == 0 {
35+
return errors.New("flag must be set")
36+
}
37+
38+
fields := strings.Split(param, "/")
39+
l := len(fields)
40+
if l != 3 {
41+
return errors.New("unsupported path length, must be form DOMAIN/NAMESPACE/NAME")
42+
}
43+
44+
for i := len(fields); i > 0; i-- {
45+
switch i {
46+
case 3:
47+
if fields[0] != domain {
48+
return fmt.Errorf("invalid domain: %s", fields[0])
49+
}
50+
fields = fields[1:]
51+
case 2:
52+
if fields[0] != namespace {
53+
return fmt.Errorf("cross namespace unsupported: %s", fields[0])
54+
}
55+
fields = fields[1:]
56+
case 1:
57+
if fields[0] == "" {
58+
return errors.New("must provide a name")
59+
}
60+
}
61+
}
62+
63+
return nil
64+
},
65+
}
66+
}
67+
68+
func ValidateArguments(flagset *flag.FlagSet, validators ...ValidatorContext) []string {
69+
var msgs []string
70+
for _, v := range validators {
71+
if flagset.Lookup(v.Key) != nil {
72+
err := v.V(flagset)
73+
if err != nil {
74+
msgs = append(msgs, fmt.Sprintf(errTmpl, v.Key, err.Error()))
75+
}
76+
}
77+
}
78+
79+
return msgs
80+
}
81+
82+
func MustValidateArguments(flagset *flag.FlagSet, validators ...ValidatorContext) {
83+
msgs := ValidateArguments(flagset, validators...)
84+
if msgs != nil {
85+
for i := range msgs {
86+
fmt.Fprintf(os.Stderr, "%s", msgs[i])
87+
}
88+
fmt.Fprintln(os.Stderr, "")
89+
90+
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
91+
flag.PrintDefaults()
92+
93+
os.Exit(1)
94+
}
95+
}

0 commit comments

Comments
 (0)