Skip to content

Commit

Permalink
test(pd): add integration test for grpc reflection
Browse files Browse the repository at this point in the history
Refs #4392. We want to ensure that server reflection remains
working, despite changes to the tonic dependencies (#4400)
and proto compiling logic #4422. While the most effective test
is exercising all these protos regularly, we currently lack
solid coverage, so this superficial integration test is a sanity-check
to save time versus building locally and inspecting the output manually.
  • Loading branch information
conorsch committed May 23, 2024
1 parent a9d5c7b commit 711494d
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
sh -c "$(curl --location https://raw.githubusercontent.com/F1bonacc1/process-compose/main/scripts/get-pc.sh)" --
-d -b ~/bin
- name: Install grpcurl
run: ./deployments/scripts/install-grpcurl

- name: Run the smoke test suite
run: |
export PATH="$HOME/bin:$PATH"
Expand Down
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.

3 changes: 3 additions & 0 deletions crates/bin/pd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,6 @@ penumbra-proof-params = { workspace = true, features = [
"bundled-proving-keys",
"download-proving-keys",
], default-features = true }
assert_cmd = { workspace = true }
predicates = "2.1"
prost-reflect = "0.13.1"
74 changes: 74 additions & 0 deletions crates/bin/pd/tests/network_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
//! headers in all contexts. Does NOT evaluate application logic; see the
//! integration tests for pcli/pclientd for that.
use assert_cmd::Command;
use http::StatusCode;
use penumbra_proto::FILE_DESCRIPTOR_SET;
use predicates::prelude::*;
use prost_reflect::{DescriptorPool, ServiceDescriptor};
use url::Url;

#[ignore]
#[tokio::test]
Expand Down Expand Up @@ -39,3 +44,72 @@ async fn check_minifront_http_ok() -> anyhow::Result<()> {
assert_eq!(r.status(), StatusCode::OK);
Ok(())
}

#[ignore]
#[tokio::test]
/// Validate that gRPC server reflection is enabled and working, by calling out
/// to `grpcurl` and verifying that it can view methods. See GH4392 for context.
async fn check_grpc_server_reflection() -> anyhow::Result<()> {
let pd_url: Url = std::env::var("PENUMBRA_NODE_PD_URL")
.unwrap_or("http://localhost:8080".to_string())
.parse()
.unwrap();
let pd_hostname = format!("{}:{}", pd_url.host().unwrap(), pd_url.port().unwrap());
let mut args = Vec::<String>::new();
if pd_url.scheme() == "http" {
args.push("-plaintext".to_owned());
}
args.push(pd_hostname);
// grpcurl takes `list` as a command, to inspect the server reflection API.
args.push("list".to_owned());

// Permit override of the fullpath to the `grpcurl` binary, in case we want
// to test multiple versions in CI.
let grpcurl_path = std::env::var("GRPCURL_PATH").unwrap_or("grpcurl".to_string());
let std_cmd = std::process::Command::new(grpcurl_path);
let mut cmd = Command::from_std(std_cmd);
cmd.args(args);

// Here we hardcode a few specific checks, to verify they're present.
// This ensures reflection is ostensibly working, and doesn't assume
// that the FILE_DESCRIPTOR tonic-build logic is wired up.
let methods = vec![
"penumbra.core.app.v1.QueryService",
// "grpc.reflection.v1alpha.ServerReflection",
"grpc.reflection.v1.ServerReflection",
"ibc.core.channel.v1.Query",
];
for m in methods {
cmd.assert().stdout(predicate::str::contains(m));
}

// Here we look up the gRPC services exported from the proto crate,
// as FILE_DESCRIPTOR_SET. All of these methods should be visible
// to the `grpcurl` list command, if reflection is working.
let grpc_service_names = get_all_grpc_services()?;
// Sanity-check that we actually got results.
assert!(grpc_service_names.len() > 5);
for m in grpc_service_names {
cmd.assert().stdout(predicate::str::contains(m));
}
Ok(())
}

/// Returns a Vec<String> where each String is a fully qualified gRPC query service name,
/// such as:
///
/// - penumbra.core.component.community_pool.v1.QueryService
/// - penumbra.view.v1.ViewService
/// - penumbra.core.component.dex.v1.SimulationService
///
/// The gRPC service names are read from the [penumbra_proto] crate's [FILE_DESCRIPTOR_SET],
/// which is exported at build time.
fn get_all_grpc_services() -> anyhow::Result<Vec<String>> {
// Intentionally verbose to be explicit.
let services: Vec<ServiceDescriptor> = DescriptorPool::decode(FILE_DESCRIPTOR_SET)?
.services()
.into_iter()
.collect();
let service_names: Vec<String> = services.iter().map(|x| x.full_name().to_owned()).collect();
Ok(service_names)
}
34 changes: 34 additions & 0 deletions deployments/scripts/install-grpcurl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/bash
# Utility script to download a specific version of grpcurl for use
# in testing Penumbra, specifically in validating the gRPC reflection
# APIs via integration tests.
# Designed to be used in CI contexts, to bootstrap a testing setup quickly.
set -euo pipefail


# Sane defaults
GRPCURL_VERSION="${GRPCURL_VERSION:-1.9.1}"

# Download and extract
grpcurl_download_url="https://github.com/fullstorydev/grpcurl/releases/download/v${GRPCURL_VERSION}/grpcurl_${GRPCURL_VERSION}_linux_x86_64.tar.gz"
grpcurl_temp_dir="$(mktemp -d)"
pushd "$grpcurl_temp_dir" > /dev/null
curl -sSfL -O "$grpcurl_download_url"
tar -xzf "grpcurl_${GRPCURL_VERSION}_linux_x86_64.tar.gz" grpcurl
trap 'rm -r "$grpcurl_temp_dir"' EXIT

# Try to write to system-wide location.
if [[ -w /usr/local/bin/ ]] ; then
mv -v grpcurl /usr/local/bin/
else
grpcurl_install_dir="${HOME:?}/bin"
>&2 echo "WARNING: /usr/local/bin/ not writable, installing grpcurl to $grpcurl_install_dir"
mkdir -p "$grpcurl_install_dir"
mv -v grpcurl "${grpcurl_install_dir}/"
export PATH="$PATH:$grpcurl_install_dir"
fi

# Sanity checks
echo "Checking that grpcurl is installed:"
which grpcurl
grpcurl --version
6 changes: 6 additions & 0 deletions deployments/scripts/smoke-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ if ! hash process-compose > /dev/null 2>&1 ; then
exit 1
fi

if ! hash grpcurl > /dev/null 2>&1 ; then
>&2 echo "ERROR: grpcurl not found in PATH"
>&2 echo "Install it via https://github.com/fullstorydev/grpcurl/"
exit 1
fi

# Check for interactive terminal session, enable TUI if yes.
if [[ -t 1 ]] ; then
use_tui="true"
Expand Down

0 comments on commit 711494d

Please sign in to comment.