diff --git a/include/istio/mixerclient/check_response.h b/include/istio/mixerclient/check_response.h index 86000628b1e..82928d0c06f 100644 --- a/include/istio/mixerclient/check_response.h +++ b/include/istio/mixerclient/check_response.h @@ -22,21 +22,15 @@ namespace istio { namespace mixerclient { -// The CheckResponseInfo holds response information in detail. -struct CheckResponseInfo { - // Whether this check response is from cache. - bool is_check_cache_hit{false}; +// The CheckResponseInfo exposes policy and quota check details to the check +// callbacks. +class CheckResponseInfo { + public: + virtual ~CheckResponseInfo(){}; - // Whether this quota response is from cache. - bool is_quota_cache_hit{false}; + virtual const ::google::protobuf::util::Status& status() const = 0; - // The check and quota response status. - ::google::protobuf::util::Status response_status{ - ::google::protobuf::util::Status::UNKNOWN}; - - // Routing directive (applicable if the status is OK) - ::istio::mixer::v1::RouteDirective route_directive{ - ::istio::mixer::v1::RouteDirective::default_instance()}; + virtual const ::istio::mixer::v1::RouteDirective& routeDirective() const = 0; }; } // namespace mixerclient diff --git a/include/istio/mixerclient/client.h b/include/istio/mixerclient/client.h index d869a1144f0..0d904856084 100644 --- a/include/istio/mixerclient/client.h +++ b/include/istio/mixerclient/client.h @@ -19,6 +19,8 @@ #include "environment.h" #include "include/istio/quota_config/requirement.h" #include "options.h" +#include "src/istio/mixerclient/check_context.h" +#include "src/istio/mixerclient/shared_attributes.h" #include @@ -84,13 +86,13 @@ class MixerClient { // The response data from mixer will be consumed by mixer client. // A check call. - virtual CancelFunc Check( - const ::istio::mixer::v1::Attributes& attributes, - const std::vector<::istio::quota_config::Requirement>& quotas, - TransportCheckFunc transport, CheckDoneFunc on_done) = 0; + virtual CancelFunc Check(istio::mixerclient::CheckContextSharedPtr& context, + TransportCheckFunc transport, + CheckDoneFunc on_done) = 0; // A report call. - virtual void Report(const ::istio::mixer::v1::Attributes& attributes) = 0; + virtual void Report( + const istio::mixerclient::SharedAttributesSharedPtr& attributes) = 0; // Get statistics. virtual void GetStatistics(Statistics* stat) const = 0; diff --git a/include/istio/utils/attributes_builder.h b/include/istio/utils/attributes_builder.h index a91a45fd832..4d1f0996284 100644 --- a/include/istio/utils/attributes_builder.h +++ b/include/istio/utils/attributes_builder.h @@ -162,6 +162,8 @@ class AttributesBuilder { return *filters; } + // TODO(jblatt) audit all uses of raw pointers and replace as many as possible + // with unique/shared pointers. ::istio::mixer::v1::Attributes *attributes_; }; diff --git a/include/istio/utils/simple_lru_cache_inl.h b/include/istio/utils/simple_lru_cache_inl.h index 09eea44fac2..3d6c8b6156d 100644 --- a/include/istio/utils/simple_lru_cache_inl.h +++ b/include/istio/utils/simple_lru_cache_inl.h @@ -497,7 +497,7 @@ class SimpleLRUCacheBase { // so we implement it in the derived SimpleLRUCache. virtual void RemoveElement(const Key& k, Value* value) = 0; - virtual void DebugIterator(const Key& k, const Value* value, int pin_count, + virtual void DebugIterator(const Key&, const Value* value, int pin_count, int64_t last_timestamp, bool is_deferred, std::string* output) const { std::stringstream ss; @@ -1054,7 +1054,7 @@ class SimpleLRUCache EQ>(total_units) {} protected: - virtual void RemoveElement(const Key& k, Value* value) { delete value; } + virtual void RemoveElement(const Key&, Value* value) { delete value; } private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SimpleLRUCache); @@ -1077,7 +1077,7 @@ class SimpleLRUCacheWithDeleter : Base(total_units), deleter_(deleter) {} protected: - virtual void RemoveElement(const Key& k, Value* value) { deleter_(value); } + virtual void RemoveElement(const Key&, Value* value) { deleter_(value); } private: Deleter deleter_; diff --git a/src/envoy/http/mixer/filter.cc b/src/envoy/http/mixer/filter.cc index 3de446898ca..c3ec6bba84f 100644 --- a/src/envoy/http/mixer/filter.cc +++ b/src/envoy/http/mixer/filter.cc @@ -24,6 +24,7 @@ #include "src/envoy/utils/header_update.h" using ::google::protobuf::util::Status; +using ::istio::mixer::v1::RouteDirective; using ::istio::mixerclient::CheckResponseInfo; namespace Envoy { @@ -147,7 +148,8 @@ void Filter::setDecoderFilterCallbacks( } void Filter::completeCheck(const CheckResponseInfo& info) { - auto status = info.response_status; + const Status& status = info.status(); + ENVOY_LOG(debug, "Called Mixer::Filter : check complete {}", status.ToString()); // This stream has been reset, abort the callback. @@ -155,7 +157,7 @@ void Filter::completeCheck(const CheckResponseInfo& info) { return; } - route_directive_ = info.route_directive; + route_directive_ = info.routeDirective(); Utils::CheckResponseInfoToStreamInfo(info, decoder_callbacks_->streamInfo()); diff --git a/src/envoy/tcp/mixer/filter.cc b/src/envoy/tcp/mixer/filter.cc index 9cb94a25efc..79ba5e9b05f 100644 --- a/src/envoy/tcp/mixer/filter.cc +++ b/src/envoy/tcp/mixer/filter.cc @@ -138,7 +138,7 @@ Network::FilterStatus Filter::onNewConnection() { } void Filter::completeCheck(const CheckResponseInfo &info) { - const auto &status = info.response_status; + const auto &status = info.status(); ENVOY_LOG(debug, "Called tcp filter completeCheck: {}", status.ToString()); cancel_check_ = nullptr; if (state_ == State::Closed) { diff --git a/src/envoy/utils/utils.cc b/src/envoy/utils/utils.cc index 0337c619557..d598d12bc0e 100644 --- a/src/envoy/utils/utils.cc +++ b/src/envoy/utils/utils.cc @@ -143,13 +143,12 @@ Status ParseJsonMessage(const std::string& json, Message* output) { void CheckResponseInfoToStreamInfo( const istio::mixerclient::CheckResponseInfo& check_response, StreamInfo::StreamInfo& stream_info) { - if (!check_response.response_status.ok()) { + if (!check_response.status().ok()) { stream_info.setResponseFlag( StreamInfo::ResponseFlag::UnauthorizedExternalService); ProtobufWkt::Struct metadata; auto& fields = *metadata.mutable_fields(); - fields["status"].set_string_value( - check_response.response_status.ToString()); + fields["status"].set_string_value(check_response.status().ToString()); stream_info.setDynamicMetadata(istio::utils::kMixerMetadataKey, metadata); } } diff --git a/src/envoy/utils/utils_test.cc b/src/envoy/utils/utils_test.cc index acd2bb4f9de..66a12024f6c 100644 --- a/src/envoy/utils/utils_test.cc +++ b/src/envoy/utils/utils_test.cc @@ -15,6 +15,7 @@ #include "src/envoy/utils/utils.h" #include "mixer/v1/config/client/client_config.pb.h" +#include "src/istio/mixerclient/check_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/test_common/utility.h" @@ -48,8 +49,9 @@ TEST(UtilsTest, ParseMessageWithUnknownField) { } TEST(UtilsTest, CheckResponseInfoToStreamInfo) { - ::istio::mixerclient::CheckResponseInfo - check_response; // by default status is unknown + auto attributes = std::make_shared<::istio::mixerclient::SharedAttributes>(); + ::istio::mixerclient::CheckContext check_response( + false /* fail_open */, attributes); // by default status is unknown Envoy::StreamInfo::MockStreamInfo mock_stream_info; EXPECT_CALL( diff --git a/src/istio/control/BUILD b/src/istio/control/BUILD index 637f9817586..f58bfe9ba94 100644 --- a/src/istio/control/BUILD +++ b/src/istio/control/BUILD @@ -21,7 +21,6 @@ cc_library( ], hdrs = [ "client_context_base.h", - "request_context.h", ], visibility = [":__subpackages__"], deps = [ diff --git a/src/istio/control/client_context_base.cc b/src/istio/control/client_context_base.cc index 2c9690d2564..069d596c0eb 100644 --- a/src/istio/control/client_context_base.cc +++ b/src/istio/control/client_context_base.cc @@ -80,37 +80,22 @@ ClientContextBase::ClientContextBase(const TransportConfig& config, options.env = env; mixer_client_ = ::istio::mixerclient::CreateMixerClient(options); CreateLocalAttributes(local_node, &local_attributes_); + network_fail_open_ = options.check_options.network_fail_open; } -CancelFunc ClientContextBase::SendCheck(TransportCheckFunc transport, - CheckDoneFunc on_done, - RequestContext* request) { - // Intercept the callback to save check status in request_context - auto local_on_done = [request, - on_done](const CheckResponseInfo& check_response_info) { - // save the check status code - request->check_status = check_response_info.response_status; - - utils::AttributesBuilder builder(request->attributes); - builder.AddBool(utils::AttributeName::kCheckCacheHit, - check_response_info.is_check_cache_hit); - builder.AddBool(utils::AttributeName::kQuotaCacheHit, - check_response_info.is_quota_cache_hit); - on_done(check_response_info); - }; - +CancelFunc ClientContextBase::SendCheck( + const TransportCheckFunc& transport, const CheckDoneFunc& on_done, + ::istio::mixerclient::CheckContextSharedPtr& context) { MIXER_DEBUG("Check attributes: %s", - request->attributes->DebugString().c_str()); - - return mixer_client_->Check(*request->attributes, request->quotas, transport, - local_on_done); + context->attributes()->DebugString().c_str()); + return mixer_client_->Check(context, transport, on_done); } -void ClientContextBase::SendReport(const RequestContext& request) { +void ClientContextBase::SendReport( + const istio::mixerclient::SharedAttributesSharedPtr& attributes) { MIXER_DEBUG("Report attributes: %s", - request.attributes->DebugString().c_str()); - - mixer_client_->Report(*request.attributes); + attributes->attributes()->DebugString().c_str()); + mixer_client_->Report(attributes); } void ClientContextBase::GetStatistics(Statistics* stat) const { diff --git a/src/istio/control/client_context_base.h b/src/istio/control/client_context_base.h index 560c090083c..5f7f741dc8b 100644 --- a/src/istio/control/client_context_base.h +++ b/src/istio/control/client_context_base.h @@ -20,7 +20,8 @@ #include "include/istio/utils/attribute_names.h" #include "include/istio/utils/local_attributes.h" #include "mixer/v1/config/client/client_config.pb.h" -#include "request_context.h" +#include "src/istio/mixerclient/check_context.h" +#include "src/istio/mixerclient/shared_attributes.h" namespace istio { namespace control { @@ -40,17 +41,20 @@ class ClientContextBase { bool outbound, ::istio::utils::LocalAttributes& local_attributes) : mixer_client_(std::move(mixer_client)), outbound_(outbound), - local_attributes_(local_attributes) {} + local_attributes_(local_attributes), + network_fail_open_(false) {} // virtual destrutor virtual ~ClientContextBase() {} // Use mixer client object to make a Check call. ::istio::mixerclient::CancelFunc SendCheck( - ::istio::mixerclient::TransportCheckFunc transport, - ::istio::mixerclient::CheckDoneFunc on_done, RequestContext* request); + const ::istio::mixerclient::TransportCheckFunc& transport, + const ::istio::mixerclient::CheckDoneFunc& on_done, + ::istio::mixerclient::CheckContextSharedPtr& check_context); // Use mixer client object to make a Report call. - void SendReport(const RequestContext& request); + void SendReport( + const istio::mixerclient::SharedAttributesSharedPtr& attributes); // Get statistics. void GetStatistics(::istio::mixerclient::Statistics* stat) const; @@ -60,6 +64,8 @@ class ClientContextBase { void AddLocalNodeForwardAttribues( ::istio::mixer::v1::Attributes* request) const; + bool NetworkFailOpen() const { return network_fail_open_; } + private: // The mixer client object with check cache and report batch features. std::unique_ptr<::istio::mixerclient::MixerClient> mixer_client_; @@ -69,6 +75,8 @@ class ClientContextBase { // local attributes - owned by the client context. ::istio::utils::LocalAttributes local_attributes_; + + bool network_fail_open_; }; } // namespace control diff --git a/src/istio/control/http/attributes_builder.cc b/src/istio/control/http/attributes_builder.cc index 795b70b83e2..32ffa5c8423 100644 --- a/src/istio/control/http/attributes_builder.cc +++ b/src/istio/control/http/attributes_builder.cc @@ -17,6 +17,7 @@ #include +#include "google/protobuf/stubs/status.h" #include "include/istio/utils/attribute_names.h" #include "include/istio/utils/attributes_builder.h" #include "include/istio/utils/status.h" @@ -35,7 +36,7 @@ const std::set kGrpcContentTypes{ } // namespace void AttributesBuilder::ExtractRequestHeaderAttributes(CheckData *check_data) { - utils::AttributesBuilder builder(request_->attributes); + utils::AttributesBuilder builder(attributes_); std::map headers = check_data->GetRequestHeaders(); builder.AddStringMap(utils::AttributeName::kRequestHeaders, headers); @@ -79,7 +80,7 @@ void AttributesBuilder::ExtractRequestHeaderAttributes(CheckData *check_data) { } void AttributesBuilder::ExtractAuthAttributes(CheckData *check_data) { - utils::AttributesBuilder builder(request_->attributes); + utils::AttributesBuilder builder(attributes_); std::string destination_principal; if (check_data->GetPrincipal(false, &destination_principal)) { @@ -146,7 +147,7 @@ void AttributesBuilder::ExtractForwardedAttributes(CheckData *check_data) { }; auto fwd = v2_format.attributes(); - utils::AttributesBuilder builder(request_->attributes); + utils::AttributesBuilder builder(attributes_); for (const auto &attribute : kForwardWhitelist) { const auto &iter = fwd.find(attribute); if (iter != fwd.end() && !iter->second.string_value().empty()) { @@ -161,7 +162,7 @@ void AttributesBuilder::ExtractCheckAttributes(CheckData *check_data) { ExtractRequestHeaderAttributes(check_data); ExtractAuthAttributes(check_data); - utils::AttributesBuilder builder(request_->attributes); + utils::AttributesBuilder builder(attributes_); // connection remote IP is always reported as origin IP std::string source_ip; @@ -200,8 +201,9 @@ void AttributesBuilder::ForwardAttributes(const Attributes &forward_attributes, header_update->AddIstioAttributes(str); } -void AttributesBuilder::ExtractReportAttributes(ReportData *report_data) { - utils::AttributesBuilder builder(request_->attributes); +void AttributesBuilder::ExtractReportAttributes( + const ::google::protobuf::util::Status &status, ReportData *report_data) { + utils::AttributesBuilder builder(attributes_); std::string dest_ip; int dest_port; @@ -238,14 +240,13 @@ void AttributesBuilder::ExtractReportAttributes(ReportData *report_data) { builder.AddInt64(utils::AttributeName::kResponseTotalSize, info.response_total_size); builder.AddDuration(utils::AttributeName::kResponseDuration, info.duration); - if (!request_->check_status.ok()) { - builder.AddInt64( - utils::AttributeName::kResponseCode, - utils::StatusHttpCode(request_->check_status.error_code())); + if (status != ::google::protobuf::util::Status::UNKNOWN && !status.ok()) { + builder.AddInt64(utils::AttributeName::kResponseCode, + utils::StatusHttpCode(status.error_code())); builder.AddInt64(utils::AttributeName::kCheckErrorCode, - request_->check_status.error_code()); + status.error_code()); builder.AddString(utils::AttributeName::kCheckErrorMessage, - request_->check_status.ToString()); + status.ToString()); } else { builder.AddInt64(utils::AttributeName::kResponseCode, info.response_code); } diff --git a/src/istio/control/http/attributes_builder.h b/src/istio/control/http/attributes_builder.h index a8346f7f5ec..0385f152bd7 100644 --- a/src/istio/control/http/attributes_builder.h +++ b/src/istio/control/http/attributes_builder.h @@ -18,7 +18,7 @@ #include "include/istio/control/http/check_data.h" #include "include/istio/control/http/report_data.h" -#include "src/istio/control/request_context.h" +#include "mixer/v1/attributes.pb.h" namespace istio { namespace control { @@ -27,7 +27,8 @@ namespace http { // The context for each HTTP request. class AttributesBuilder { public: - AttributesBuilder(RequestContext* request) : request_(request) {} + AttributesBuilder(istio::mixer::v1::Attributes* attributes) + : attributes_(attributes) {} // Extract forwarded attributes from HTTP header. void ExtractForwardedAttributes(CheckData* check_data); @@ -39,7 +40,8 @@ class AttributesBuilder { // Extract attributes for Check call. void ExtractCheckAttributes(CheckData* check_data); // Extract attributes for Report call. - void ExtractReportAttributes(ReportData* report_data); + void ExtractReportAttributes(const ::google::protobuf::util::Status& status, + ReportData* report_data); private: // Extract HTTP header attributes @@ -52,8 +54,7 @@ class AttributesBuilder { // not available. void ExtractAuthAttributes(CheckData* check_data); - // The request context object. - RequestContext* request_; + istio::mixer::v1::Attributes* attributes_; }; } // namespace http diff --git a/src/istio/control/http/attributes_builder_test.cc b/src/istio/control/http/attributes_builder_test.cc index 76974df802d..09c84bc1567 100644 --- a/src/istio/control/http/attributes_builder_test.cc +++ b/src/istio/control/http/attributes_builder_test.cc @@ -16,6 +16,7 @@ #include "src/istio/control/http/attributes_builder.h" #include "gmock/gmock.h" +#include "google/protobuf/stubs/status.h" #include "google/protobuf/text_format.h" #include "google/protobuf/util/message_differencer.h" #include "gtest/gtest.h" @@ -531,15 +532,17 @@ fields { } )"; -void ClearContextTime(const std::string &name, RequestContext *request) { +void ClearContextTime(const std::string &name, + istio::mixer::v1::Attributes *attributes) { // Override timestamp with - - utils::AttributesBuilder builder(request->attributes); + utils::AttributesBuilder builder(attributes); std::chrono::time_point time0; builder.AddTimestamp(name, time0); } -void SetDestinationIp(RequestContext *request, const std::string &ip) { - utils::AttributesBuilder builder(request->attributes); +void SetDestinationIp(istio::mixer::v1::Attributes *attributes, + const std::string &ip) { + utils::AttributesBuilder builder(attributes); builder.AddBytes(utils::AttributeName::kDestinationIp, ip); } @@ -554,10 +557,10 @@ TEST(AttributesBuilderTest, TestExtractForwardedAttributes) { return true; })); - RequestContext request; - AttributesBuilder builder(&request); + istio::mixer::v1::Attributes attributes; + AttributesBuilder builder(&attributes); builder.ExtractForwardedAttributes(&mock_data); - EXPECT_THAT(*request.attributes, EqualsAttribute(attr)); + EXPECT_THAT(attributes, EqualsAttribute(attr)); } TEST(AttributesBuilderTest, TestForwardAttributes) { @@ -638,16 +641,16 @@ TEST(AttributesBuilderTest, TestCheckAttributesWithoutAuthnFilter) { return true; })); - RequestContext request; - AttributesBuilder builder(&request); + istio::mixer::v1::Attributes attributes; + AttributesBuilder builder(&attributes); builder.ExtractCheckAttributes(&mock_data); - ClearContextTime(utils::AttributeName::kRequestTime, &request); + ClearContextTime(utils::AttributeName::kRequestTime, &attributes); Attributes expected_attributes; ASSERT_TRUE(TextFormat::ParseFromString(kCheckAttributesWithoutAuthnFilter, &expected_attributes)); - EXPECT_THAT(*request.attributes, EqualsAttribute(expected_attributes)); + EXPECT_THAT(attributes, EqualsAttribute(expected_attributes)); } TEST(AttributesBuilderTest, TestCheckAttributes) { @@ -712,16 +715,16 @@ TEST(AttributesBuilderTest, TestCheckAttributes) { return true; })); - RequestContext request; - AttributesBuilder builder(&request); + istio::mixer::v1::Attributes attributes; + AttributesBuilder builder(&attributes); builder.ExtractCheckAttributes(&mock_data); - ClearContextTime(utils::AttributeName::kRequestTime, &request); + ClearContextTime(utils::AttributeName::kRequestTime, &attributes); Attributes expected_attributes; ASSERT_TRUE( TextFormat::ParseFromString(kCheckAttributes, &expected_attributes)); - EXPECT_THAT(*request.attributes, EqualsAttribute(expected_attributes)); + EXPECT_THAT(attributes, EqualsAttribute(expected_attributes)); } TEST(AttributesBuilderTest, TestReportAttributes) { @@ -787,11 +790,12 @@ TEST(AttributesBuilderTest, TestReportAttributes) { EXPECT_CALL(mock_data, GetDynamicFilterState()) .WillOnce(ReturnRef(filter_metadata)); - RequestContext request; - AttributesBuilder builder(&request); - builder.ExtractReportAttributes(&mock_data); + istio::mixer::v1::Attributes attributes; + AttributesBuilder builder(&attributes); + builder.ExtractReportAttributes(::google::protobuf::util::Status::OK, + &mock_data); - ClearContextTime(utils::AttributeName::kResponseTime, &request); + ClearContextTime(utils::AttributeName::kResponseTime, &attributes); Attributes expected_attributes; ASSERT_TRUE( @@ -805,7 +809,7 @@ TEST(AttributesBuilderTest, TestReportAttributes) { (*expected_attributes .mutable_attributes())[utils::AttributeName::kResponseGrpcMessage] .set_string_value("grpc-message"); - EXPECT_THAT(*request.attributes, EqualsAttribute(expected_attributes)); + EXPECT_THAT(attributes, EqualsAttribute(expected_attributes)); } TEST(AttributesBuilderTest, TestReportAttributesWithDestIP) { @@ -861,17 +865,18 @@ TEST(AttributesBuilderTest, TestReportAttributesWithDestIP) { EXPECT_CALL(mock_data, GetDynamicFilterState()) .WillOnce(ReturnRef(filter_metadata)); - RequestContext request; - SetDestinationIp(&request, "1.2.3.4"); - AttributesBuilder builder(&request); - builder.ExtractReportAttributes(&mock_data); + istio::mixer::v1::Attributes attributes; + SetDestinationIp(&attributes, "1.2.3.4"); + AttributesBuilder builder(&attributes); + builder.ExtractReportAttributes(::google::protobuf::util::Status::OK, + &mock_data); - ClearContextTime(utils::AttributeName::kResponseTime, &request); + ClearContextTime(utils::AttributeName::kResponseTime, &attributes); Attributes expected_attributes; ASSERT_TRUE( TextFormat::ParseFromString(kReportAttributes, &expected_attributes)); - EXPECT_THAT(*request.attributes, EqualsAttribute(expected_attributes)); + EXPECT_THAT(attributes, EqualsAttribute(expected_attributes)); } } // namespace diff --git a/src/istio/control/http/request_handler_impl.cc b/src/istio/control/http/request_handler_impl.cc index 56bb7c4ed7a..73cc95dd8aa 100644 --- a/src/istio/control/http/request_handler_impl.cc +++ b/src/istio/control/http/request_handler_impl.cc @@ -29,9 +29,10 @@ namespace http { RequestHandlerImpl::RequestHandlerImpl( std::shared_ptr service_context) - : service_context_(service_context), - check_attributes_added_(false), - forward_attributes_added_(false) {} + : attributes_(new istio::mixerclient::SharedAttributes()), + check_context_(new istio::mixerclient::CheckContext( + service_context->client_context()->NetworkFailOpen(), attributes_)), + service_context_(service_context) {} void RequestHandlerImpl::AddForwardAttributes(CheckData* check_data) { if (forward_attributes_added_) { @@ -39,7 +40,7 @@ void RequestHandlerImpl::AddForwardAttributes(CheckData* check_data) { } forward_attributes_added_ = true; - AttributesBuilder builder(&request_context_); + AttributesBuilder builder(attributes_->attributes()); builder.ExtractForwardedAttributes(check_data); } @@ -51,12 +52,12 @@ void RequestHandlerImpl::AddCheckAttributes(CheckData* check_data) { if (service_context_->enable_mixer_check() || service_context_->enable_mixer_report()) { - service_context_->AddStaticAttributes(&request_context_); + service_context_->AddStaticAttributes(attributes_->attributes()); - AttributesBuilder builder(&request_context_); + AttributesBuilder builder(attributes_->attributes()); builder.ExtractCheckAttributes(check_data); - service_context_->AddApiAttributes(check_data, &request_context_); + service_context_->AddApiAttributes(check_data, attributes_->attributes()); } } @@ -72,16 +73,16 @@ CancelFunc RequestHandlerImpl::Check(CheckData* check_data, service_context_->InjectForwardedAttributes(header_update); if (!service_context_->enable_mixer_check()) { - CheckResponseInfo check_response_info; - check_response_info.response_status = Status::OK; - on_done(check_response_info); + check_context_->setFinalStatus(Status::OK, false); + on_done(*check_context_); return nullptr; } - service_context_->AddQuotas(&request_context_); + service_context_->AddQuotas(attributes_->attributes(), + check_context_->quotaRequirements()); return service_context_->client_context()->SendCheck(transport, on_done, - &request_context_); + check_context_); } // Make remote report call. @@ -94,10 +95,10 @@ void RequestHandlerImpl::Report(CheckData* check_data, AddForwardAttributes(check_data); AddCheckAttributes(check_data); - AttributesBuilder builder(&request_context_); - builder.ExtractReportAttributes(report_data); + AttributesBuilder builder(attributes_->attributes()); + builder.ExtractReportAttributes(check_context_->status(), report_data); - service_context_->client_context()->SendReport(request_context_); + service_context_->client_context()->SendReport(attributes_); } } // namespace http diff --git a/src/istio/control/http/request_handler_impl.h b/src/istio/control/http/request_handler_impl.h index 0e8c2b28072..599f0660ee0 100644 --- a/src/istio/control/http/request_handler_impl.h +++ b/src/istio/control/http/request_handler_impl.h @@ -19,7 +19,7 @@ #include "include/istio/control/http/request_handler.h" #include "src/istio/control/http/client_context.h" #include "src/istio/control/http/service_context.h" -#include "src/istio/control/request_context.h" +#include "src/istio/mixerclient/check_context.h" namespace istio { namespace control { @@ -45,16 +45,16 @@ class RequestHandlerImpl : public RequestHandler { // Add check attributes, allow re-entry void AddCheckAttributes(CheckData* check_data); - // The request context object. - RequestContext request_context_; + // memory for telemetry reports and policy checks. Telemetry only needs the + // shared attributes. + istio::mixerclient::SharedAttributesSharedPtr attributes_; + istio::mixerclient::CheckContextSharedPtr check_context_; // The service context. std::shared_ptr service_context_; - // If true, request attributes are added - bool check_attributes_added_; - // If true, forward attributes are added - bool forward_attributes_added_; + bool check_attributes_added_{false}; + bool forward_attributes_added_{false}; }; } // namespace http diff --git a/src/istio/control/http/request_handler_impl_test.cc b/src/istio/control/http/request_handler_impl_test.cc index 325614e5f1a..d8ea8e30184 100644 --- a/src/istio/control/http/request_handler_impl_test.cc +++ b/src/istio/control/http/request_handler_impl_test.cc @@ -210,7 +210,7 @@ TEST_F(RequestHandlerImplTest, TestHandlerDisabledCheckReport) { EXPECT_CALL(mock_data, GetPrincipal(_, _)).Times(0); // Check should NOT be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)).Times(0); + EXPECT_CALL(*mock_client_, Check(_, _, _)).Times(0); ServiceConfig config; config.set_disable_check_calls(true); @@ -219,10 +219,9 @@ TEST_F(RequestHandlerImplTest, TestHandlerDisabledCheckReport) { ApplyPerRouteConfig(config, &per_route); auto handler = controller_->CreateRequestHandler(per_route); - handler->Check(&mock_data, &mock_header, nullptr, - [](const CheckResponseInfo &info) { - EXPECT_TRUE(info.response_status.ok()); - }); + handler->Check( + &mock_data, &mock_header, nullptr, + [](const CheckResponseInfo &info) { EXPECT_TRUE(info.status().ok()); }); } TEST_F(RequestHandlerImplTest, TestHandlerDisabledCheck) { @@ -233,7 +232,7 @@ TEST_F(RequestHandlerImplTest, TestHandlerDisabledCheck) { EXPECT_CALL(mock_data, GetPrincipal(_, _)).Times(2); // Check should NOT be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)).Times(0); + EXPECT_CALL(*mock_client_, Check(_, _, _)).Times(0); ServiceConfig config; config.set_disable_check_calls(true); @@ -241,10 +240,9 @@ TEST_F(RequestHandlerImplTest, TestHandlerDisabledCheck) { ApplyPerRouteConfig(config, &per_route); auto handler = controller_->CreateRequestHandler(per_route); - handler->Check(&mock_data, &mock_header, nullptr, - [](const CheckResponseInfo &info) { - EXPECT_TRUE(info.response_status.ok()); - }); + handler->Check( + &mock_data, &mock_header, nullptr, + [](const CheckResponseInfo &info) { EXPECT_TRUE(info.status().ok()); }); } TEST_F(RequestHandlerImplTest, TestPerRouteAttributes) { @@ -254,12 +252,11 @@ TEST_F(RequestHandlerImplTest, TestPerRouteAttributes) { EXPECT_CALL(mock_data, GetPrincipal(_, _)).Times(2); // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map["global-key"].string_value(), "global-value"); EXPECT_EQ(map["per-route-key"].string_value(), "per-route-value"); return nullptr; @@ -282,12 +279,11 @@ TEST_F(RequestHandlerImplTest, TestDefaultRouteAttributes) { EXPECT_CALL(mock_data, GetPrincipal(_, _)).Times(2); // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map["global-key"].string_value(), "global-value"); EXPECT_EQ(map["route0-key"].string_value(), "route0-value"); return nullptr; @@ -321,12 +317,11 @@ TEST_F(RequestHandlerImplTest, TestRouteAttributes) { SetServiceConfig("route1", route_config); // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map["global-key"].string_value(), "service-value"); EXPECT_EQ(map["route1-key"].string_value(), "route1-value"); return nullptr; @@ -352,16 +347,15 @@ TEST_F(RequestHandlerImplTest, TestPerRouteQuota) { ::testing::NiceMock mock_header; // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map["global-key"].string_value(), "global-value"); - EXPECT_EQ(quotas.size(), 1); - EXPECT_EQ(quotas[0].quota, "route0-quota"); - EXPECT_EQ(quotas[0].charge, 10); + EXPECT_EQ(context->quotaRequirements().size(), 1); + EXPECT_EQ(context->quotaRequirements()[0].quota, "route0-quota"); + EXPECT_EQ(context->quotaRequirements()[0].charge, 10); return nullptr; })); @@ -394,12 +388,11 @@ TEST_F(RequestHandlerImplTest, TestPerRouteApiSpec) { })); // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map["global-key"].string_value(), "global-value"); EXPECT_EQ(map["api.name"].string_value(), "test-name"); EXPECT_EQ(map["api.operation"].string_value(), "test-method"); @@ -430,7 +423,7 @@ TEST_F(RequestHandlerImplTest, TestHandlerCheck) { EXPECT_CALL(mock_data, GetPrincipal(_, _)).Times(2); // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)).Times(1); + EXPECT_CALL(*mock_client_, Check(_, _, _)).Times(1); ServiceConfig config; Controller::PerRouteConfig per_route; @@ -454,12 +447,11 @@ TEST_F(RequestHandlerImplTest, TestDefaultApiKey) { })); // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map[utils::AttributeName::kRequestApiKey].string_value(), "test-api-key"); return nullptr; @@ -533,7 +525,7 @@ TEST_F(RequestHandlerImplTest, TestEmptyConfig) { })); // Check should NOT be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)).Times(0); + EXPECT_CALL(*mock_client_, Check(_, _, _)).Times(0); ::testing::NiceMock mock_report; EXPECT_CALL(mock_report, GetResponseHeaders()).Times(0); @@ -545,10 +537,9 @@ TEST_F(RequestHandlerImplTest, TestEmptyConfig) { Controller::PerRouteConfig config; auto handler = controller_->CreateRequestHandler(config); - handler->Check(&mock_check, &mock_header, nullptr, - [](const CheckResponseInfo &info) { - EXPECT_TRUE(info.response_status.ok()); - }); + handler->Check( + &mock_check, &mock_header, nullptr, + [](const CheckResponseInfo &info) { EXPECT_TRUE(info.status().ok()); }); handler->Report(&mock_check, &mock_report); } @@ -556,12 +547,11 @@ TEST_F(OutboundRequestHandlerImplTest, TestLocalAttributes) { ::testing::NiceMock mock_data; ::testing::NiceMock mock_header; // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map["source.uid"].string_value(), "kubernetes://src-client-84469dc8d7-jbbxt.default"); return nullptr; @@ -590,12 +580,11 @@ TEST_F(OutboundRequestHandlerImplTest, TestLocalAttributesOverride) { })); // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map["source.uid"].string_value(), "fwded"); EXPECT_NE(map["destination.uid"].string_value(), "ignored"); return nullptr; diff --git a/src/istio/control/http/service_context.cc b/src/istio/control/http/service_context.cc index dc1332db698..3882dffaa6a 100644 --- a/src/istio/control/http/service_context.cc +++ b/src/istio/control/http/service_context.cc @@ -51,15 +51,15 @@ void ServiceContext::BuildParsers() { } // Add static mixer attributes. -void ServiceContext::AddStaticAttributes(RequestContext *request) const { - client_context_->AddLocalNodeAttributes(request->attributes); +void ServiceContext::AddStaticAttributes( + ::istio::mixer::v1::Attributes *attributes) const { + client_context_->AddLocalNodeAttributes(attributes); if (client_context_->config().has_mixer_attributes()) { - request->attributes->MergeFrom( - client_context_->config().mixer_attributes()); + attributes->MergeFrom(client_context_->config().mixer_attributes()); } if (service_config_ && service_config_->has_mixer_attributes()) { - request->attributes->MergeFrom(service_config_->mixer_attributes()); + attributes->MergeFrom(service_config_->mixer_attributes()); } } @@ -82,8 +82,8 @@ void ServiceContext::InjectForwardedAttributes( } } -void ServiceContext::AddApiAttributes(CheckData *check_data, - RequestContext *request) const { +void ServiceContext::AddApiAttributes( + CheckData *check_data, ::istio::mixer::v1::Attributes *attributes) const { if (!api_spec_parser_) { return; } @@ -91,21 +91,22 @@ void ServiceContext::AddApiAttributes(CheckData *check_data, std::string path; if (check_data->FindHeaderByType(CheckData::HEADER_METHOD, &http_method) && check_data->FindHeaderByType(CheckData::HEADER_PATH, &path)) { - api_spec_parser_->AddAttributes(http_method, path, request->attributes); + api_spec_parser_->AddAttributes(http_method, path, attributes); } std::string api_key; if (api_spec_parser_->ExtractApiKey(check_data, &api_key)) { - (*request->attributes - ->mutable_attributes())[utils::AttributeName::kRequestApiKey] + (*attributes->mutable_attributes())[utils::AttributeName::kRequestApiKey] .set_string_value(api_key); } } // Add quota requirements from quota configs. -void ServiceContext::AddQuotas(RequestContext *request) const { +void ServiceContext::AddQuotas( + ::istio::mixer::v1::Attributes *attributes, + std::vector<::istio::quota_config::Requirement> "as) const { for (const auto &parser : quota_parsers_) { - parser->GetRequirements(*request->attributes, &request->quotas); + parser->GetRequirements(*attributes, "as); } } diff --git a/src/istio/control/http/service_context.h b/src/istio/control/http/service_context.h index ee446fe2f46..78e9b52f7d9 100644 --- a/src/istio/control/http/service_context.h +++ b/src/istio/control/http/service_context.h @@ -38,16 +38,18 @@ class ServiceContext { } // Add static mixer attributes. - void AddStaticAttributes(RequestContext* request) const; + void AddStaticAttributes(::istio::mixer::v1::Attributes* attributes) const; // Inject a header that contains the static forwarded attributes. void InjectForwardedAttributes(HeaderUpdate* header_update) const; // Add api attributes from api_spec. - void AddApiAttributes(CheckData* check_data, RequestContext* request) const; + void AddApiAttributes(CheckData* check_data, + ::istio::mixer::v1::Attributes* attributes) const; // Add quota requirements from quota configs. - void AddQuotas(RequestContext* request) const; + void AddQuotas(::istio::mixer::v1::Attributes* attributes, + std::vector<::istio::quota_config::Requirement>& quotas) const; bool enable_mixer_check() const { return service_config_ && !service_config_->disable_check_calls(); diff --git a/src/istio/control/mock_mixer_client.h b/src/istio/control/mock_mixer_client.h index b7367271370..e94277490ba 100644 --- a/src/istio/control/mock_mixer_client.h +++ b/src/istio/control/mock_mixer_client.h @@ -25,13 +25,13 @@ namespace control { // The mock object for MixerClient interface. class MockMixerClient : public ::istio::mixerclient::MixerClient { public: - MOCK_METHOD4( - Check, ::istio::mixerclient::CancelFunc( - const ::istio::mixer::v1::Attributes& attributes, - const std::vector<::istio::quota_config::Requirement>& quotas, - ::istio::mixerclient::TransportCheckFunc transport, - ::istio::mixerclient::CheckDoneFunc on_done)); - MOCK_METHOD1(Report, void(const ::istio::mixer::v1::Attributes& attributes)); + MOCK_METHOD3(Check, ::istio::mixerclient::CancelFunc( + ::istio::mixerclient::CheckContextSharedPtr& context, + ::istio::mixerclient::TransportCheckFunc transport, + ::istio::mixerclient::CheckDoneFunc on_done)); + MOCK_METHOD1( + Report, + void(const istio::mixerclient::SharedAttributesSharedPtr& attributes)); MOCK_CONST_METHOD1(GetStatistics, void(::istio::mixerclient::Statistics* stat)); }; diff --git a/src/istio/control/request_context.h b/src/istio/control/request_context.h deleted file mode 100644 index 5e655abbba0..00000000000 --- a/src/istio/control/request_context.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2017 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ISTIO_CONTROL_REQUEST_CONTEXT_H -#define ISTIO_CONTROL_REQUEST_CONTEXT_H - -#include "google/protobuf/arena.h" -#include "google/protobuf/stubs/status.h" -#include "include/istio/quota_config/requirement.h" -#include "mixer/v1/attributes.pb.h" - -#include - -namespace istio { -namespace control { - -// The context to hold request data for both HTTP and TCP. -struct RequestContext { - RequestContext() { - attributes = - google::protobuf::Arena::CreateMessage<::istio::mixer::v1::Attributes>( - &arena_); - } - // protobuf arena - google::protobuf::Arena arena_; - // The attributes for both Check and Report. - ::istio::mixer::v1::Attributes* attributes; - // The quota requirements - std::vector<::istio::quota_config::Requirement> quotas; - // The check status. - ::google::protobuf::util::Status check_status; -}; - -} // namespace control -} // namespace istio - -#endif // ISTIO_CONTROL_REQUEST_CONTEXT_H diff --git a/src/istio/control/tcp/attributes_builder.cc b/src/istio/control/tcp/attributes_builder.cc index 0c8876e7923..782d0bdf6d1 100644 --- a/src/istio/control/tcp/attributes_builder.cc +++ b/src/istio/control/tcp/attributes_builder.cc @@ -14,10 +14,10 @@ */ #include "src/istio/control/tcp/attributes_builder.h" -#include "src/istio/utils/utils.h" - +#include "google/protobuf/stubs/status.h" #include "include/istio/utils/attribute_names.h" #include "include/istio/utils/attributes_builder.h" +#include "src/istio/utils/utils.h" namespace istio { namespace control { @@ -30,7 +30,7 @@ const std::string kConnectionClose("close"); } // namespace void AttributesBuilder::ExtractCheckAttributes(CheckData *check_data) { - utils::AttributesBuilder builder(request_->attributes); + utils::AttributesBuilder builder(attributes_); std::string source_ip; int source_port; @@ -81,9 +81,10 @@ void AttributesBuilder::ExtractCheckAttributes(CheckData *check_data) { } void AttributesBuilder::ExtractReportAttributes( - ReportData *report_data, ReportData::ConnectionEvent event, + const ::google::protobuf::util::Status &status, ReportData *report_data, + ReportData::ConnectionEvent event, ReportData::ReportInfo *last_report_info) { - utils::AttributesBuilder builder(request_->attributes); + utils::AttributesBuilder builder(attributes_); ReportData::ReportInfo info; report_data->GetReportInfo(&info); @@ -99,11 +100,11 @@ void AttributesBuilder::ExtractReportAttributes( if (event == ReportData::ConnectionEvent::CLOSE) { builder.AddDuration(utils::AttributeName::kConnectionDuration, info.duration); - if (!request_->check_status.ok()) { + if (status != ::google::protobuf::util::Status::UNKNOWN && !status.ok()) { builder.AddInt64(utils::AttributeName::kCheckErrorCode, - request_->check_status.error_code()); + status.error_code()); builder.AddString(utils::AttributeName::kCheckErrorMessage, - request_->check_status.ToString()); + status.ToString()); } builder.AddString(utils::AttributeName::kConnectionEvent, kConnectionClose); } else if (event == ReportData::ConnectionEvent::OPEN) { diff --git a/src/istio/control/tcp/attributes_builder.h b/src/istio/control/tcp/attributes_builder.h index 9b0f8004230..e69a7498a6d 100644 --- a/src/istio/control/tcp/attributes_builder.h +++ b/src/istio/control/tcp/attributes_builder.h @@ -18,7 +18,7 @@ #include "include/istio/control/tcp/check_data.h" #include "include/istio/control/tcp/report_data.h" -#include "src/istio/control/request_context.h" +#include "mixer/v1/attributes.pb.h" namespace istio { namespace control { @@ -27,18 +27,19 @@ namespace tcp { // The builder class to add TCP attributes. class AttributesBuilder { public: - AttributesBuilder(RequestContext* request) : request_(request) {} + AttributesBuilder(istio::mixer::v1::Attributes* attributes) + : attributes_(attributes) {} // Extract attributes for Check. void ExtractCheckAttributes(CheckData* check_data); // Extract attributes for Report. - void ExtractReportAttributes(ReportData* report_data, + void ExtractReportAttributes(const ::google::protobuf::util::Status& status, + ReportData* report_data, ReportData::ConnectionEvent event, ReportData::ReportInfo* last_report_info); private: - // The request context object. - RequestContext* request_; + istio::mixer::v1::Attributes* attributes_; }; } // namespace tcp diff --git a/src/istio/control/tcp/attributes_builder_test.cc b/src/istio/control/tcp/attributes_builder_test.cc index eeb58fd2432..5c9c4ab9119 100644 --- a/src/istio/control/tcp/attributes_builder_test.cc +++ b/src/istio/control/tcp/attributes_builder_test.cc @@ -14,7 +14,7 @@ */ #include "src/istio/control/tcp/attributes_builder.h" - +#include "google/protobuf/stubs/status.h" #include "google/protobuf/text_format.h" #include "google/protobuf/util/message_differencer.h" #include "gtest/gtest.h" @@ -419,9 +419,9 @@ attributes { } )"; -void ClearContextTime(RequestContext *request) { +void ClearContextTime(istio::mixer::v1::Attributes *attributes) { // Override timestamp with - - utils::AttributesBuilder builder(request->attributes); + utils::AttributesBuilder builder(attributes); std::chrono::time_point time0; builder.AddTimestamp(utils::AttributeName::kContextTime, time0); } @@ -452,21 +452,20 @@ TEST(AttributesBuilderTest, TestCheckAttributes) { *name = "www.google.com"; return true; })); - RequestContext request; - AttributesBuilder builder(&request); + istio::mixer::v1::Attributes attributes; + AttributesBuilder builder(&attributes); builder.ExtractCheckAttributes(&mock_data); - ClearContextTime(&request); + ClearContextTime(&attributes); std::string out_str; - TextFormat::PrintToString(*request.attributes, &out_str); + TextFormat::PrintToString(attributes, &out_str); GOOGLE_LOG(INFO) << "===" << out_str << "==="; ::istio::mixer::v1::Attributes expected_attributes; ASSERT_TRUE( TextFormat::ParseFromString(kCheckAttributes, &expected_attributes)); - EXPECT_TRUE( - MessageDifferencer::Equals(*request.attributes, expected_attributes)); + EXPECT_TRUE(MessageDifferencer::Equals(attributes, expected_attributes)); } TEST(AttributesBuilderTest, TestReportAttributes) { @@ -528,77 +527,80 @@ TEST(AttributesBuilderTest, TestReportAttributes) { info->duration = std::chrono::nanoseconds(4); })); - RequestContext request; - request.check_status = ::google::protobuf::util::Status( + istio::mixer::v1::Attributes attributes; + AttributesBuilder builder(&attributes); + auto check_status = ::google::protobuf::util::Status( ::google::protobuf::util::error::INVALID_ARGUMENT, "Invalid argument"); - AttributesBuilder builder(&request); ReportData::ReportInfo last_report_info{0ULL, 0ULL, std::chrono::nanoseconds::zero()}; // Verify first open report - builder.ExtractReportAttributes(&mock_data, ReportData::ConnectionEvent::OPEN, + builder.ExtractReportAttributes(check_status, &mock_data, + ReportData::ConnectionEvent::OPEN, &last_report_info); - ClearContextTime(&request); + ClearContextTime(&attributes); std::string out_str; - TextFormat::PrintToString(*request.attributes, &out_str); + TextFormat::PrintToString(attributes, &out_str); GOOGLE_LOG(INFO) << "===" << out_str << "==="; ::istio::mixer::v1::Attributes expected_open_attributes; ASSERT_TRUE(TextFormat::ParseFromString(kFirstReportAttributes, &expected_open_attributes)); - EXPECT_TRUE(MessageDifferencer::Equals(*request.attributes, - expected_open_attributes)); + EXPECT_TRUE(MessageDifferencer::Equals(attributes, expected_open_attributes)); EXPECT_EQ(0, last_report_info.received_bytes); EXPECT_EQ(0, last_report_info.send_bytes); // Verify delta one report - builder.ExtractReportAttributes( - &mock_data, ReportData::ConnectionEvent::CONTINUE, &last_report_info); - ClearContextTime(&request); + builder.ExtractReportAttributes(check_status, &mock_data, + ReportData::ConnectionEvent::CONTINUE, + &last_report_info); + ClearContextTime(&attributes); - TextFormat::PrintToString(*request.attributes, &out_str); + TextFormat::PrintToString(attributes, &out_str); GOOGLE_LOG(INFO) << "===" << out_str << "==="; ::istio::mixer::v1::Attributes expected_delta_attributes; ASSERT_TRUE(TextFormat::ParseFromString(kDeltaOneReportAttributes, &expected_delta_attributes)); - EXPECT_TRUE(MessageDifferencer::Equals(*request.attributes, - expected_delta_attributes)); + EXPECT_TRUE( + MessageDifferencer::Equals(attributes, expected_delta_attributes)); EXPECT_EQ(100, last_report_info.received_bytes); EXPECT_EQ(200, last_report_info.send_bytes); // Verify delta two report - builder.ExtractReportAttributes( - &mock_data, ReportData::ConnectionEvent::CONTINUE, &last_report_info); - ClearContextTime(&request); + builder.ExtractReportAttributes(check_status, &mock_data, + ReportData::ConnectionEvent::CONTINUE, + &last_report_info); + ClearContextTime(&attributes); out_str.clear(); - TextFormat::PrintToString(*request.attributes, &out_str); + TextFormat::PrintToString(attributes, &out_str); GOOGLE_LOG(INFO) << "===" << out_str << "==="; expected_delta_attributes.Clear(); ASSERT_TRUE(TextFormat::ParseFromString(kDeltaTwoReportAttributes, &expected_delta_attributes)); - EXPECT_TRUE(MessageDifferencer::Equals(*request.attributes, - expected_delta_attributes)); + EXPECT_TRUE( + MessageDifferencer::Equals(attributes, expected_delta_attributes)); EXPECT_EQ(201, last_report_info.received_bytes); EXPECT_EQ(404, last_report_info.send_bytes); // Verify final report - builder.ExtractReportAttributes( - &mock_data, ReportData::ConnectionEvent::CLOSE, &last_report_info); - ClearContextTime(&request); + builder.ExtractReportAttributes(check_status, &mock_data, + ReportData::ConnectionEvent::CLOSE, + &last_report_info); + ClearContextTime(&attributes); out_str.clear(); - TextFormat::PrintToString(*request.attributes, &out_str); + TextFormat::PrintToString(attributes, &out_str); GOOGLE_LOG(INFO) << "===" << out_str << "==="; ::istio::mixer::v1::Attributes expected_final_attributes; ASSERT_TRUE(TextFormat::ParseFromString(kReportAttributes, &expected_final_attributes)); - EXPECT_TRUE(MessageDifferencer::Equals(*request.attributes, - expected_final_attributes)); + EXPECT_TRUE( + MessageDifferencer::Equals(attributes, expected_final_attributes)); } } // namespace diff --git a/src/istio/control/tcp/client_context.h b/src/istio/control/tcp/client_context.h index 38072694aed..ea20dc64317 100644 --- a/src/istio/control/tcp/client_context.h +++ b/src/istio/control/tcp/client_context.h @@ -20,7 +20,6 @@ #include "include/istio/quota_config/config_parser.h" #include "include/istio/utils/local_attributes.h" #include "src/istio/control/client_context_base.h" -#include "src/istio/control/request_context.h" namespace istio { namespace control { @@ -51,18 +50,20 @@ class ClientContext : public ClientContextBase { } // Add static mixer attributes. - void AddStaticAttributes(RequestContext* request) const { - AddLocalNodeAttributes(request->attributes); + void AddStaticAttributes(::istio::mixer::v1::Attributes* attributes) const { + AddLocalNodeAttributes(attributes); if (config_.has_mixer_attributes()) { - request->attributes->MergeFrom(config_.mixer_attributes()); + attributes->MergeFrom(config_.mixer_attributes()); } } // Add quota requirements from quota configs. - void AddQuotas(RequestContext* request) const { + void AddQuotas( + ::istio::mixer::v1::Attributes* attributes, + std::vector<::istio::quota_config::Requirement>& quotas) const { if (quota_parser_) { - quota_parser_->GetRequirements(*request->attributes, &request->quotas); + quota_parser_->GetRequirements(*attributes, "as); } } diff --git a/src/istio/control/tcp/request_handler_impl.cc b/src/istio/control/tcp/request_handler_impl.cc index f9b7b25d59c..eb74be8ccce 100644 --- a/src/istio/control/tcp/request_handler_impl.cc +++ b/src/istio/control/tcp/request_handler_impl.cc @@ -28,29 +28,32 @@ namespace tcp { RequestHandlerImpl::RequestHandlerImpl( std::shared_ptr client_context) - : client_context_(client_context), + : attributes_(new istio::mixerclient::SharedAttributes()), + check_context_(new istio::mixerclient::CheckContext( + client_context->NetworkFailOpen(), attributes_)), + client_context_(client_context), last_report_info_{0ULL, 0ULL, std::chrono::nanoseconds::zero()} {} CancelFunc RequestHandlerImpl::Check(CheckData* check_data, CheckDoneFunc on_done) { if (client_context_->enable_mixer_check() || client_context_->enable_mixer_report()) { - client_context_->AddStaticAttributes(&request_context_); + client_context_->AddStaticAttributes(attributes_->attributes()); - AttributesBuilder builder(&request_context_); + AttributesBuilder builder(attributes_->attributes()); builder.ExtractCheckAttributes(check_data); } if (!client_context_->enable_mixer_check()) { - CheckResponseInfo check_response_info; - check_response_info.response_status = Status::OK; - on_done(check_response_info); + check_context_->setFinalStatus(Status::OK, false); + on_done(*check_context_); return nullptr; } - client_context_->AddQuotas(&request_context_); + client_context_->AddQuotas(attributes_->attributes(), + check_context_->quotaRequirements()); - return client_context_->SendCheck(nullptr, on_done, &request_context_); + return client_context_->SendCheck(nullptr, on_done, check_context_); } void RequestHandlerImpl::Report(ReportData* report_data, @@ -59,10 +62,11 @@ void RequestHandlerImpl::Report(ReportData* report_data, return; } - AttributesBuilder builder(&request_context_); - builder.ExtractReportAttributes(report_data, event, &last_report_info_); + AttributesBuilder builder(attributes_->attributes()); + builder.ExtractReportAttributes(check_context_->status(), report_data, event, + &last_report_info_); - client_context_->SendReport(request_context_); + client_context_->SendReport(attributes_); } } // namespace tcp diff --git a/src/istio/control/tcp/request_handler_impl.h b/src/istio/control/tcp/request_handler_impl.h index ba7f890bd2c..57d72623d9e 100644 --- a/src/istio/control/tcp/request_handler_impl.h +++ b/src/istio/control/tcp/request_handler_impl.h @@ -17,8 +17,8 @@ #define ISTIO_CONTROL_TCP_REQUEST_HANDLER_IMPL_H #include "include/istio/control/tcp/request_handler.h" -#include "src/istio/control/request_context.h" #include "src/istio/control/tcp/client_context.h" +#include "src/istio/mixerclient/check_context.h" namespace istio { namespace control { @@ -39,8 +39,10 @@ class RequestHandlerImpl : public RequestHandler { ReportData::ConnectionEvent event) override; private: - // The request context object. - RequestContext request_context_; + // memory for telemetry reports and policy checks. Telemetry only needs the + // shared attributes. + istio::mixerclient::SharedAttributesSharedPtr attributes_; + istio::mixerclient::CheckContextSharedPtr check_context_; // The client context object. std::shared_ptr client_context_; diff --git a/src/istio/control/tcp/request_handler_impl_test.cc b/src/istio/control/tcp/request_handler_impl_test.cc index c27763b3bc4..40916270c91 100644 --- a/src/istio/control/tcp/request_handler_impl_test.cc +++ b/src/istio/control/tcp/request_handler_impl_test.cc @@ -108,12 +108,12 @@ TEST_F(RequestHandlerImplTest, TestHandlerDisabledCheck) { EXPECT_CALL(mock_data, GetPrincipal(_, _)).Times(2); // Check should not be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)).Times(0); + EXPECT_CALL(*mock_client_, Check(_, _, _)).Times(0); client_config_.set_disable_check_calls(true); auto handler = controller_->CreateRequestHandler(); handler->Check(&mock_data, [](const CheckResponseInfo &info) { - EXPECT_TRUE(info.response_status.ok()); + EXPECT_TRUE(info.status().ok()); }); } @@ -123,16 +123,15 @@ TEST_F(RequestHandlerImplTest, TestHandlerCheck) { EXPECT_CALL(mock_data, GetPrincipal(_, _)).Times(2); // Check should be called. - EXPECT_CALL(*mock_client_, Check(_, _, _, _)) - .WillOnce(Invoke([](const Attributes &attributes, - const std::vector "as, + EXPECT_CALL(*mock_client_, Check(_, _, _)) + .WillOnce(Invoke([](istio::mixerclient::CheckContextSharedPtr &context, TransportCheckFunc transport, CheckDoneFunc on_done) -> CancelFunc { - auto map = attributes.attributes(); + auto map = context->attributes()->attributes(); EXPECT_EQ(map["key1"].string_value(), "value1"); - EXPECT_EQ(quotas.size(), 1); - EXPECT_EQ(quotas[0].quota, "quota"); - EXPECT_EQ(quotas[0].charge, 5); + EXPECT_EQ(context->quotaRequirements().size(), 1); + EXPECT_EQ(context->quotaRequirements()[0].quota, "quota"); + EXPECT_EQ(context->quotaRequirements()[0].charge, 5); return nullptr; })); diff --git a/src/istio/mixerclient/BUILD b/src/istio/mixerclient/BUILD index 6d5d8010b38..129ff01116f 100644 --- a/src/istio/mixerclient/BUILD +++ b/src/istio/mixerclient/BUILD @@ -40,6 +40,7 @@ cc_library( "attribute_compressor.h", "check_cache.cc", "check_cache.h", + "check_context.h", "client_impl.cc", "client_impl.h", "global_dictionary.cc", @@ -50,6 +51,7 @@ cc_library( "referenced.h", "report_batch.cc", "report_batch.h", + "shared_attributes.h", ], visibility = ["//visibility:public"], deps = [ diff --git a/src/istio/mixerclient/check_cache.h b/src/istio/mixerclient/check_cache.h index 9e2d6d3fc2d..ad14f85eddf 100644 --- a/src/istio/mixerclient/check_cache.h +++ b/src/istio/mixerclient/check_cache.h @@ -25,7 +25,6 @@ #include #include "google/protobuf/stubs/status.h" -#include "include/istio/mixerclient/client.h" #include "include/istio/mixerclient/options.h" #include "include/istio/utils/simple_lru_cache.h" #include "include/istio/utils/simple_lru_cache_inl.h" @@ -54,9 +53,9 @@ class CheckCache { bool IsCacheHit() const; - ::google::protobuf::util::Status status() const { return status_; } + const ::google::protobuf::util::Status& status() const { return status_; } - ::istio::mixer::v1::RouteDirective route_directive() const { + const ::istio::mixer::v1::RouteDirective& route_directive() const { return route_directive_; } @@ -148,7 +147,7 @@ class CheckCache { std::chrono::time_point expire_time_; // if -1, not to check use_count. // if 0, cache item should not be used. - // use_cound is decreased by 1 for each request, + // use_count is decreased by 1 for each request, int use_count_; }; diff --git a/src/istio/mixerclient/check_context.h b/src/istio/mixerclient/check_context.h new file mode 100644 index 00000000000..0abff0d2d8e --- /dev/null +++ b/src/istio/mixerclient/check_context.h @@ -0,0 +1,194 @@ +/* Copyright 2019 Istio Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "google/protobuf/arena.h" +#include "google/protobuf/stubs/status.h" +#include "include/istio/mixerclient/check_response.h" +#include "include/istio/quota_config/requirement.h" +#include "include/istio/utils/attribute_names.h" +#include "include/istio/utils/attributes_builder.h" +#include "mixer/v1/attributes.pb.h" +#include "mixer/v1/mixer.pb.h" +#include "src/istio/mixerclient/attribute_compressor.h" +#include "src/istio/mixerclient/check_cache.h" +#include "src/istio/mixerclient/quota_cache.h" +#include "src/istio/mixerclient/shared_attributes.h" + +#include + +namespace istio { +namespace mixerclient { + +/** + * All memory for the upstream policy and quota checks should hang off of these + * objects. + */ +class CheckContext : public CheckResponseInfo { + public: + CheckContext(bool fail_open, SharedAttributesSharedPtr& shared_attributes) + : shared_attributes_(shared_attributes), fail_open_(fail_open) {} + + const istio::mixer::v1::Attributes* attributes() const { + return shared_attributes_->attributes(); + } + + const std::vector& quotaRequirements() + const { + return quota_requirements_; + } + std::vector& quotaRequirements() { + return quota_requirements_; + } + + // + // Policy Cache Checks + // + + bool policyCacheHit() const { return policy_cache_hit_; } + const google::protobuf::util::Status& policyStatus() const { + return policy_cache_result_.status(); + } + + void checkPolicyCache(CheckCache& policyCache) { + policyCache.Check(*shared_attributes_->attributes(), &policy_cache_result_); + policy_cache_hit_ = policy_cache_result_.IsCacheHit(); + } + + void updatePolicyCache(const google::protobuf::util::Status& status, + const istio::mixer::v1::CheckResponse& response) { + policy_cache_result_.SetResponse(status, *shared_attributes_->attributes(), + response); + } + + // + // Quota Cache Checks + // + + bool quotaCheckRequired() const { return !quota_requirements_.empty(); } + bool remoteQuotaRequestRequired() const { + return remote_quota_check_required_; + } + + void checkQuotaCache(QuotaCache& quotaCache) { + if (!quotaCheckRequired()) { + return; + } + + // + // Quota is removed from the quota cache iff there is a policy cache hit. If + // there is a policy cache miss, then a request has to be sent upstream + // anyways, so the quota will be decremented on the upstream response. + // + quotaCache.Check(*shared_attributes_->attributes(), quota_requirements_, + policyCacheHit(), "a_cache_result_); + + remote_quota_check_required_ = + quota_cache_result_.BuildRequest(allocRequestOnce()); + + quota_cache_hit_ = quota_cache_result_.IsCacheHit(); + } + + void updateQuotaCache(const google::protobuf::util::Status& status, + const istio::mixer::v1::CheckResponse& response) { + quota_cache_result_.SetResponse(status, *shared_attributes_->attributes(), + response); + } + + bool quotaCacheHit() const { return quota_cache_hit_; } + const google::protobuf::util::Status& quotaStatus() const { + return quota_cache_result_.status(); + } + + // + // Upstream request and response + // + + void compressRequest(const AttributeCompressor& compressor, + const std::string& deduplication_id) { + compressor.Compress(*shared_attributes_->attributes(), + allocRequestOnce()->mutable_attributes()); + request_->set_global_word_count(compressor.global_word_count()); + request_->set_deduplication_id(deduplication_id); + } + + bool networkFailOpen() const { return fail_open_; } + + const istio::mixer::v1::CheckRequest& request() { return *request_; } + + istio::mixer::v1::CheckResponse* response() { + if (!response_) { + response_ = google::protobuf::Arena::CreateMessage< + istio::mixer::v1::CheckResponse>(&shared_attributes_->arena()); + } + return response_; + } + + void setFinalStatus(const google::protobuf::util::Status& status, + bool add_report_attributes = true) { + if (add_report_attributes) { + utils::AttributesBuilder builder(shared_attributes_->attributes()); + builder.AddBool(utils::AttributeName::kCheckCacheHit, policy_cache_hit_); + builder.AddBool(utils::AttributeName::kQuotaCacheHit, quota_cache_hit_); + } + + final_status_ = status; + } + + // + // CheckResponseInfo (exposed to the top-level filter) + // + + const google::protobuf::util::Status& status() const override { + return final_status_; + } + + const istio::mixer::v1::RouteDirective& routeDirective() const override { + return policy_cache_result_.route_directive(); + } + + private: + istio::mixer::v1::CheckRequest* allocRequestOnce() { + if (!request_) { + request_ = google::protobuf::Arena::CreateMessage< + istio::mixer::v1::CheckRequest>(&shared_attributes_->arena()); + } + + return request_; + } + + istio::mixerclient::SharedAttributesSharedPtr shared_attributes_; + std::vector quota_requirements_; + + bool quota_cache_hit_{false}; + bool policy_cache_hit_{false}; + + QuotaCache::CheckResult quota_cache_result_; + CheckCache::CheckResult policy_cache_result_; + + istio::mixer::v1::CheckRequest* request_{nullptr}; + istio::mixer::v1::CheckResponse* response_{nullptr}; + + bool fail_open_{false}; + bool remote_quota_check_required_{false}; + google::protobuf::util::Status final_status_{ + google::protobuf::util::Status::UNKNOWN}; +}; + +typedef std::shared_ptr CheckContextSharedPtr; + +} // namespace mixerclient +} // namespace istio diff --git a/src/istio/mixerclient/client_impl.cc b/src/istio/mixerclient/client_impl.cc index 2a563039f41..f4498ac7a8e 100644 --- a/src/istio/mixerclient/client_impl.cc +++ b/src/istio/mixerclient/client_impl.cc @@ -52,109 +52,121 @@ MixerClientImpl::MixerClientImpl(const MixerClientOptions &options) MixerClientImpl::~MixerClientImpl() {} -CancelFunc MixerClientImpl::Check( - const Attributes &attributes, - const std::vector<::istio::quota_config::Requirement> "as, - TransportCheckFunc transport, CheckDoneFunc on_done) { +CancelFunc MixerClientImpl::Check(CheckContextSharedPtr &context, + TransportCheckFunc transport, + CheckDoneFunc on_done) { + // + // Always check the policy cache + // + + context->checkPolicyCache(*check_cache_); ++total_check_calls_; - std::unique_ptr check_result( - new CheckCache::CheckResult); - check_cache_->Check(attributes, check_result.get()); - - CheckResponseInfo check_response_info; - check_response_info.is_check_cache_hit = check_result->IsCacheHit(); - check_response_info.response_status = check_result->status(); - check_response_info.route_directive = check_result->route_directive(); - - if (check_result->IsCacheHit() && !check_result->status().ok()) { - on_done(check_response_info); + if (context->policyCacheHit() && + (!context->policyStatus().ok() || !context->quotaCheckRequired())) { + // + // If the policy cache denies the request, immediately fail the request + // + // If policy cache accepts the request and a quota check is not required, + // immediately accept the request. + // + context->setFinalStatus(context->policyStatus()); + on_done(*context); return nullptr; } - if (!quotas.empty()) { + if (context->quotaCheckRequired()) { + context->checkQuotaCache(*quota_cache_); ++total_quota_calls_; - } - std::unique_ptr quota_result( - new QuotaCache::CheckResult); - // Only use quota cache if Check is using cache with OK status. - // Otherwise, a remote Check call may be rejected, but quota amounts were - // substracted from quota cache already. - quota_cache_->Check(attributes, quotas, check_result->IsCacheHit(), - quota_result.get()); - - auto arena = new google::protobuf::Arena; - CheckRequest *request = - google::protobuf::Arena::CreateMessage(arena); - bool quota_call = quota_result->BuildRequest(request); - check_response_info.is_quota_cache_hit = quota_result->IsCacheHit(); - check_response_info.response_status = quota_result->status(); - if (check_result->IsCacheHit() && quota_result->IsCacheHit()) { - on_done(check_response_info); - on_done = nullptr; - if (!quota_call) { - delete arena; - return nullptr; + + if (context->quotaCacheHit() && context->policyCacheHit()) { + // + // If both policy and quota caches are hit, we can call the completion + // handler now. However sometimes the quota cache's prefetch + // implementation will still need to send a request to the Mixer server + // in the background. + // + context->setFinalStatus(context->quotaStatus()); + on_done(*context); + on_done = nullptr; + if (!context->remoteQuotaRequestRequired()) { + return nullptr; + } } } - compressor_.Compress(attributes, request->mutable_attributes()); - request->set_global_word_count(compressor_.global_word_count()); - request->set_deduplication_id(deduplication_id_base_ + - std::to_string(deduplication_id_.fetch_add(1))); - - // Need to make a copy for processing the response for check cache. - Attributes *attributes_copy = - google::protobuf::Arena::CreateMessage(arena); - CheckResponse *response = - google::protobuf::Arena::CreateMessage(arena); - *attributes_copy = attributes; - // Lambda capture could not pass unique_ptr, use raw pointer. - CheckCache::CheckResult *raw_check_result = check_result.release(); - QuotaCache::CheckResult *raw_quota_result = quota_result.release(); + // TODO(jblatt) mjog thinks this is a big CPU hog. Look into it. + context->compressRequest( + compressor_, + deduplication_id_base_ + std::to_string(deduplication_id_.fetch_add(1))); + if (!transport) { transport = options_.env.check_transport; } + + // // We are going to make a remote call now. + // + ++total_remote_check_calls_; - if (!quotas.empty()) { + + if (context->quotaCheckRequired()) { ++total_remote_quota_calls_; } + if (on_done) { ++total_blocking_remote_check_calls_; - if (!quotas.empty()) { + if (context->quotaCheckRequired()) { ++total_blocking_remote_quota_calls_; } } - return transport( - *request, response, - [this, attributes_copy, response, raw_check_result, raw_quota_result, - on_done, arena](const Status &status) { - raw_check_result->SetResponse(status, *attributes_copy, *response); - raw_quota_result->SetResponse(status, *attributes_copy, *response); - CheckResponseInfo check_response_info; - if (on_done) { - if (!raw_check_result->status().ok()) { - check_response_info.response_status = raw_check_result->status(); - } else { - check_response_info.response_status = raw_quota_result->status(); - } - check_response_info.route_directive = - raw_check_result->route_directive(); - on_done(check_response_info); - } - delete raw_check_result; - delete raw_quota_result; - delete arena; - - if (utils::InvalidDictionaryStatus(status)) { - compressor_.ShrinkGlobalDictionary(); - } - }); + return transport(context->request(), context->response(), + [this, context, on_done](const Status &status) { + // + // Update caches. This has the side-effect of updating + // status, so track those too + // + + if (!context->policyCacheHit()) { + context->updatePolicyCache(status, *context->response()); + } + + if (context->quotaCheckRequired()) { + context->updateQuotaCache(status, *context->response()); + } + + // + // Determine final status for Filter::completeCheck(). This + // will send an error response to the downstream client if + // the final status is not Status::OK + // + + if (!status.ok()) { + if (context->networkFailOpen()) { + context->setFinalStatus(Status::OK); + } else { + context->setFinalStatus(status); + } + } else if (!context->quotaCheckRequired()) { + context->setFinalStatus(context->policyStatus()); + } else if (!context->policyStatus().ok()) { + context->setFinalStatus(context->policyStatus()); + } else { + context->setFinalStatus(context->quotaStatus()); + } + + if (on_done) { + on_done(*context); + } + + if (utils::InvalidDictionaryStatus(status)) { + compressor_.ShrinkGlobalDictionary(); + } + }); } -void MixerClientImpl::Report(const Attributes &attributes) { +void MixerClientImpl::Report(const SharedAttributesSharedPtr &attributes) { report_batch_->Report(attributes); } diff --git a/src/istio/mixerclient/client_impl.h b/src/istio/mixerclient/client_impl.h index d23ff15842e..3cc6d2892f1 100644 --- a/src/istio/mixerclient/client_impl.h +++ b/src/istio/mixerclient/client_impl.h @@ -35,11 +35,10 @@ class MixerClientImpl : public MixerClient { // Destructor virtual ~MixerClientImpl(); - CancelFunc Check( - const ::istio::mixer::v1::Attributes& attributes, - const std::vector<::istio::quota_config::Requirement>& quotas, - TransportCheckFunc transport, CheckDoneFunc on_done) override; - void Report(const ::istio::mixer::v1::Attributes& attributes) override; + CancelFunc Check(istio::mixerclient::CheckContextSharedPtr& context, + TransportCheckFunc transport, + CheckDoneFunc on_done) override; + void Report(const SharedAttributesSharedPtr& attributes) override; void GetStatistics(Statistics* stat) const override; diff --git a/src/istio/mixerclient/client_impl_test.cc b/src/istio/mixerclient/client_impl_test.cc index ee38af82753..f984c17b32f 100644 --- a/src/istio/mixerclient/client_impl_test.cc +++ b/src/istio/mixerclient/client_impl_test.cc @@ -25,6 +25,7 @@ using ::google::protobuf::util::error::Code; using ::istio::mixer::v1::Attributes; using ::istio::mixer::v1::CheckRequest; using ::istio::mixer::v1::CheckResponse; +using ::istio::mixerclient::CheckContextSharedPtr; using ::istio::mixerclient::CheckResponseInfo; using ::istio::quota_config::Requirement; using ::testing::_; @@ -52,10 +53,10 @@ class MockCheckTransport { class MixerClientImplTest : public ::testing::Test { public: MixerClientImplTest() { - quotas_.push_back({kRequestCount, 1}); CreateClient(true /* check_cache */, true /* quota_cache */); } + protected: void CreateClient(bool check_cache, bool quota_cache) { MixerClientOptions options(CheckOptions(check_cache ? 1 : 0 /*entries */), ReportOptions(1, 1000), @@ -66,8 +67,18 @@ class MixerClientImplTest : public ::testing::Test { client_ = CreateMixerClient(options); } - Attributes request_; - std::vector quotas_; + CheckContextSharedPtr CreateContext(int quota_request) { + bool fail_open{false}; + istio::mixerclient::SharedAttributesSharedPtr attributes{ + new SharedAttributes()}; + istio::mixerclient::CheckContextSharedPtr context{ + new CheckContext(fail_open, attributes)}; + if (0 < quota_request) { + context->quotaRequirements().push_back({kRequestCount, quota_request}); + } + return context; + } + std::unique_ptr client_; MockCheckTransport mock_check_transport_; TransportCheckFunc empty_transport_; @@ -81,24 +92,22 @@ TEST_F(MixerClientImplTest, TestSuccessCheck) { on_done(Status::OK); })); - // Not to test quota - std::vector empty_quotas; - CheckResponseInfo check_response_info; - client_->Check(request_, empty_quotas, empty_transport_, - [&check_response_info](const CheckResponseInfo& info) { - check_response_info.response_status = info.response_status; - }); - EXPECT_TRUE(check_response_info.response_status.ok()); + { + CheckContextSharedPtr context = CreateContext(0); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); + } - for (int i = 0; i < 10; i++) { - // Other calls should be cached. - CheckResponseInfo check_response_info1; - client_->Check(request_, empty_quotas, empty_transport_, - [&check_response_info1](const CheckResponseInfo& info) { - check_response_info1.response_status = - info.response_status; - }); - EXPECT_TRUE(check_response_info1.response_status.ok()); + for (size_t i = 0; i < 10; i++) { + CheckContextSharedPtr context = CreateContext(0); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); } Statistics stat; @@ -126,24 +135,22 @@ TEST_F(MixerClientImplTest, TestPerRequestTransport) { on_done(Status::OK); })); - // Not to test quota - std::vector empty_quotas; - CheckResponseInfo check_response_info; - client_->Check(request_, empty_quotas, local_check_transport.GetFunc(), - [&check_response_info](const CheckResponseInfo& info) { - check_response_info.response_status = info.response_status; - }); - EXPECT_TRUE(check_response_info.response_status.ok()); + { + CheckContextSharedPtr context = CreateContext(0); + Status status; + client_->Check( + context, local_check_transport.GetFunc(), + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); + } - for (int i = 0; i < 10; i++) { - // Other calls should be cached. - CheckResponseInfo check_response_info1; - client_->Check(request_, empty_quotas, local_check_transport.GetFunc(), - [&check_response_info1](const CheckResponseInfo& info) { - check_response_info1.response_status = - info.response_status; - }); - EXPECT_TRUE(check_response_info1.response_status.ok()); + for (size_t i = 0; i < 10; i++) { + CheckContextSharedPtr context = CreateContext(0); + Status status; + client_->Check( + context, local_check_transport.GetFunc(), + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); } Statistics stat; @@ -174,22 +181,23 @@ TEST_F(MixerClientImplTest, TestNoCheckCache) { on_done(Status::OK); })); - CheckResponseInfo check_response_info; - client_->Check(request_, quotas_, empty_transport_, - [&check_response_info](const CheckResponseInfo& info) { - check_response_info.response_status = info.response_status; - }); - EXPECT_TRUE(check_response_info.response_status.ok()); + { + CheckContextSharedPtr context = CreateContext(1); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); + } - for (int i = 0; i < 10; i++) { + for (size_t i = 0; i < 10; i++) { // Other calls are not cached. - CheckResponseInfo check_response_info1; - client_->Check(request_, quotas_, empty_transport_, - [&check_response_info1](const CheckResponseInfo& info) { - check_response_info1.response_status = - info.response_status; - }); - EXPECT_TRUE(check_response_info1.response_status.ok()); + CheckContextSharedPtr context = CreateContext(1); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); } // Call count 11 since check is not cached. EXPECT_EQ(call_counts, 11); @@ -211,31 +219,34 @@ TEST_F(MixerClientImplTest, TestNoQuotaCache) { EXPECT_CALL(mock_check_transport_, Check(_, _, _)) .WillRepeatedly(Invoke([&](const CheckRequest& request, CheckResponse* response, DoneFunc on_done) { + auto request_quotas = request.quotas(); + auto requested_amount = request_quotas[kRequestCount].amount(); response->mutable_precondition()->set_valid_use_count(1000); CheckResponse::QuotaResult quota_result; - quota_result.set_granted_amount(10); + quota_result.set_granted_amount(requested_amount); quota_result.mutable_valid_duration()->set_seconds(10); (*response->mutable_quotas())[kRequestCount] = quota_result; call_counts++; on_done(Status::OK); })); - CheckResponseInfo check_response_info; - client_->Check(request_, quotas_, empty_transport_, - [&check_response_info](const CheckResponseInfo& info) { - check_response_info.response_status = info.response_status; - }); - EXPECT_TRUE(check_response_info.response_status.ok()); + { + CheckContextSharedPtr context = CreateContext(1); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); + } - for (int i = 0; i < 10; i++) { + for (size_t i = 0; i < 10; i++) { // Other calls should be cached. - CheckResponseInfo check_response_info1; - client_->Check(request_, quotas_, empty_transport_, - [&check_response_info1](const CheckResponseInfo& info) { - check_response_info1.response_status = - info.response_status; - }); - EXPECT_TRUE(check_response_info1.response_status.ok()); + CheckContextSharedPtr context = CreateContext(1); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); } // Call count 11 since quota is not cached. EXPECT_EQ(call_counts, 11); @@ -255,43 +266,54 @@ TEST_F(MixerClientImplTest, TestSuccessCheckAndQuota) { EXPECT_CALL(mock_check_transport_, Check(_, _, _)) .WillRepeatedly(Invoke([&](const CheckRequest& request, CheckResponse* response, DoneFunc on_done) { + auto request_quotas = request.quotas(); + auto requested_amount = request_quotas[kRequestCount].amount(); response->mutable_precondition()->set_valid_use_count(1000); CheckResponse::QuotaResult quota_result; - quota_result.set_granted_amount(10); + quota_result.set_granted_amount(requested_amount); quota_result.mutable_valid_duration()->set_seconds(10); (*response->mutable_quotas())[kRequestCount] = quota_result; call_counts++; on_done(Status::OK); })); - CheckResponseInfo check_response_info; - client_->Check(request_, quotas_, empty_transport_, - [&check_response_info](const CheckResponseInfo& info) { - check_response_info.response_status = info.response_status; - }); - EXPECT_TRUE(check_response_info.response_status.ok()); + // quota cache starts with 1 resource. by requesting exactly 1 the request + // will be satisfied by the cache and a background request will be initiated + // to store 2 more in the cache + { + CheckContextSharedPtr context = CreateContext(1); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); + } - for (int i = 0; i < 10; i++) { - // Other calls should be cached. - CheckResponseInfo check_response_info1; - client_->Check(request_, quotas_, empty_transport_, - [&check_response_info1](const CheckResponseInfo& info) { - check_response_info1.response_status = - info.response_status; - }); - EXPECT_TRUE(check_response_info1.response_status.ok()); + // Half of the requests from now on will be satisfied by the cache but require + // background refills. + for (size_t i = 0; i < 100; i++) { + CheckContextSharedPtr context = CreateContext(1); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_TRUE(status.ok()); } - // Call count should be less than 4 - EXPECT_LE(call_counts, 3); + + // The number of remote prefetch calls should be less than or equal to the + // current prefetch impl's value of 6. Decreases are of course acceptable, + // but increases should be allowed only with a good reason. + int expected_prefetchs = 6; + + EXPECT_EQ(call_counts, 1 + expected_prefetchs); Statistics stat; client_->GetStatistics(&stat); - // Less than 4 remote calls are made for prefetching, and they are - // non-blocking remote calls. - EXPECT_EQ(stat.total_check_calls, 11); - EXPECT_LE(stat.total_remote_check_calls, 3); + + EXPECT_EQ(stat.total_check_calls, 101); + EXPECT_EQ(stat.total_remote_check_calls, 1 + expected_prefetchs); EXPECT_EQ(stat.total_blocking_remote_check_calls, 1); - EXPECT_EQ(stat.total_quota_calls, 11); - EXPECT_LE(stat.total_remote_quota_calls, 3); + EXPECT_EQ(stat.total_quota_calls, 101); + EXPECT_EQ(stat.total_remote_quota_calls, 1 + expected_prefetchs); EXPECT_EQ(stat.total_blocking_remote_quota_calls, 1); } @@ -309,24 +331,23 @@ TEST_F(MixerClientImplTest, TestFailedCheckAndQuota) { on_done(Status::OK); })); - CheckResponseInfo check_response_info; - client_->Check(request_, quotas_, empty_transport_, - [&check_response_info](const CheckResponseInfo& info) { - check_response_info.response_status = info.response_status; - }); - EXPECT_ERROR_CODE(Code::FAILED_PRECONDITION, - check_response_info.response_status); + { + CheckContextSharedPtr context = CreateContext(1); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_ERROR_CODE(Code::FAILED_PRECONDITION, status); + } - for (int i = 0; i < 10; i++) { + for (size_t i = 0; i < 10; i++) { // Other calls should be cached. - CheckResponseInfo check_response_info1; - client_->Check(request_, quotas_, empty_transport_, - [&check_response_info1](const CheckResponseInfo& info) { - check_response_info1.response_status = - info.response_status; - }); - EXPECT_ERROR_CODE(Code::FAILED_PRECONDITION, - check_response_info1.response_status); + CheckContextSharedPtr context = CreateContext(1); + Status status; + client_->Check( + context, empty_transport_, + [&status](const CheckResponseInfo& info) { status = info.status(); }); + EXPECT_ERROR_CODE(Code::FAILED_PRECONDITION, status); } Statistics stat; client_->GetStatistics(&stat); diff --git a/src/istio/mixerclient/quota_cache.h b/src/istio/mixerclient/quota_cache.h index 231e4324795..a667b9b089a 100644 --- a/src/istio/mixerclient/quota_cache.h +++ b/src/istio/mixerclient/quota_cache.h @@ -22,8 +22,10 @@ #include #include -#include "include/istio/mixerclient/client.h" +#include "google/protobuf/stubs/status.h" +#include "include/istio/mixerclient/options.h" #include "include/istio/prefetch/quota_prefetch.h" +#include "include/istio/quota_config/requirement.h" #include "include/istio/utils/simple_lru_cache.h" #include "include/istio/utils/simple_lru_cache_inl.h" #include "src/istio/mixerclient/referenced.h" @@ -57,7 +59,7 @@ class QuotaCache { bool IsCacheHit() const; - ::google::protobuf::util::Status status() const { return status_; } + const ::google::protobuf::util::Status& status() const { return status_; } void SetResponse(const ::google::protobuf::util::Status& status, const ::istio::mixer::v1::Attributes& attributes, diff --git a/src/istio/mixerclient/report_batch.cc b/src/istio/mixerclient/report_batch.cc index 9906eba3883..73fc421a2ba 100644 --- a/src/istio/mixerclient/report_batch.cc +++ b/src/istio/mixerclient/report_batch.cc @@ -43,10 +43,11 @@ ReportBatch::ReportBatch(const ReportOptions& options, ReportBatch::~ReportBatch() { Flush(); } -void ReportBatch::Report(const Attributes& request) { +void ReportBatch::Report( + const istio::mixerclient::SharedAttributesSharedPtr& attributes) { std::lock_guard lock(mutex_); ++total_report_calls_; - batch_compressor_->Add(request); + batch_compressor_->Add(*attributes->attributes()); if (batch_compressor_->size() >= options_.max_batch_entries) { FlushWithLock(); } else { diff --git a/src/istio/mixerclient/report_batch.h b/src/istio/mixerclient/report_batch.h index 156129ee00a..60dfe1de22d 100644 --- a/src/istio/mixerclient/report_batch.h +++ b/src/istio/mixerclient/report_batch.h @@ -34,7 +34,7 @@ class ReportBatch { virtual ~ReportBatch(); // Make batched report call. - void Report(const ::istio::mixer::v1::Attributes& request); + void Report(const istio::mixerclient::SharedAttributesSharedPtr& attributes); // Flush out batched reports. void Flush(); diff --git a/src/istio/mixerclient/report_batch_test.cc b/src/istio/mixerclient/report_batch_test.cc index 06cc7156ded..59e2a917e8e 100644 --- a/src/istio/mixerclient/report_batch_test.cc +++ b/src/istio/mixerclient/report_batch_test.cc @@ -83,7 +83,8 @@ TEST_F(ReportBatchTest, TestBatchDisabled) { Invoke([](const ReportRequest& request, ReportResponse* response, DoneFunc on_done) { on_done(Status::OK); })); - Attributes report; + istio::mixerclient::SharedAttributesSharedPtr report{ + new istio::mixerclient::SharedAttributes()}; batch_->Report(report); } @@ -96,7 +97,8 @@ TEST_F(ReportBatchTest, TestBatchReport) { on_done(Status::OK); })); - Attributes report; + istio::mixerclient::SharedAttributesSharedPtr report{ + new istio::mixerclient::SharedAttributes()}; for (int i = 0; i < 10; ++i) { batch_->Report(report); } @@ -115,7 +117,8 @@ TEST_F(ReportBatchTest, TestBatchReportWithTimeout) { on_done(Status::OK); })); - Attributes report; + istio::mixerclient::SharedAttributesSharedPtr report{ + new istio::mixerclient::SharedAttributes()}; batch_->Report(report); EXPECT_EQ(report_call_count, 0); diff --git a/src/istio/mixerclient/shared_attributes.h b/src/istio/mixerclient/shared_attributes.h new file mode 100644 index 00000000000..418c1fabc22 --- /dev/null +++ b/src/istio/mixerclient/shared_attributes.h @@ -0,0 +1,49 @@ +/* Copyright 2019 Istio Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "google/protobuf/arena.h" +#include "mixer/v1/attributes.pb.h" + +namespace istio { +namespace mixerclient { + +/** + * Attributes shared by the policy/quota check requests and telemetry requests + * sent to the Mixer server. + */ +class SharedAttributes { + public: + SharedAttributes() + : attributes_(google::protobuf::Arena::CreateMessage< + ::istio::mixer::v1::Attributes>(&arena_)) {} + + const ::istio::mixer::v1::Attributes* attributes() const { + return attributes_; + } + ::istio::mixer::v1::Attributes* attributes() { return attributes_; } + + google::protobuf::Arena& arena() { return arena_; } + + private: + google::protobuf::Arena arena_; + ::istio::mixer::v1::Attributes* attributes_; +}; + +typedef std::shared_ptr SharedAttributesSharedPtr; + +} // namespace mixerclient +} // namespace istio