Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class Arena;
}
} // namespace google

namespace grpc
{
class ChannelArguments;
} // namespace grpc

namespace opentelemetry
{
namespace proto
Expand Down Expand Up @@ -254,6 +259,13 @@ class OtlpGrpcClient
bool IsShutdown() const noexcept;

private:
friend class OtlpGrpcClientTestPeer;

// Populate gRPC channel arguments from exporter options. Shared by MakeChannel() and unit
// tests.
static void PopulateChannelArguments(const OtlpGrpcClientOptions &options,
grpc::ChannelArguments &grpc_arguments);

// Stores if this gRPC client had its Shutdown() method called
std::atomic<bool> is_shutdown_;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
namespace grpc
{
class ChannelCredentials;
}
class ChannelArguments;
} // namespace grpc

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
Expand Down Expand Up @@ -90,6 +91,13 @@ struct OtlpGrpcClientOptions

/** The backoff will be multiplied by this value after each retry attempt. */
float retry_policy_backoff_multiplier{};

/**
* Optional caller-provided gRPC channel arguments.
* This is a non-owning pointer, and the pointed-to arguments are copied when the channel is
* created.
*/
const grpc::ChannelArguments *channel_arguments{};
Comment thread
owent marked this conversation as resolved.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit - Question: can we make the lifetime requirement a bit more explicit here? Something like:

/*
This is a non-owning pointer; the pointed-to grpc::ChannelArguments must remain valid until client/channel construction copies it.
*/

Currently, "Copied when the channel is created" is directionally right, but it still leaves the required lifetime a little ambiguous.

};

} // namespace otlp
Expand Down
86 changes: 48 additions & 38 deletions exporters/otlp/src/otlp_grpc_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,54 @@ std::shared_ptr<grpc::Channel> OtlpGrpcClient::MakeChannel(const OtlpGrpcClientO
}

grpc::ChannelArguments grpc_arguments;
PopulateChannelArguments(options, grpc_arguments);

if (options.use_ssl_credentials)
{
grpc::SslCredentialsOptions ssl_opts;
ssl_opts.pem_root_certs = GetFileContentsOrInMemoryContents(
options.ssl_credentials_cacert_path, options.ssl_credentials_cacert_as_string);
#ifdef ENABLE_OTLP_GRPC_SSL_MTLS_PREVIEW
ssl_opts.pem_private_key = GetFileContentsOrInMemoryContents(options.ssl_client_key_path,
options.ssl_client_key_string);
ssl_opts.pem_cert_chain = GetFileContentsOrInMemoryContents(options.ssl_client_cert_path,
options.ssl_client_cert_string);

#endif
channel =
grpc::CreateCustomChannel(grpc_target, grpc::SslCredentials(ssl_opts), grpc_arguments);
}
else
{
channel =
grpc::CreateCustomChannel(grpc_target, grpc::InsecureChannelCredentials(), grpc_arguments);
}

#ifdef ENABLE_OTLP_GRPC_CREDENTIAL_PREVIEW
if (options.credentials)
{
if (options.use_ssl_credentials)
{
OTEL_INTERNAL_LOG_WARN(
"[OTLP GRPC Client] Both 'credentials' and 'use_ssl_credentials' options are set. "
"The former takes priority.");
}
channel = grpc::CreateCustomChannel(grpc_target, options.credentials, grpc_arguments);
}
#endif // ENABLE_OTLP_GRPC_CREDENTIAL_PREVIEW

return channel;
}

void OtlpGrpcClient::PopulateChannelArguments(const OtlpGrpcClientOptions &options,
grpc::ChannelArguments &grpc_arguments)
{
grpc_arguments = grpc::ChannelArguments{};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems redundant because it is already default initialized.


if (options.channel_arguments != nullptr)
{
grpc_arguments = *options.channel_arguments;
}
grpc_arguments.SetUserAgentPrefix(options.user_agent);

if (options.max_threads > 0)
Expand Down Expand Up @@ -399,9 +447,7 @@ std::shared_ptr<grpc::Channel> OtlpGrpcClient::MakeChannel(const OtlpGrpcClientO
]
})"};

// Allocate string with buffer large enough to hold the formatted json config
auto service_config = std::string(kServiceConfigJson.size(), '\0');
// Prior to C++17, need to explicitly cast away constness from `data()` buffer
std::snprintf(
const_cast<decltype(service_config)::value_type *>(service_config.data()),
service_config.size(), kServiceConfigJson.data(), options.retry_policy_max_attempts,
Expand All @@ -412,42 +458,6 @@ std::shared_ptr<grpc::Channel> OtlpGrpcClient::MakeChannel(const OtlpGrpcClientO
grpc_arguments.SetServiceConfigJSON(service_config);
}
#endif // ENABLE_OTLP_RETRY_PREVIEW

if (options.use_ssl_credentials)
{
grpc::SslCredentialsOptions ssl_opts;
ssl_opts.pem_root_certs = GetFileContentsOrInMemoryContents(
options.ssl_credentials_cacert_path, options.ssl_credentials_cacert_as_string);
#ifdef ENABLE_OTLP_GRPC_SSL_MTLS_PREVIEW
ssl_opts.pem_private_key = GetFileContentsOrInMemoryContents(options.ssl_client_key_path,
options.ssl_client_key_string);
ssl_opts.pem_cert_chain = GetFileContentsOrInMemoryContents(options.ssl_client_cert_path,
options.ssl_client_cert_string);

#endif
channel =
grpc::CreateCustomChannel(grpc_target, grpc::SslCredentials(ssl_opts), grpc_arguments);
}
else
{
channel =
grpc::CreateCustomChannel(grpc_target, grpc::InsecureChannelCredentials(), grpc_arguments);
}

#ifdef ENABLE_OTLP_GRPC_CREDENTIAL_PREVIEW
if (options.credentials)
{
if (options.use_ssl_credentials)
{
OTEL_INTERNAL_LOG_WARN(
"[OTLP GRPC Client] Both 'credentials' and 'use_ssl_credentials' options are set. "
"The former takes priority.");
}
channel = grpc::CreateCustomChannel(grpc_target, options.credentials, grpc_arguments);
}
#endif // ENABLE_OTLP_GRPC_CREDENTIAL_PREVIEW

return channel;
}

std::unique_ptr<grpc::ClientContext> OtlpGrpcClient::MakeClientContext(
Expand Down
63 changes: 63 additions & 0 deletions exporters/otlp/test/otlp_grpc_target_test.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include <grpc/grpc.h>
#include <grpcpp/support/channel_arguments.h>
#include <gtest/gtest.h>

#include <cstring>
#include <string>

#include "opentelemetry/exporters/otlp/otlp_grpc_client.h"
Expand All @@ -14,6 +18,36 @@ namespace exporter
namespace otlp
{

namespace
{

const grpc_arg *FindChannelArg(const grpc_channel_args &channel_args, const char *key)
{
for (std::size_t i = 0; i < channel_args.num_args; ++i)
{
const grpc_arg &arg = channel_args.args[i];
if (arg.key != nullptr && std::strcmp(arg.key, key) == 0)
{
return &arg;
}
}

return nullptr;
}

} // namespace

class OtlpGrpcClientTestPeer : public ::testing::Test
{
public:
static grpc::ChannelArguments BuildChannelArguments(const OtlpGrpcClientOptions &options)
{
grpc::ChannelArguments grpc_arguments;
OtlpGrpcClient::PopulateChannelArguments(options, grpc_arguments);
return grpc_arguments;
}
};

TEST(OtlpGrpcClientEndpointTest, GrpcClientTest)
{
OtlpGrpcClientOptions opts1;
Expand All @@ -34,6 +68,35 @@ TEST(OtlpGrpcClientEndpointTest, GrpcClientTest)
EXPECT_EQ(target3, "localhost:4317");
}

TEST_F(OtlpGrpcClientTestPeer, ChannelArgumentsOptionTest)
{
OtlpGrpcClientOptions options;
options.user_agent = "otlp-grpc-target-test";

grpc::ChannelArguments channel_arguments;
channel_arguments.SetMaxReceiveMessageSize(1);
options.channel_arguments = &channel_arguments;

auto built_arguments = BuildChannelArguments(options);
channel_arguments.SetMaxReceiveMessageSize(-1);

auto built_channel_args = built_arguments.c_channel_args();

const grpc_arg *max_receive_arg =
FindChannelArg(built_channel_args, GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH);
ASSERT_NE(max_receive_arg, nullptr);
EXPECT_EQ(max_receive_arg->type, GRPC_ARG_INTEGER);
EXPECT_EQ(max_receive_arg->value.integer, 1);

const grpc_arg *user_agent_arg =
FindChannelArg(built_channel_args, GRPC_ARG_PRIMARY_USER_AGENT_STRING);
ASSERT_NE(user_agent_arg, nullptr);
EXPECT_EQ(user_agent_arg->type, GRPC_ARG_STRING);
EXPECT_EQ(std::strncmp(user_agent_arg->value.string, options.user_agent.c_str(),
options.user_agent.size()),
0);
}

} // namespace otlp
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
Loading