Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NET-1374] #2166

Merged
merged 34 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c614f03
bootstrap control plane request limits
loshz May 22, 2023
d69e4d6
ctrl-generate
DanStough May 22, 2023
4df9ae7
implement ConfigEntryResource
loshz May 23, 2023
e5cda85
add changelog
loshz May 23, 2023
7e9895b
add control-plane tests
loshz May 23, 2023
80a6651
controllers: add test cases
loshz May 23, 2023
9e04086
api: add webhook
loshz May 23, 2023
dac0b31
Update command.go
loshz May 23, 2023
68a9b28
generate yaml
loshz May 23, 2023
f734677
update helm chart
loshz May 23, 2023
68df878
Update .changelog/2166.txt
loshz May 24, 2023
bde72dd
rm unused generated files
loshz May 26, 2023
96a442e
add copywrite headers
loshz May 26, 2023
9e296af
add handle and validate logic
loshz May 26, 2023
f32584d
fix lint issues
loshz May 26, 2023
ad4c5b9
fix lint issues
loshz May 26, 2023
367fc16
go mod tidy
jmurret May 30, 2023
7b78bf3
update controller to use subresources
jmurret May 30, 2023
a18cb05
fixing unit test so it matches on correct config entry
jmurret May 30, 2023
cf3fc91
fixing copy pasta
jmurret May 31, 2023
8202802
fixing broken unit tests. adding piece to acceptance tests.
jmurret May 31, 2023
a16b36b
updating tests
jmurret May 31, 2023
8525a31
marking crd as requiring connectInject.enabled
jmurret May 31, 2023
76acabd
removed readrate and writerate checks for < 0
jmurret May 31, 2023
3dfe0e2
fixing copy pasta in controlplanerequestlimit_webhook.go to reference…
jmurret May 31, 2023
54b8452
putting removed copyright header back
jmurret May 31, 2023
39c138f
fixing lint errors
jmurret May 31, 2023
8e10b1c
fixup generated CRDs
thisisnotashwin Jun 1, 2023
c2b25c2
add type tests
loshz Jun 2, 2023
6e9ffd3
fixup generated CRDs
thisisnotashwin Jun 1, 2023
c55718d
add webhook tests
loshz Jun 2, 2023
725f45b
fix lint issues
loshz Jun 2, 2023
2c38012
add namespace acceptance tests
loshz Jun 2, 2023
729fd20
commenting out prepared query to validate why acceptance tests are fa…
thisisnotashwin Jun 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/2166.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
Add support for configuring Consul server-side rate limiting
```
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ ifeq (, $(shell which controller-gen))
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
cd $$CONTROLLER_GEN_TMP_DIR ;\
go mod init tmp ;\
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.10.0 ;\
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.12.0 ;\
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
}
CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen
Expand Down
65 changes: 59 additions & 6 deletions acceptance/tests/config-entries/config_entries_namespaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func TestControllerNamespaces(t *testing.T) {
require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI)

// jwt-provider
entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil)
entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts)
require.NoError(r, err)
jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry)
require.True(r, ok, "could not cast to JWTProviderConfigEntry")
Expand All @@ -227,7 +227,7 @@ func TestControllerNamespaces(t *testing.T) {
require.Equal(r, 15, jwtProviderConfigEntry.CacheConfig.Size)

// exported-services
entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil)
entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts)
require.NoError(r, err)
exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry)
require.True(r, ok, "could not cast to ExportedServicesConfigEntry")
Expand All @@ -236,6 +236,41 @@ func TestControllerNamespaces(t *testing.T) {
require.Equal(r, "partitionName", exportedServicesConfigEntry.Services[0].Consumers[0].Partition)
require.Equal(r, "peerName", exportedServicesConfigEntry.Services[0].Consumers[1].Peer)
require.Equal(r, "groupName", exportedServicesConfigEntry.Services[0].Consumers[2].SamenessGroup)

// control-plane-request-limit
entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts)
require.NoError(r, err)
rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry)
require.True(r, ok, "could not cast to RateLimitIPConfigEntry")
require.Equal(t, "permissive", rateLimitIPConfigEntry.Mode)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate)
//require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate)
//require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.WriteRate)
})
}

Expand Down Expand Up @@ -282,6 +317,9 @@ func TestControllerNamespaces(t *testing.T) {
patchPartition := "destination"
k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "exportedservices", "default", "-p", fmt.Sprintf(`{"spec": {"services": [{"name": "frontend", "namespace": "frontend", "consumers": [{"partition": "%s"}, {"peer": "peerName"}, {"samenessGroup": "groupName"}]}]}}`, patchPartition), "--type=merge")

logger.Log(t, "patching control-plane-request-limit custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "controlplanerequestlimit", "controlplanerequestlimit", "-p", `{"spec": {"mode": "disabled"}}`, "--type=merge")

counter := &retry.Counter{Count: 20, Wait: 2 * time.Second}
retry.RunWith(counter, t, func(r *retry.R) {
// service-defaults
Expand Down Expand Up @@ -350,18 +388,25 @@ func TestControllerNamespaces(t *testing.T) {
require.Equal(r, patchSNI, terminatingGatewayEntry.Services[0].SNI)

// jwt-Provider
entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil)
entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts)
require.NoError(r, err)
jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry)
require.True(r, ok, "could not cast to JWTProviderConfigEntry")
require.Equal(r, patchIssuer, jwtProviderConfigEntry.Issuer)

// exported-services
entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil)
entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts)
require.NoError(r, err)
exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry)
require.True(r, ok, "could not cast to ExportedServicesConfigEntry")
require.Equal(r, patchPartition, exportedServicesConfigEntry.Services[0].Consumers[0].Partition)

// control-plane-request-limit
entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts)
require.NoError(r, err)
rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry)
require.True(r, ok, "could not cast to RateLimitIPConfigEntry")
require.Equal(r, rateLimitIPConfigEntry.Mode, "disabled")
})
}

Expand Down Expand Up @@ -400,6 +445,9 @@ func TestControllerNamespaces(t *testing.T) {
logger.Log(t, "deleting exported-services custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "exportedservices", "default")

logger.Log(t, "deleting control-plane-request-limit custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "controlplanerequestlimit", "controlplanerequestlimit")

counter := &retry.Counter{Count: 20, Wait: 2 * time.Second}
retry.RunWith(counter, t, func(r *retry.R) {
// service-defaults
Expand Down Expand Up @@ -448,12 +496,17 @@ func TestControllerNamespaces(t *testing.T) {
require.Contains(r, err.Error(), "404 (Config entry not found")

// jwt-provider
_, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil)
_, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")

// exported-services
_, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil)
_, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")

// control-plane-request-limit
_, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")
})
Expand Down
54 changes: 54 additions & 0 deletions acceptance/tests/config-entries/config_entries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,42 @@ func TestController(t *testing.T) {
require.Equal(r, "frontend", exportedServicesConfigEntry.Services[0].Name)
require.Equal(r, "peerName", exportedServicesConfigEntry.Services[0].Consumers[0].Peer)
require.Equal(r, "groupName", exportedServicesConfigEntry.Services[0].Consumers[1].SamenessGroup)

// control-plane-request-limit
entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil)
require.NoError(r, err)
rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry)
require.True(r, ok, "could not cast to RateLimitIPConfigEntry")
require.Equal(t, "permissive", rateLimitIPConfigEntry.Mode)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate)
//require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate)
//require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.WriteRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.ReadRate)
require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.WriteRate, 100.0)

})
}

Expand Down Expand Up @@ -249,6 +285,9 @@ func TestController(t *testing.T) {
patchPeer := "destination"
k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "exportedservices", "default", "-p", fmt.Sprintf(`{"spec": {"services": [{"name": "frontend", "consumers": [{"peer": "%s"}, {"samenessGroup": "groupName"}]}]}}`, patchPeer), "--type=merge")

logger.Log(t, "patching control-plane-request-limit custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "controlplanerequestlimit", "controlplanerequestlimit", "-p", `{"spec": {"mode": "disabled"}}`, "--type=merge")

counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond}
retry.RunWith(counter, t, func(r *retry.R) {
// service-defaults
Expand Down Expand Up @@ -330,6 +369,13 @@ func TestController(t *testing.T) {
exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry)
require.True(r, ok, "could not cast to ExportedServicesConfigEntry")
require.Equal(r, patchPeer, exportedServicesConfigEntry.Services[0].Consumers[0].Peer)

// control-plane-request-limit
entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil)
require.NoError(r, err)
rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry)
require.True(r, ok, "could not cast to RateLimitIPConfigEntry")
require.Equal(r, rateLimitIPConfigEntry.Mode, "disabled")
})
}

Expand Down Expand Up @@ -368,6 +414,9 @@ func TestController(t *testing.T) {
logger.Log(t, "deleting exported-services custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "exportedservices", "default")

logger.Log(t, "deleting control-plane-request-limit custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "controlplanerequestlimit", "controlplanerequestlimit")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing an assertion that the resource has been deleted. Additionally, these steps need to be copied over to the namespaces test. you can copy paste them for the most part but you will need to add some -n kubens flags here and there. it should be clear in the test itself.

counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond}
retry.RunWith(counter, t, func(r *retry.R) {
// service-defaults
Expand Down Expand Up @@ -424,6 +473,11 @@ func TestController(t *testing.T) {
_, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")

// control-plane-request-limit
_, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")
})
}
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

apiVersion: consul.hashicorp.com/v1alpha1
kind: ControlPlaneRequestLimit
metadata:
name: controlplanerequestlimit
spec:
mode: "permissive"
readRate: 100.0
writeRate: 100.0
acl:
readRate: 100.0
writeRate: 100.0
catalog:
readRate: 100.0
writeRate: 100.0
configEntry:
readRate: 100.0
writeRate: 100.0
connectCA:
readRate: 100.0
writeRate: 100.0
coordinate:
readRate: 100.0
writeRate: 100.0
discoveryChain:
readRate: 100.0
writeRate: 100.0
health:
readRate: 100.0
writeRate: 100.0
intention:
readRate: 100.0
writeRate: 100.0
kv:
readRate: 100.0
writeRate: 100.0
tenancy:
readRate: 100.0
writeRate: 100.0
# preparedQuery:
# readRate: 100.0
# writeRate: 100.0
session:
readRate: 100.0
writeRate: 100.0
txn:
readRate: 100.0
writeRate: 100.0
3 changes: 2 additions & 1 deletion acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ resources:
- servicesplitter.yaml
- terminatinggateway.yaml
- jwtprovider.yaml
- exportedservices.yaml
- exportedservices.yaml
- controlplanerequestlimit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.10.0
creationTimestamp: null
controller-gen.kubebuilder.io/version: v0.12.0
name: gatewayclassconfigs.consul.hashicorp.com
spec:
group: consul.hashicorp.com
Expand Down
2 changes: 2 additions & 0 deletions charts/consul/templates/connect-inject-clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ rules:
- gatewayclassconfigs
- meshservices
- samenessgroups
- controlplanerequestlimits
{{- if .Values.global.peering.enabled }}
- peeringacceptors
- peeringdialers
Expand Down Expand Up @@ -54,6 +55,7 @@ rules:
- ingressgateways/status
- terminatinggateways/status
- samenessgroups/status
- controlplanerequestlimits/status
{{- if .Values.global.peering.enabled }}
- peeringacceptors/status
- peeringdialers/status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,27 @@ webhooks:
resources:
- exportedservices
sideEffects: None
- clientConfig:
service:
name: {{ template "consul.fullname" . }}-connect-injector
namespace: {{ .Release.Namespace }}
path: /mutate-v1alpha1-controlplanerequestlimits
failurePolicy: Fail
admissionReviewVersions:
- "v1beta1"
- "v1"
name: mutate-controlplanerequestlimit.consul.hashicorp.com
rules:
- apiGroups:
- consul.hashicorp.com
apiVersions:
- v1alpha1
operations:
- CREATE
- UPDATE
resources:
- controlplanerequestlimits
sideEffects: None
- name: {{ template "consul.fullname" . }}-connect-injector.consul.hashicorp.com
# The webhook will fail scheduling all pods that are not part of consul if all replicas of the webhook are unhealthy.
objectSelector:
Expand Down
Loading