Skip to content

Commit

Permalink
Merge pull request #224 from Kuadrant/grpc-reflection
Browse files Browse the repository at this point in the history
gRPC server reflection
  • Loading branch information
eguzki authored Jan 16, 2024
2 parents 7fb080b + d55df6d commit 5e44784
Show file tree
Hide file tree
Showing 17 changed files with 169 additions and 8 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ port, that implements the Envoy Rate Limit protocol (v3).

- [**Getting started**](#getting-started)
- [**How it works**](/doc/how-it-works.md)
- [**Configuration**](/doc/server/configuration.md)
- [**Development**](#development)
- [**Testing Environment**](limitador-server/sandbox/README.md)
- [**Kubernetes**](limitador-server/kubernetes/)
Expand Down
2 changes: 2 additions & 0 deletions doc/server/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Options:
Validates the LIMITS_FILE and exits
-H, --rate-limit-headers <rate_limit_headers>
Enables rate limit response headers [default: NONE] [possible values: NONE, DRAFT_VERSION_03]
--grpc-reflection-service
Enables gRPC server reflection service
-h, --help
Print help
-V, --version
Expand Down
1 change: 1 addition & 0 deletions limitador-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ limitador = { path = "../limitador", features = ['lenient_conditions'] }
tokio = { version = "1", features = ["full"] }
thiserror = "1"
tonic = "0.10"
tonic-reflection = "0.10.2"
prost = "0.12"
prost-types = "0.12"
serde_yaml = "0.9"
Expand Down
22 changes: 14 additions & 8 deletions limitador-server/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::env;
use std::error::Error;
use std::path::PathBuf;
use std::process::Command;

fn main() -> Result<(), Box<dyn Error>> {
Expand All @@ -9,14 +11,18 @@ fn main() -> Result<(), Box<dyn Error>> {
}

fn generate_protobuf() -> Result<(), Box<dyn Error>> {
tonic_build::configure().build_server(true).compile(
&["envoy/service/ratelimit/v3/rls.proto"],
&[
"vendor/protobufs/data-plane-api",
"vendor/protobufs/protoc-gen-validate",
"vendor/protobufs/xds",
],
)?;
let original_out_dir = PathBuf::from(env::var("OUT_DIR")?);
tonic_build::configure()
.build_server(true)
.file_descriptor_set_path(original_out_dir.join("rls.bin"))
.compile(
&["envoy/service/ratelimit/v3/rls.proto"],
&[
"vendor/protobufs/data-plane-api",
"vendor/protobufs/protoc-gen-validate",
"vendor/protobufs/xds",
],
)?;
Ok(())
}

Expand Down
1 change: 1 addition & 0 deletions limitador-server/sandbox/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/bin/
*.crt
*.key
*.pem
Expand Down
21 changes: 21 additions & 0 deletions limitador-server/sandbox/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,29 @@ clean-containers: ## clean containers
- $(DOCKER) compose -f docker-compose-envoy.yaml -f docker-compose-limitador-redis.yaml down --volumes --remove-orphans
- $(DOCKER)_compose -f docker-compose-envoy.yaml -f docker-compose-limitador-redis-cached.yaml down --volumes --remove-orphans
- $(DOCKER) compose -f docker-compose-envoy.yaml -f docker-compose-limitador-infinispan.yaml down --volumes --remove-orphans
- $(DOCKER) compose -f docker-compose-envoy.yaml -f docker-compose-limitador-disk.yaml down --volumes --remove-orphans
- $(MAKE) cleancerts

clean: ## clean all
- $(MAKE) clean-containers
- $(MAKE) redis-clean-certs

GRPCURL=$(PROJECT_PATH)/bin/grpcurl
$(GRPCURL):
$(call go-install-tool,$(GRPCURL),github.com/fullstorydev/grpcurl/cmd/[email protected])

.PHONY: grpcurl
grpcurl: $(GRPCURL) ## Download grpcurl locally if necessary.

# go-install-tool will 'go install' any package $2 and install it to $1.
define go-install-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_PATH)/bin go install $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef
74 changes: 74 additions & 0 deletions limitador-server/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,87 @@ Check out `make help` for all the targets.
| Redis Secured | `make deploy-redis-tls` | Uses Redis with TLS and password protected to store counters |
| Redis Cached | `make deploy-redis-cached` | Uses Redis to store counters, with an in-memory cache |
| Infinispan | `make deploy-infinispan` | Uses Infinispan to store counters |
| Disk | `make deploy-disk` | Uses disk to store counters |

### Limitador's admin HTTP endpoint

Limits

```bash
curl -i http://127.0.0.1:18080/limits/test_namespace
```

Counters

```bash
curl -i http://127.0.0.1:18080/counters/test_namespace
```

Metrics

```bash
curl -i http://127.0.0.1:18080/metrics
```

### Limitador's GRPC RateLimitService endpoint

Get `grpcurl`. You need [Go SDK](https://golang.org/doc/install) installed.

Golang version >= 1.18 (from [fullstorydev/grpcurl](https://github.com/fullstorydev/grpcurl/blob/v1.8.9/go.mod#L3))

```bash
make grpcurl
```

Inspect `RateLimitService` GRPC service

```bash
bin/grpcurl -plaintext 127.0.0.1:18081 describe envoy.service.ratelimit.v3.RateLimitService
```

Make a custom request

```bash
bin/grpcurl -plaintext -d @ 127.0.0.1:18081 envoy.service.ratelimit.v3.RateLimitService.ShouldRateLimit <<EOM
{
"domain": "test_namespace",
"hits_addend": 1,
"descriptors": [
{
"entries": [
{
"key": "req.method",
"value": "POST"
}
]
}
]
}
EOM
```

Do repeated requests. As the limit is set to max 5 request for 60 seconds,
you should see `OVER_LIMIT` response after 5 requests.

```bash
while :; do bin/grpcurl -plaintext -d @ 127.0.0.1:18081 envoy.service.ratelimit.v3.RateLimitService.ShouldRateLimit <<EOM; sleep 1; done
{
"domain": "test_namespace",
"hits_addend": 1,
"descriptors": [
{
"entries": [
{
"key": "req.method",
"value": "POST"
}
]
}
]
}
EOM
```
### Downstream traffic
**Upstream** service implemented by [httpbin.org](https://httpbin.org/)
Expand Down
2 changes: 2 additions & 0 deletions limitador-server/sandbox/docker-compose-limitador-disk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- disk
- "/tmp/data"
Expand All @@ -24,5 +25,6 @@ services:
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- --http-port
- "8080"
- -vvvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- infinispan
- --cache-name
Expand All @@ -29,6 +30,7 @@ services:
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
infinispan:
Expand Down
2 changes: 2 additions & 0 deletions limitador-server/sandbox/docker-compose-limitador-memory.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- memory
expose:
- "8080"
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- redis_cached
- --ttl
Expand All @@ -33,6 +34,7 @@ services:
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
redis:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- redis
- rediss://:foobared@redis:6379/#insecure
Expand Down
2 changes: 2 additions & 0 deletions limitador-server/sandbox/docker-compose-limitador-redis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- redis
- redis://redis:6379
Expand All @@ -25,6 +26,7 @@ services:
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
redis:
Expand Down
4 changes: 4 additions & 0 deletions limitador-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct Configuration {
pub limit_name_in_labels: bool,
pub log_level: Option<LevelFilter>,
pub rate_limit_headers: RateLimitHeaders,
pub grpc_reflection_service: bool,
}

pub mod env {
Expand Down Expand Up @@ -83,6 +84,7 @@ impl Configuration {
http_port: u16,
limit_name_in_labels: bool,
rate_limit_headers: RateLimitHeaders,
grpc_reflection_service: bool,
) -> Self {
Self {
limits_file,
Expand All @@ -94,6 +96,7 @@ impl Configuration {
limit_name_in_labels,
log_level: None,
rate_limit_headers,
grpc_reflection_service,
}
}

Expand Down Expand Up @@ -121,6 +124,7 @@ impl Default for Configuration {
limit_name_in_labels: false,
log_level: None,
rate_limit_headers: RateLimitHeaders::None,
grpc_reflection_service: false,
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions limitador-server/src/envoy_rls/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,32 @@ pub fn to_response_header(
headers
}

mod rls_proto {
pub(crate) const RLS_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("rls");
}

pub async fn run_envoy_rls_server(
address: String,
limiter: Arc<Limiter>,
rate_limit_headers: RateLimitHeaders,
grpc_reflection_service: bool,
) -> Result<(), transport::Error> {
let rate_limiter = MyRateLimiter::new(limiter, rate_limit_headers);
let svc = RateLimitServiceServer::new(rate_limiter);

let reflection_service = match grpc_reflection_service {
false => None,
true => Some(
tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(rls_proto::RLS_DESCRIPTOR_SET)
.build()
.unwrap(),
),
};

Server::builder()
.add_service(svc)
.add_optional_service(reflection_service)
.serve(address.parse().unwrap())
.await
}
Expand Down
10 changes: 10 additions & 0 deletions limitador-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let envoy_rls_address = config.rlp_address();
let http_api_address = config.http_address();
let rate_limit_headers = config.rate_limit_headers.clone();
let grpc_reflection_service = config.grpc_reflection_service;

let rate_limiter: Arc<Limiter> = match Limiter::new(config).await {
Ok(limiter) => Arc::new(limiter),
Expand Down Expand Up @@ -390,6 +391,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
envoy_rls_address.to_string(),
rate_limiter.clone(),
rate_limit_headers,
grpc_reflection_service,
));

info!("HTTP server starting on {}", http_api_address);
Expand Down Expand Up @@ -515,6 +517,13 @@ fn create_config() -> (Configuration, &'static str) {
]))
.help("Enables rate limit response headers"),
)
.arg(
Arg::new("grpc_reflection_service")
.long("grpc-reflection-service")
.action(ArgAction::SetTrue)
.display_order(9)
.help("Enables gRPC server reflection service"),
)
.subcommand(
Command::new("memory")
.display_order(1)
Expand Down Expand Up @@ -745,6 +754,7 @@ fn create_config() -> (Configuration, &'static str) {
matches.get_flag("limit_name_in_labels")
|| env_option_is_enabled("LIMIT_NAME_IN_PROMETHEUS_LABELS"),
rate_limit_headers,
matches.get_flag("grpc_reflection_service"),
);

config.log_level = match matches.get_count("v") {
Expand Down

0 comments on commit 5e44784

Please sign in to comment.