-
Notifications
You must be signed in to change notification settings - Fork 83
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
Adaptive Load command line client #616
Changes from all commits
8ea442d
5ac755a
b8c25a5
1c19c68
7050686
0776563
16fd8f6
c383010
6e1a483
4ef1140
4111bf4
871a959
1fd77c1
edc36b2
4d0364e
aed6d94
d9ae87d
a05a6f5
8cd4d21
d814a96
5f5a885
7e20a78
9048267
306c0ec
d33f543
442cca9
677b783
cefb366
f3684df
5463051
46e0e25
f634642
3c39faa
b9c8f2b
5fc4db4
64e7852
12807f1
e8e960f
6306b4e
1ece783
70705e9
e576bc1
1fca528
ed32856
eecf00d
13179fb
3eb89e6
fae60ad
9fec200
9d1cf53
cfad844
bdde910
8100376
694fda6
1df6137
2b9e1e8
a6da150
f19768f
60fb398
d9771ce
bcc77f4
264a84d
9b6d002
f4ddcc4
c2b86a4
8ab3c97
06bcf78
9465d64
4b8ba84
15817da
f69619f
99c04d3
77a4ced
975c9de
1417e02
eaa20de
11a9161
3fa6439
45dade9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
#include "adaptive_load/adaptive_load_client_main.h" | ||
|
||
#include <cstring> | ||
#include <memory> | ||
#include <string> | ||
#include <utility> | ||
|
||
#include "envoy/common/exception.h" | ||
|
||
#include "nighthawk/adaptive_load/adaptive_load_controller.h" | ||
#include "nighthawk/common/exception.h" | ||
|
||
#include "external/envoy/source/common/grpc/google_grpc_utils.h" | ||
#include "external/envoy/source/common/protobuf/protobuf.h" | ||
|
||
#include "api/adaptive_load/adaptive_load.pb.h" | ||
#include "api/client/service.grpc.pb.h" | ||
#include "api/client/service.pb.h" | ||
|
||
#include "common/utility.h" | ||
#include "common/version_info.h" | ||
|
||
#include "fmt/ranges.h" | ||
#include "google/rpc/status.pb.h" | ||
#include "tclap/CmdLine.h" | ||
|
||
namespace Nighthawk { | ||
|
||
namespace { | ||
|
||
/** | ||
* Writes a string to a file. | ||
* | ||
* @param filesystem Envoy abstraction around filesystem functions, to facilitate unit testing. | ||
* @param path Relative or absolute path to the file to write. | ||
* @param contents String to write to the file. | ||
* | ||
* @throw Nighthawk::NighthawkException For any filesystem error. | ||
*/ | ||
void WriteFileOrThrow(Envoy::Filesystem::Instance& filesystem, absl::string_view path, | ||
absl::string_view contents) { | ||
Envoy::Filesystem::FilePtr file = filesystem.createFile(std::string(path)); | ||
const Envoy::Api::IoCallBoolResult open_result = | ||
file->open(((1 << Envoy::Filesystem::File::Operation::Write)) | | ||
(1 << (Envoy::Filesystem::File::Operation::Create))); | ||
if (!open_result.ok()) { | ||
throw Nighthawk::NighthawkException(absl::StrCat("Unable to open output file \"", path, | ||
"\": ", open_result.err_->getErrorDetails())); | ||
} | ||
const Envoy::Api::IoCallSizeResult write_result = file->write(contents); | ||
if (!write_result.ok()) { | ||
throw Nighthawk::NighthawkException(absl::StrCat("Unable to write to output file \"", path, | ||
"\": ", write_result.err_->getErrorDetails())); | ||
} | ||
const Envoy::Api::IoCallBoolResult close_result = file->close(); | ||
if (!close_result.ok()) { | ||
throw Nighthawk::NighthawkException(absl::StrCat("Unable to close output file \"", path, | ||
"\": ", close_result.err_->getErrorDetails())); | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
AdaptiveLoadClientMain::AdaptiveLoadClientMain(int argc, const char* const* argv, | ||
AdaptiveLoadController& controller, | ||
Envoy::Filesystem::Instance& filesystem) | ||
: controller_{controller}, filesystem_{filesystem} { | ||
TCLAP::CmdLine cmd("Adaptive Load tool that finds the optimal load on the target " | ||
"through a series of Nighthawk Service benchmarks.", | ||
/*delimiter=*/' ', VersionInfo::version()); | ||
|
||
TCLAP::ValueArg<std::string> nighthawk_service_address( | ||
/*flag_name=*/"", "nighthawk-service-address", | ||
"host:port for Nighthawk Service. To enable TLS, set --use-tls.", | ||
/*required=*/false, "localhost:8443", "string", cmd); | ||
TCLAP::SwitchArg use_tls( | ||
/*flag_name=*/"", "use-tls", | ||
"Use TLS for the gRPC connection from this program to the Nighthawk Service. Set environment " | ||
"variable GRPC_DEFAULT_SSL_ROOTS_FILE_PATH to override the default root certificates.", | ||
cmd); | ||
TCLAP::ValueArg<std::string> spec_filename( | ||
/*flag_name=*/"", "spec-file", | ||
"Path to a textproto file describing the adaptive load session " | ||
"(nighthawk::adaptive_load::AdaptiveLoadSessionSpec).", | ||
/*required=*/true, /*default_value=*/"", "string", cmd); | ||
TCLAP::ValueArg<std::string> output_filename( | ||
/*flag_name=*/"", "output-file", | ||
"Path to write adaptive load session output textproto " | ||
"(nighthawk::adaptive_load::AdaptiveLoadSessionOutput).", | ||
/*required=*/true, /*default_value=*/"", "string", cmd); | ||
|
||
Nighthawk::Utility::parseCommand(cmd, argc, argv); | ||
|
||
nighthawk_service_address_ = nighthawk_service_address.getValue(); | ||
use_tls_ = use_tls.getValue(); | ||
spec_filename_ = spec_filename.getValue(); | ||
output_filename_ = output_filename.getValue(); | ||
} | ||
|
||
uint32_t AdaptiveLoadClientMain::Run() { | ||
ENVOY_LOG(info, "Attempting adaptive load session: {}", DescribeInputs()); | ||
std::string spec_textproto; | ||
try { | ||
spec_textproto = filesystem_.fileReadToEnd(spec_filename_); | ||
} catch (const Envoy::EnvoyException& e) { | ||
throw Nighthawk::NighthawkException("Failed to read spec textproto file \"" + spec_filename_ + | ||
"\": " + e.what()); | ||
} | ||
nighthawk::adaptive_load::AdaptiveLoadSessionSpec spec; | ||
if (!Envoy::Protobuf::TextFormat::ParseFromString(spec_textproto, &spec)) { | ||
throw Nighthawk::NighthawkException("Unable to parse file \"" + spec_filename_ + | ||
"\" as a text protobuf (type " + spec.GetTypeName() + ")"); | ||
} | ||
std::shared_ptr<::grpc::Channel> channel = grpc::CreateChannel( | ||
nighthawk_service_address_, use_tls_ ? grpc::SslCredentials(grpc::SslCredentialsOptions()) | ||
: grpc::InsecureChannelCredentials()); | ||
std::unique_ptr<nighthawk::client::NighthawkService::StubInterface> stub( | ||
nighthawk::client::NighthawkService::NewStub(channel)); | ||
|
||
absl::StatusOr<nighthawk::adaptive_load::AdaptiveLoadSessionOutput> output_or = | ||
controller_.PerformAdaptiveLoadSession(stub.get(), spec); | ||
if (!output_or.ok()) { | ||
ENVOY_LOG(error, "Error in adaptive load session: {}", output_or.status().message()); | ||
return 1; | ||
} | ||
nighthawk::adaptive_load::AdaptiveLoadSessionOutput output = output_or.value(); | ||
WriteFileOrThrow(filesystem_, output_filename_, output.DebugString()); | ||
return 0; | ||
} | ||
|
||
std::string AdaptiveLoadClientMain::DescribeInputs() { | ||
return "Nighthawk Service " + nighthawk_service_address_ + " using " + | ||
(use_tls_ ? "TLS" : "insecure") + " connection, input file: " + spec_filename_ + | ||
", output file: " + output_filename_; | ||
} | ||
|
||
} // namespace Nighthawk |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#pragma once | ||
|
||
#include "envoy/common/time.h" | ||
#include "envoy/filesystem/filesystem.h" | ||
|
||
#include "nighthawk/adaptive_load/adaptive_load_controller.h" | ||
|
||
#include "external/envoy/source/common/common/logger.h" | ||
|
||
namespace Nighthawk { | ||
|
||
/** | ||
* Main implementation of the CLI wrapper around the adaptive load controller library. | ||
* Parses command line options, reads adaptive load session spec proto from a file, | ||
* runs an adaptive load session, and writes the output proto to a file. | ||
*/ | ||
class AdaptiveLoadClientMain : public Envoy::Logger::Loggable<Envoy::Logger::Id::main> { | ||
public: | ||
/** | ||
* Parses the command line arguments to class members. | ||
* | ||
* @param argc Standard argc passed through from the exe entry point. | ||
* @param argv Standard argv passed through from the exe entry point. | ||
* @param controller Adaptive load controller, passed in to allow unit testing of this class. | ||
* @param filesystem Abstraction of the filesystem, passed in to allow unit testing of this | ||
* class. | ||
* | ||
* @throw Nighthawk::Client::MalformedArgvException If command line constraints are violated. | ||
*/ | ||
AdaptiveLoadClientMain(int argc, const char* const* argv, AdaptiveLoadController& controller, | ||
Envoy::Filesystem::Instance& filesystem); | ||
/** | ||
* Loads the adaptive load session spec proto from a file, runs an adaptive load session, and | ||
* writes the output proto to a file. File paths are taken from class members initialized in the | ||
* constructor. | ||
* | ||
* @return Exit code for this process. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we state that 0 is good or is that implicit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a standard in linux, so we can rely on that understanding. https://linuxize.com/post/bash-exit/#exit-status |
||
* @throw Nighthawk::NighthawkException If a file read or write error occurs. | ||
*/ | ||
uint32_t Run(); | ||
/** | ||
* Describes the program inputs as parsed from the command line. | ||
*/ | ||
std::string DescribeInputs(); | ||
|
||
private: | ||
std::string nighthawk_service_address_; | ||
bool use_tls_; | ||
std::string spec_filename_; | ||
std::string output_filename_; | ||
AdaptiveLoadController& controller_; | ||
Envoy::Filesystem::Instance& filesystem_; | ||
}; | ||
|
||
} // namespace Nighthawk |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Command line adaptive load controller driving a Nighthawk Service. | ||
#include <iostream> | ||
|
||
#include "nighthawk/common/exception.h" | ||
|
||
#include "external/envoy/source/common/event/real_time_system.h" | ||
#include "external/envoy/source/exe/platform_impl.h" | ||
|
||
#include "common/nighthawk_service_client_impl.h" | ||
|
||
#include "absl/debugging/symbolize.h" | ||
#include "adaptive_load/adaptive_load_client_main.h" | ||
#include "adaptive_load/adaptive_load_controller_impl.h" | ||
#include "adaptive_load/metrics_evaluator_impl.h" | ||
#include "adaptive_load/session_spec_proto_helper_impl.h" | ||
|
||
// NOLINT(namespace-nighthawk) | ||
|
||
int main(int argc, char* argv[]) { | ||
#ifndef __APPLE__ | ||
// absl::Symbolize mostly works without this, but this improves corner case | ||
// handling, such as running in a chroot jail. | ||
absl::InitializeSymbolizer(argv[0]); | ||
#endif | ||
Nighthawk::NighthawkServiceClientImpl nighthawk_service_client; | ||
Nighthawk::MetricsEvaluatorImpl metrics_evaluator; | ||
Nighthawk::AdaptiveLoadSessionSpecProtoHelperImpl spec_proto_helper; | ||
Envoy::Event::RealTimeSystem time_system; // NO_CHECK_FORMAT(real_time) | ||
Nighthawk::AdaptiveLoadControllerImpl controller(nighthawk_service_client, metrics_evaluator, | ||
spec_proto_helper, time_system); | ||
Envoy::PlatformImpl platform_impl; | ||
try { | ||
Nighthawk::AdaptiveLoadClientMain program(argc, argv, controller, platform_impl.fileSystem()); | ||
return program.Run(); | ||
} catch (const Nighthawk::Client::NoServingException& e) { | ||
return EXIT_SUCCESS; | ||
} catch (const Nighthawk::Client::MalformedArgvException& e) { | ||
std::cerr << "Invalid args: " << e.what() << std::endl; | ||
return EXIT_FAILURE; | ||
} catch (const Nighthawk::NighthawkException& e) { | ||
std::cerr << "Failure: " << e.what() << std::endl; | ||
return EXIT_FAILURE; | ||
} | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we file a future bug to enable some dependency injection of the channel here? in the future, people might want to enable other ways of connecting to the nighthawk service besides the two provided here. but we don't need to prematurely allow for that case until we have a specific usecase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #626.