-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Make envoy_esp talk to Mixer. #29
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
Changes from 4 commits
37af5b2
e3f2672
3371f6f
0471618
d31aeb9
ca5a168
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,55 @@ | ||
|
|
||
| This Proxy will use Envoy and talk to Mixer server. | ||
|
|
||
|
|
||
| ## Build Mixer server | ||
|
|
||
| * Follow https://github.com/istio/mixer/blob/master/doc/devel/development.md to set up environment, and build via: | ||
|
|
||
| ``` | ||
| cd $(ISTIO)/mixer | ||
| bazel build ...:all | ||
| ``` | ||
|
|
||
| ## Build Envoy proxy | ||
|
|
||
| * Build target envoy_esp: | ||
|
|
||
| ``` | ||
| bazel build //src/envoy/prototype:envoy_esp | ||
| ``` | ||
|
|
||
| ## How to run it | ||
|
|
||
| * Start mixer server. In mixer folder run: | ||
|
|
||
| ``` | ||
| bazel-bin/cmd/server/mixs server | ||
| ``` | ||
|
|
||
| The server will run at port 9091 | ||
|
|
||
| * Start backend Echo server. At ESP repo (https://github.com/cloudendpoints/esp) | ||
|
|
||
| ``` | ||
| cd test/echo | ||
| npm install | ||
| node echo.js | ||
| ``` | ||
|
|
||
| * Start Envoy proxy, run | ||
|
|
||
| ``` | ||
| bazel-bin/src/envoy/prototype/envoy_esp -c src/envoy/prototype/envoy-esp.conf | ||
| ``` | ||
|
|
||
| * Then issue HTTP request to proxy. | ||
|
|
||
| ``` | ||
| curl http://localhost:9090/echo?key=API-KEY -d "hello world" | ||
| ``` | ||
|
|
||
| ## How to add attributes or facts | ||
|
|
||
| Now only some of attributes are passed to mixer. If you want to add more attributes, you can | ||
| modify this [file](https://gcp-apis.git.corp.google.com/esp/+/test/envoy-mixer/src/api_manager/mixer/mixer.cc). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,11 @@ | |
| #include "common/http/headers.h" | ||
| #include "common/http/message_impl.h" | ||
| #include "envoy/event/timer.h" | ||
| #include "google/protobuf/stubs/status.h" | ||
| #include "source/common/grpc/common.h" | ||
|
|
||
| using ::google::api_manager::utils::Status; | ||
| using ::google::protobuf::util::error::Code; | ||
|
|
||
| namespace Http { | ||
| namespace ApiManager { | ||
|
|
@@ -99,13 +104,14 @@ class HTTPRequest : public Http::Message { | |
| virtual std::string bodyAsString() override { return ""; } | ||
| }; | ||
|
|
||
| class RequestCallbacks : public AsyncClient::Callbacks { | ||
| class HTTPRequestCallbacks : public AsyncClient::Callbacks { | ||
| private: | ||
| std::unique_ptr<google::api_manager::HTTPRequest> request_; | ||
| std::unique_ptr<AsyncClient::Request> sent_request_; | ||
|
|
||
| public: | ||
| RequestCallbacks(std::unique_ptr<google::api_manager::HTTPRequest> &&request) | ||
| HTTPRequestCallbacks( | ||
| std::unique_ptr<google::api_manager::HTTPRequest> &&request) | ||
| : request_(std::move(request)) {} | ||
| virtual void onSuccess(MessagePtr &&response) override { | ||
| google::api_manager::utils::Status status( | ||
|
|
@@ -129,20 +135,81 @@ class RequestCallbacks : public AsyncClient::Callbacks { | |
| } | ||
| }; | ||
|
|
||
| namespace { | ||
| // Copy the code here from envoy/grpc/common.cc | ||
| Buffer::InstancePtr SerializeGrpcBody(const std::string &body_str) { | ||
| // http://www.grpc.io/docs/guides/wire.html | ||
| Buffer::InstancePtr body(new Buffer::OwnedImpl()); | ||
| uint8_t compressed = 0; | ||
| body->add(&compressed, sizeof(compressed)); | ||
| uint32_t size = htonl(body_str.size()); | ||
| body->add(&size, sizeof(size)); | ||
| body->add(body_str); | ||
| return body; | ||
| } | ||
| Http::MessagePtr PrepareGrpcHeaders(const std::string &upstream_cluster, | ||
| const std::string &service_full_name, | ||
| const std::string &method_name) { | ||
| Http::MessagePtr message(new Http::RequestMessageImpl()); | ||
| message->headers().insertMethod().value( | ||
| Http::Headers::get().MethodValues.Post); | ||
| message->headers().insertPath().value( | ||
| fmt::format("/{}/{}", service_full_name, method_name)); | ||
| message->headers().insertHost().value(upstream_cluster); | ||
| message->headers().insertContentType().value(Grpc::Common::GRPC_CONTENT_TYPE); | ||
| return message; | ||
|
Contributor
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. You may have missed this line message->headers().addViaMoveValue(LowerCaseString("te"), "trailers"); according to @lizan , it will not work if you miss it.
Contributor
Author
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.
Contributor
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. OK, it is good as long as it works.
Contributor
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. probably Go gRPC server doesn't enforce it. C++ gRPC server doesn't accept without the header. You can define a static |
||
| } | ||
| } // annoymous namespace | ||
|
|
||
| class GrpcRequestCallbacks : public AsyncClient::Callbacks { | ||
| private: | ||
| Env *env_; | ||
| std::unique_ptr<google::api_manager::GRPCRequest> request_; | ||
|
|
||
| public: | ||
| GrpcRequestCallbacks( | ||
| Env *env, std::unique_ptr<google::api_manager::GRPCRequest> &&request) | ||
| : env_(env), request_(std::move(request)) {} | ||
| virtual void onSuccess(MessagePtr &&response) override { | ||
| google::api_manager::utils::Status status( | ||
| std::stoi(response->headers().Status()->value().c_str()), ""); | ||
| Grpc::Common::validateResponse(*response); | ||
| env_->LogInfo("pass validate"); | ||
| // remove 5 bytes of grpc header | ||
| response->body()->drain(5); | ||
| request_->OnComplete(status, response->bodyAsString()); | ||
| delete this; | ||
| } | ||
| virtual void onFailure(AsyncClient::FailureReason reason) override { | ||
| google::api_manager::utils::Status status(-1, "Cannot connect to Mixer"); | ||
|
Contributor
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. Cannot connect to gRPC server. |
||
| request_->OnComplete(status, ""); | ||
| delete this; | ||
| } | ||
| }; | ||
|
|
||
| void Env::RunHTTPRequest( | ||
| std::unique_ptr<google::api_manager::HTTPRequest> request) { | ||
| auto &client = cm_.httpAsyncClientForCluster("api_manager"); | ||
|
|
||
| MessagePtr message{new HTTPRequest(request.get())}; | ||
| RequestCallbacks *callbacks = new RequestCallbacks(std::move(request)); | ||
| HTTPRequestCallbacks *callbacks = | ||
| new HTTPRequestCallbacks(std::move(request)); | ||
|
Contributor
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. memory leak. callbacks object is NOT freed.
Contributor
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. Let me find out how to free it.
Contributor
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. Sorry, my mistake. the object is deleted. no memory leak. |
||
| client.send( | ||
| std::move(message), *callbacks, | ||
| Optional<std::chrono::milliseconds>(std::chrono::milliseconds(10000))); | ||
| } | ||
|
|
||
| void Env::RunGRPCRequest( | ||
| std::unique_ptr<google::api_manager::GRPCRequest> request) { | ||
| // TODO: send grpc request. | ||
| auto &client = cm_.httpAsyncClientForCluster(request->server()); | ||
|
|
||
| Http::MessagePtr message = | ||
| PrepareGrpcHeaders("localhost", request->service(), request->method()); | ||
| message->body(SerializeGrpcBody(request->body())); | ||
| auto callbacks = new GrpcRequestCallbacks(this, std::move(request)); | ||
| client.send( | ||
| std::move(message), *callbacks, | ||
| Optional<std::chrono::milliseconds>(std::chrono::milliseconds(10000))); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,15 +30,27 @@ class Config : public Logger::Loggable<Logger::Id::http> { | |
| public: | ||
| Config(const Json::Object& config, Server::Instance& server) | ||
| : cm_(server.clusterManager()) { | ||
| const std::string service_config = config.getString("service_config"); | ||
| std::string service_config_content; | ||
| if (config.hasObject("service_config")) { | ||
| const std::string service_config = config.getString("service_config"); | ||
| service_config_content = ReadFile(service_config); | ||
| } else { | ||
| log().error("Service_config is not specified: {}", __func__); | ||
|
Contributor
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. service_config is required but not specified in the config. |
||
| } | ||
|
|
||
| std::string service_config_content = ReadFile(service_config); | ||
| std::string server_config_content; | ||
| if (config.hasObject("server_config")) { | ||
| const std::string server_config = config.getString("server_config"); | ||
| server_config_content = ReadFile(server_config); | ||
| } else { | ||
| log().warn("Server_config is not specified: {}", __func__); | ||
|
Contributor
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. server_config is optional, not need to log warning if not present. |
||
| } | ||
|
|
||
|
Contributor
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. Have to handle if "server_config" not there. or file doesn't exist.
Contributor
Author
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. Done. |
||
| std::unique_ptr<google::api_manager::ApiManagerEnvInterface> env( | ||
| new Env(server)); | ||
|
|
||
| api_manager_ = api_manager_factory_.GetOrCreateApiManager( | ||
| std::move(env), service_config_content, ""); | ||
| std::move(env), service_config_content, server_config_content); | ||
|
|
||
| api_manager_->Init(); | ||
| log().debug("Called ApiManager::Config constructor: {}", __func__); | ||
|
|
||
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.
define "istio.mixer.v1.Mixer" in a const char
same as "check" method.