Skip to content

Commit 773fea0

Browse files
salonichf5sjberman
andauthored
Add support for multiple gateways (#3275)
Problem: Users want to be able to configure multiple Gateways with a single installation of NGF. Solution: Support the ability to create multiple Gateways. Routes and policies can be attached to multiple Gateways. Also fixed conformance tests. --------- Co-authored-by: Saylor Berman <[email protected]>
1 parent e6cae12 commit 773fea0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+7132
-4013
lines changed

apis/v1alpha2/nginxproxy_types.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ const (
550550

551551
// ExternalTrafficPolicy describes how nodes distribute service traffic they
552552
// receive on one of the Service's "externally-facing" addresses (NodePorts, ExternalIPs,
553-
// and LoadBalancer IPs.
553+
// and LoadBalancer IPs. Ignored for ClusterIP services.
554554
// +kubebuilder:validation:Enum=Cluster;Local
555555
type ExternalTrafficPolicy corev1.ServiceExternalTrafficPolicy
556556

cmd/gateway/commands.go

+7-36
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"github.com/spf13/cobra"
1313
"github.com/spf13/pflag"
1414
"go.uber.org/zap"
15-
"k8s.io/apimachinery/pkg/types"
1615
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
1716
"k8s.io/klog/v2"
1817
ctlr "sigs.k8s.io/controller-runtime"
@@ -59,11 +58,9 @@ func createRootCommand() *cobra.Command {
5958
func createControllerCommand() *cobra.Command {
6059
// flag names
6160
const (
62-
gatewayFlag = "gateway"
6361
configFlag = "config"
6462
serviceFlag = "service"
6563
agentTLSSecretFlag = "agent-tls-secret"
66-
updateGCStatusFlag = "update-gatewayclass-status"
6764
metricsDisableFlag = "metrics-disable"
6865
metricsSecureFlag = "metrics-secure-serving"
6966
metricsPortFlag = "metrics-port"
@@ -94,9 +91,7 @@ func createControllerCommand() *cobra.Command {
9491
validator: validateResourceName,
9592
}
9693

97-
updateGCStatus bool
98-
gateway = namespacedNameValue{}
99-
configName = stringValidatingValue{
94+
configName = stringValidatingValue{
10095
validator: validateResourceName,
10196
}
10297
serviceName = stringValidatingValue{
@@ -200,11 +195,6 @@ func createControllerCommand() *cobra.Command {
200195
return fmt.Errorf("error parsing telemetry endpoint insecure: %w", err)
201196
}
202197

203-
var gwNsName *types.NamespacedName
204-
if cmd.Flags().Changed(gatewayFlag) {
205-
gwNsName = &gateway.value
206-
}
207-
208198
var usageReportConfig config.UsageReportConfig
209199
if plus && usageReportSecretName.value == "" {
210200
return errors.New("usage-report-secret is required when using NGINX Plus")
@@ -229,14 +219,12 @@ func createControllerCommand() *cobra.Command {
229219
}
230220

231221
conf := config.Config{
232-
GatewayCtlrName: gatewayCtlrName.value,
233-
ConfigName: configName.String(),
234-
Logger: logger,
235-
AtomicLevel: atom,
236-
GatewayClassName: gatewayClassName.value,
237-
GatewayNsName: gwNsName,
238-
UpdateGatewayClassStatus: updateGCStatus,
239-
GatewayPodConfig: podConfig,
222+
GatewayCtlrName: gatewayCtlrName.value,
223+
ConfigName: configName.String(),
224+
Logger: logger,
225+
AtomicLevel: atom,
226+
GatewayClassName: gatewayClassName.value,
227+
GatewayPodConfig: podConfig,
240228
HealthConfig: config.HealthConfig{
241229
Enabled: !disableHealth,
242230
Port: healthListenPort.value,
@@ -293,16 +281,6 @@ func createControllerCommand() *cobra.Command {
293281
)
294282
utilruntime.Must(cmd.MarkFlagRequired(gatewayClassFlag))
295283

296-
cmd.Flags().Var(
297-
&gateway,
298-
gatewayFlag,
299-
"The namespaced name of the Gateway resource to use. "+
300-
"Must be of the form: NAMESPACE/NAME. "+
301-
"If not specified, the control plane will process all Gateways for the configured GatewayClass. "+
302-
"However, among them, it will choose the oldest resource by creation timestamp. If the timestamps are "+
303-
"equal, it will choose the resource that appears first in alphabetical order by {namespace}/{name}.",
304-
)
305-
306284
cmd.Flags().VarP(
307285
&configName,
308286
configFlag,
@@ -326,13 +304,6 @@ func createControllerCommand() *cobra.Command {
326304
`NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway).`,
327305
)
328306

329-
cmd.Flags().BoolVar(
330-
&updateGCStatus,
331-
updateGCStatusFlag,
332-
true,
333-
"Update the status of the GatewayClass resource.",
334-
)
335-
336307
cmd.Flags().BoolVar(
337308
&disableMetrics,
338309
metricsDisableFlag,

cmd/gateway/commands_test.go

-66
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
. "github.com/onsi/gomega"
1010
"github.com/spf13/cobra"
1111
"github.com/spf13/pflag"
12-
"k8s.io/apimachinery/pkg/types"
1312

1413
"github.com/nginx/nginx-gateway-fabric/internal/mode/static/config"
1514
)
@@ -137,11 +136,9 @@ func TestControllerCmdFlagValidation(t *testing.T) {
137136
args: []string{
138137
"--gateway-ctlr-name=gateway.nginx.org/nginx-gateway", // common and required flag
139138
"--gatewayclass=nginx", // common and required flag
140-
"--gateway=nginx-gateway/nginx",
141139
"--config=nginx-gateway-config",
142140
"--service=nginx-gateway",
143141
"--agent-tls-secret=agent-tls",
144-
"--update-gatewayclass-status=true",
145142
"--metrics-port=9114",
146143
"--metrics-disable",
147144
"--metrics-secure-serving",
@@ -170,23 +167,6 @@ func TestControllerCmdFlagValidation(t *testing.T) {
170167
},
171168
wantErr: false,
172169
},
173-
{
174-
name: "gateway is set to empty string",
175-
args: []string{
176-
"--gateway=",
177-
},
178-
wantErr: true,
179-
expectedErrPrefix: `invalid argument "" for "--gateway" flag: must be set`,
180-
},
181-
{
182-
name: "gateway is invalid",
183-
args: []string{
184-
"--gateway=nginx-gateway", // no namespace
185-
},
186-
wantErr: true,
187-
expectedErrPrefix: `invalid argument "nginx-gateway" for "--gateway" flag: invalid format; ` +
188-
"must be NAMESPACE/NAME",
189-
},
190170
{
191171
name: "config is set to empty string",
192172
args: []string{
@@ -235,22 +215,6 @@ func TestControllerCmdFlagValidation(t *testing.T) {
235215
wantErr: true,
236216
expectedErrPrefix: `invalid argument "!@#$" for "--agent-tls-secret" flag: invalid format`,
237217
},
238-
{
239-
name: "update-gatewayclass-status is set to empty string",
240-
args: []string{
241-
"--update-gatewayclass-status=",
242-
},
243-
wantErr: true,
244-
expectedErrPrefix: `invalid argument "" for "--update-gatewayclass-status" flag: strconv.ParseBool`,
245-
},
246-
{
247-
name: "update-gatewayclass-status is invalid",
248-
args: []string{
249-
"--update-gatewayclass-status=invalid", // not a boolean
250-
},
251-
wantErr: true,
252-
expectedErrPrefix: `invalid argument "invalid" for "--update-gatewayclass-status" flag: strconv.ParseBool`,
253-
},
254218
{
255219
name: "metrics-port is invalid type",
256220
args: []string{
@@ -727,30 +691,6 @@ func TestParseFlags(t *testing.T) {
727691
err = flagSet.Set("customStringFlagUserDefined", "changed-test-flag-value")
728692
g.Expect(err).To(Not(HaveOccurred()))
729693

730-
customStringFlagNoDefaultValueUnset := namespacedNameValue{
731-
value: types.NamespacedName{},
732-
}
733-
flagSet.Var(
734-
&customStringFlagNoDefaultValueUnset,
735-
"customStringFlagNoDefaultValueUnset",
736-
"no default value custom string test flag",
737-
)
738-
739-
customStringFlagNoDefaultValueUserDefined := namespacedNameValue{
740-
value: types.NamespacedName{},
741-
}
742-
flagSet.Var(
743-
&customStringFlagNoDefaultValueUserDefined,
744-
"customStringFlagNoDefaultValueUserDefined",
745-
"no default value but with user defined namespacedName test flag",
746-
)
747-
userDefinedNamespacedName := types.NamespacedName{
748-
Namespace: "changed-namespace",
749-
Name: "changed-name",
750-
}
751-
err = flagSet.Set("customStringFlagNoDefaultValueUserDefined", userDefinedNamespacedName.String())
752-
g.Expect(err).To(Not(HaveOccurred()))
753-
754694
expectedKeys := []string{
755695
"boolFlagTrue",
756696
"boolFlagFalse",
@@ -760,9 +700,6 @@ func TestParseFlags(t *testing.T) {
760700

761701
"customStringFlagDefault",
762702
"customStringFlagUserDefined",
763-
764-
"customStringFlagNoDefaultValueUnset",
765-
"customStringFlagNoDefaultValueUserDefined",
766703
}
767704
expectedValues := []string{
768705
"true",
@@ -773,9 +710,6 @@ func TestParseFlags(t *testing.T) {
773710

774711
"default",
775712
"user-defined",
776-
777-
"default",
778-
"user-defined",
779713
}
780714

781715
flagKeys, flagValues := parseFlags(flagSet)

cmd/gateway/validating_types.go

-30
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import (
66
"fmt"
77
"strconv"
88
"strings"
9-
10-
"k8s.io/apimachinery/pkg/types"
119
)
1210

1311
// stringValidatingValue is a string flag value with custom validation logic.
@@ -106,31 +104,3 @@ func (v *intValidatingValue) Set(param string) error {
106104
func (v *intValidatingValue) Type() string {
107105
return "int"
108106
}
109-
110-
// namespacedNameValue is a string flag value that represents a namespaced name.
111-
// it implements the pflag.Value interface.
112-
type namespacedNameValue struct {
113-
value types.NamespacedName
114-
}
115-
116-
func (v *namespacedNameValue) String() string {
117-
if (v.value == types.NamespacedName{}) {
118-
// if we don't do that, the default value in the help message will be printed as "/"
119-
return ""
120-
}
121-
return v.value.String()
122-
}
123-
124-
func (v *namespacedNameValue) Set(param string) error {
125-
nsname, err := parseNamespacedResourceName(param)
126-
if err != nil {
127-
return err
128-
}
129-
130-
v.value = nsname
131-
return nil
132-
}
133-
134-
func (v *namespacedNameValue) Type() string {
135-
return "string"
136-
}

cmd/gateway/validation.go

-35
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"strconv"
99
"strings"
1010

11-
"k8s.io/apimachinery/pkg/types"
1211
"k8s.io/apimachinery/pkg/util/validation"
1312
)
1413

@@ -55,40 +54,6 @@ func validateResourceName(value string) error {
5554
return nil
5655
}
5756

58-
func validateNamespaceName(value string) error {
59-
// used by Kubernetes to validate resource namespace names
60-
messages := validation.IsDNS1123Label(value)
61-
if len(messages) > 0 {
62-
msg := strings.Join(messages, "; ")
63-
return fmt.Errorf("invalid format: %s", msg)
64-
}
65-
66-
return nil
67-
}
68-
69-
func parseNamespacedResourceName(value string) (types.NamespacedName, error) {
70-
if value == "" {
71-
return types.NamespacedName{}, errors.New("must be set")
72-
}
73-
74-
parts := strings.Split(value, "/")
75-
if len(parts) != 2 {
76-
return types.NamespacedName{}, errors.New("invalid format; must be NAMESPACE/NAME")
77-
}
78-
79-
if err := validateNamespaceName(parts[0]); err != nil {
80-
return types.NamespacedName{}, fmt.Errorf("invalid namespace name: %w", err)
81-
}
82-
if err := validateResourceName(parts[1]); err != nil {
83-
return types.NamespacedName{}, fmt.Errorf("invalid resource name: %w", err)
84-
}
85-
86-
return types.NamespacedName{
87-
Namespace: parts[0],
88-
Name: parts[1],
89-
}, nil
90-
}
91-
9257
func validateQualifiedName(name string) error {
9358
if len(name) == 0 {
9459
return errors.New("must be set")

0 commit comments

Comments
 (0)