Skip to content

Commit

Permalink
Backport of Add support for maxConnections, maxConcurrentRequests, an…
Browse files Browse the repository at this point in the history
…d maxPendingRequests to IngressGateway CRD into release/0.49.x (#1700)

* backport of commit 7592592

* backport of commit 876eb7c

* backport of commit 438aae1

Co-authored-by: Semir Patel <semir.patel@hashicorp.com>
  • Loading branch information
1 parent b50036b commit 53b64e1
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 15 deletions.
22 changes: 22 additions & 0 deletions charts/consul/templates/crd-ingressgateways.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ spec:
spec:
description: IngressGatewaySpec defines the desired state of IngressGateway.
properties:
defaults:
description: Defaults is default configuration for all upstream services
properties:
maxConcurrentRequests:
format: int32
type: integer
maxConnections:
format: int32
type: integer
maxPendingRequests:
format: int32
type: integer
type: object
listeners:
description: Listeners declares what ports the ingress gateway should
listen on, and what services to associated to those ports.
Expand Down Expand Up @@ -98,6 +111,15 @@ spec:
items:
type: string
type: array
maxConcurrentRequests:
format: int32
type: integer
maxConnections:
format: int32
type: integer
maxPendingRequests:
format: int32
type: integer
name:
description: "Name declares the service to which traffic
should be forwarded. \n This can either be a specific
Expand Down
76 changes: 69 additions & 7 deletions control-plane/api/v1alpha1/ingressgateway_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ type IngressGatewaySpec struct {
// Listeners declares what ports the ingress gateway should listen on, and
// what services to associated to those ports.
Listeners []IngressListener `json:"listeners,omitempty"`

// Defaults is default configuration for all upstream services
Defaults *IngressServiceConfig `json:"defaults,omitempty"`
}

type IngressServiceConfig struct {
MaxConnections *uint32 `json:"maxConnections,omitempty"`
MaxPendingRequests *uint32 `json:"maxPendingRequests,omitempty"`
MaxConcurrentRequests *uint32 `json:"maxConcurrentRequests,omitempty"`
}

type GatewayTLSConfig struct {
Expand Down Expand Up @@ -144,6 +153,10 @@ type IngressService struct {
// Allow HTTP header manipulation to be configured.
RequestHeaders *HTTPHeaderModifiers `json:"requestHeaders,omitempty"`
ResponseHeaders *HTTPHeaderModifiers `json:"responseHeaders,omitempty"`

MaxConnections *uint32 `json:"maxConnections,omitempty"`
MaxPendingRequests *uint32 `json:"maxPendingRequests,omitempty"`
MaxConcurrentRequests *uint32 `json:"maxConcurrentRequests,omitempty"`
}

func (in *IngressGateway) GetObjectMeta() metav1.ObjectMeta {
Expand Down Expand Up @@ -235,6 +248,7 @@ func (in *IngressGateway) ToConsul(datacenter string) capi.ConfigEntry {
TLS: *in.Spec.TLS.toConsul(),
Listeners: listeners,
Meta: meta(datacenter),
Defaults: in.Spec.Defaults.toConsul(),
}
}

Expand All @@ -257,6 +271,8 @@ func (in *IngressGateway) Validate(consulMeta common.ConsulMeta) error {
errs = append(errs, v.validate(path.Child("listeners").Index(i), consulMeta)...)
}

errs = append(errs, in.Spec.Defaults.validate(path.Child("defaults"))...)

if len(errs) > 0 {
return apierrors.NewInvalid(
schema.GroupKind{Group: ConsulHashicorpGroup, Kind: ingressGatewayKubeKind},
Expand Down Expand Up @@ -329,13 +345,16 @@ func (in IngressListener) toConsul() capi.IngressListener {

func (in IngressService) toConsul() capi.IngressService {
return capi.IngressService{
Name: in.Name,
Hosts: in.Hosts,
Namespace: in.Namespace,
Partition: in.Partition,
TLS: in.TLS.toConsul(),
RequestHeaders: in.RequestHeaders.toConsul(),
ResponseHeaders: in.ResponseHeaders.toConsul(),
Name: in.Name,
Hosts: in.Hosts,
Namespace: in.Namespace,
Partition: in.Partition,
TLS: in.TLS.toConsul(),
RequestHeaders: in.RequestHeaders.toConsul(),
ResponseHeaders: in.ResponseHeaders.toConsul(),
MaxConnections: in.MaxConnections,
MaxPendingRequests: in.MaxPendingRequests,
MaxConcurrentRequests: in.MaxConcurrentRequests,
}
}

Expand Down Expand Up @@ -406,6 +425,49 @@ func (in IngressListener) validate(path *field.Path, consulMeta common.ConsulMet
string(asJSON),
"hosts must be empty if protocol is \"tcp\""))
}

if svc.MaxConnections != nil && *svc.MaxConnections <= 0 {
errs = append(errs, field.Invalid(path.Child("maxconnections"), *svc.MaxConnections, "MaxConnections must be > 0"))
}

if svc.MaxConcurrentRequests != nil && *svc.MaxConcurrentRequests <= 0 {
errs = append(errs, field.Invalid(path.Child("maxconcurrentrequests"), *svc.MaxConcurrentRequests, "MaxConcurrentRequests must be > 0"))
}

if svc.MaxPendingRequests != nil && *svc.MaxPendingRequests <= 0 {
errs = append(errs, field.Invalid(path.Child("maxpendingrequests"), *svc.MaxPendingRequests, "MaxPendingRequests must be > 0"))
}
}
return errs
}

func (in *IngressServiceConfig) validate(path *field.Path) field.ErrorList {
if in == nil {
return nil
}
var errs field.ErrorList

if in.MaxConnections != nil && *in.MaxConnections <= 0 {
errs = append(errs, field.Invalid(path.Child("maxconnections"), *in.MaxConnections, "MaxConnections must be > 0"))
}

if in.MaxConcurrentRequests != nil && *in.MaxConcurrentRequests <= 0 {
errs = append(errs, field.Invalid(path.Child("maxconcurrentrequests"), *in.MaxConcurrentRequests, "MaxConcurrentRequests must be > 0"))
}

if in.MaxPendingRequests != nil && *in.MaxPendingRequests <= 0 {
errs = append(errs, field.Invalid(path.Child("maxpendingrequests"), *in.MaxPendingRequests, "MaxPendingRequests must be > 0"))
}
return errs
}

func (in *IngressServiceConfig) toConsul() *capi.IngressServiceConfig {
if in == nil {
return nil
}
return &capi.IngressServiceConfig{
MaxConnections: in.MaxConnections,
MaxPendingRequests: in.MaxPendingRequests,
MaxConcurrentRequests: in.MaxConcurrentRequests,
}
}
158 changes: 150 additions & 8 deletions control-plane/api/v1alpha1/ingressgateway_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,15 @@ func TestIngressGateway_MatchesConsul(t *testing.T) {
}

func TestIngressGateway_ToConsul(t *testing.T) {

defaultMaxConnections := uint32(100)
defaultMaxPendingRequests := uint32(101)
defaultMaxConcurrentRequests := uint32(102)

maxConnections := uint32(200)
maxPendingRequests := uint32(201)
maxConcurrentRequests := uint32(202)

cases := map[string]struct {
Ours IngressGateway
Exp *capi.IngressGatewayConfigEntry
Expand Down Expand Up @@ -289,6 +298,11 @@ func TestIngressGateway_ToConsul(t *testing.T) {
TLSMaxVersion: "TLSv1_1",
CipherSuites: []string{"ECDHE-ECDSA-AES128-GCM-SHA256", "AES128-SHA"},
},
Defaults: &IngressServiceConfig{
MaxConnections: &defaultMaxConnections,
MaxPendingRequests: &defaultMaxPendingRequests,
MaxConcurrentRequests: &defaultMaxConcurrentRequests,
},
Listeners: []IngressListener{
{
Port: 8888,
Expand All @@ -305,10 +319,13 @@ func TestIngressGateway_ToConsul(t *testing.T) {
},
Services: []IngressService{
{
Name: "name1",
Hosts: []string{"host1_1", "host1_2"},
Namespace: "ns1",
Partition: "default",
Name: "name1",
Hosts: []string{"host1_1", "host1_2"},
Namespace: "ns1",
Partition: "default",
MaxConnections: &maxConnections,
MaxPendingRequests: &maxPendingRequests,
MaxConcurrentRequests: &maxConcurrentRequests,
TLS: &GatewayServiceTLSConfig{
SDS: &GatewayTLSSDSConfig{
ClusterName: "cluster1",
Expand Down Expand Up @@ -378,6 +395,11 @@ func TestIngressGateway_ToConsul(t *testing.T) {
TLSMaxVersion: "TLSv1_1",
CipherSuites: []string{"ECDHE-ECDSA-AES128-GCM-SHA256", "AES128-SHA"},
},
Defaults: &capi.IngressServiceConfig{
MaxConnections: &defaultMaxConnections,
MaxPendingRequests: &defaultMaxPendingRequests,
MaxConcurrentRequests: &defaultMaxConcurrentRequests,
},
Listeners: []capi.IngressListener{
{
Port: 8888,
Expand All @@ -394,10 +416,13 @@ func TestIngressGateway_ToConsul(t *testing.T) {
},
Services: []capi.IngressService{
{
Name: "name1",
Hosts: []string{"host1_1", "host1_2"},
Namespace: "ns1",
Partition: "default",
Name: "name1",
Hosts: []string{"host1_1", "host1_2"},
Namespace: "ns1",
Partition: "default",
MaxConnections: &maxConnections,
MaxPendingRequests: &maxPendingRequests,
MaxConcurrentRequests: &maxConcurrentRequests,
TLS: &capi.GatewayServiceTLSConfig{
SDS: &capi.GatewayTLSSDSConfig{
ClusterName: "cluster1",
Expand Down Expand Up @@ -471,6 +496,8 @@ func TestIngressGateway_ToConsul(t *testing.T) {
}

func TestIngressGateway_Validate(t *testing.T) {
zero := uint32(0)

cases := map[string]struct {
input *IngressGateway
namespacesEnabled bool
Expand Down Expand Up @@ -785,6 +812,121 @@ func TestIngressGateway_Validate(t *testing.T) {
},
partitionEnabled: true,
},
"defaults.maxConnections invalid": {
input: &IngressGateway{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: IngressGatewaySpec{
Defaults: &IngressServiceConfig{
MaxConnections: &zero,
},
},
},
expectedErrMsgs: []string{
`spec.defaults.maxconnections: Invalid`,
},
},
"defaults.maxPendingRequests invalid": {
input: &IngressGateway{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: IngressGatewaySpec{
Defaults: &IngressServiceConfig{
MaxPendingRequests: &zero,
},
},
},
expectedErrMsgs: []string{
`spec.defaults.maxpendingrequests: Invalid`,
},
},
"defaults.maxConcurrentRequests invalid": {
input: &IngressGateway{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: IngressGatewaySpec{
Defaults: &IngressServiceConfig{
MaxConcurrentRequests: &zero,
},
},
},
expectedErrMsgs: []string{
`spec.defaults.maxconcurrentrequests: Invalid`,
},
},
"service.maxConnections invalid": {
input: &IngressGateway{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: IngressGatewaySpec{
Listeners: []IngressListener{
{
Protocol: "http",
Services: []IngressService{
{
Name: "svc1",
MaxConnections: &zero,
},
},
},
},
},
},
expectedErrMsgs: []string{
`spec.listeners[0].maxconnections: Invalid`,
},
},
"service.maxConcurrentRequests invalid": {
input: &IngressGateway{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: IngressGatewaySpec{
Listeners: []IngressListener{
{
Protocol: "http",
Services: []IngressService{
{
Name: "svc1",
MaxConcurrentRequests: &zero,
},
},
},
},
},
},
expectedErrMsgs: []string{
`spec.listeners[0].maxconcurrentrequests: Invalid`,
},
},
"service.maxPendingRequests invalid": {
input: &IngressGateway{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: IngressGatewaySpec{
Listeners: []IngressListener{
{
Protocol: "http",
Services: []IngressService{
{
Name: "svc1",
MaxPendingRequests: &zero,
},
},
},
},
},
},
expectedErrMsgs: []string{
`spec.listeners[0].maxpendingrequests: Invalid`,
},
},

"multiple errors": {
input: &IngressGateway{
ObjectMeta: metav1.ObjectMeta{
Expand Down
Loading

0 comments on commit 53b64e1

Please sign in to comment.