-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adaptive Load command line client (#616)
An adaptive load command line executable that drives a Nighthawk Service with a series of benchmarks, varying the RPS or another input variable dynamically. `bazel build -c opt :nighthawk` `bazel-bin/nighthawk_adaptive_load_client` - `--spec-file` _pathname_ - Pathname of an `AdaptiveLoadSessionSpec` textproto defining the session - Required - Example:`test/adaptive_load/test_data/valid_session_spec.textproto` - `--output-file` _pathname_ - Pathname to write the `AdaptiveLoadSessionOutput` textproto session output - Required - `--nighthawk-service-address` _host:port_ - Address of the Nighthawk Service to be driven by the adaptive load controller over gRPC - Default `localhost:8443` - `--use-tls` - Use TLS for the gRPC connection when sending commands to the Nighthawk Service - Default false (insecure connection) Note that to run an adaptive load session, you must also have a Nighthawk Service running, e.g.: `bazel-bin/nighthawk_service &` (to run at localhost:8443) Part of #416. Signed-off-by: eric846 <[email protected]>
- Loading branch information
Showing
12 changed files
with
799 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
* @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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.