From 38ad97e8093767f1a1236ff0d6f1e048eb10f11f Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Tue, 21 Feb 2017 18:35:15 -0800 Subject: [PATCH 1/6] Added test cases for rate limiting --- WORKSPACE | 2 +- src/nginx/t/BUILD | 3 + src/nginx/t/quota.t | 275 ++++++++++++++++++++++++++ src/nginx/t/quota_api_not_available.t | 181 +++++++++++++++++ src/nginx/t/quota_exhausted.t | 211 ++++++++++++++++++++ 5 files changed, 671 insertions(+), 1 deletion(-) create mode 100644 src/nginx/t/quota.t create mode 100644 src/nginx/t/quota_api_not_available.t create mode 100644 src/nginx/t/quota_exhausted.t diff --git a/WORKSPACE b/WORKSPACE index a2798b7f0..b9d9f1f72 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -26,7 +26,7 @@ # # A Bazel (http://bazel.io) workspace for the Google Cloud Endpoints runtime. -ISTIO_PROXY = "6e372fc510de08da504c2dd21060c09255c1aee7" +ISTIO_PROXY = "5b25d57a71c5835d265e724e0ca39b5d73a97df6" git_repository( name = "nginx", diff --git a/src/nginx/t/BUILD b/src/nginx/t/BUILD index b829dffd4..4d3297dd0 100644 --- a/src/nginx/t/BUILD +++ b/src/nginx/t/BUILD @@ -187,6 +187,9 @@ nginx_suite( "multiple_apis.t", "no_backend.t", "no_service_control.t", + "quota.t", + "quota_api_not_available.t", + "quota_exhausted.t", "reject_unrecognized.t", "report_3xx.t", "report_4xx.t", diff --git a/src/nginx/t/quota.t b/src/nginx/t/quota.t new file mode 100644 index 000000000..e4d180ca8 --- /dev/null +++ b/src/nginx/t/quota.t @@ -0,0 +1,275 @@ +# Copyright (C) Extensible Service Proxy Authors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +################################################################################ +# +use strict; +use warnings; + +use JSON::PP; +use Data::Dumper; + +################################################################################ + +use src::nginx::t::ApiManager + ; # Must be first (sets up import path to the Nginx test module) +use src::nginx::t::HttpServer; +use src::nginx::t::ServiceControl; +use Test::Nginx; # Imports Nginx's test module +use Test::More; # And the test framework + +################################################################################ + +# Port assignments +my $NginxPort = ApiManager::pick_port(); +my $BackendPort = ApiManager::pick_port(); +my $ServiceControlPort = ApiManager::pick_port(); + +my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(22); + +# Save service name in the service configuration protocol buffer file. + +$t->write_file( 'service.pb.txt', + ApiManager::get_bookstore_service_config . <<"EOF"); +control { + environment: "http://127.0.0.1:${ServiceControlPort}" +} +quota { + metric_rules [ + { + selector: "ListShelves" + metric_costs: [ + { + key: "metrics_first" + value: 2 + }, + { + key: "metrics_second" + value: 1 + } + ] + } + ] +} +EOF + +ApiManager::write_file_expand( $t, 'nginx.conf', <<"EOF"); +%%TEST_GLOBALS%% +daemon off; +events { + worker_connections 32; +} +http { + %%TEST_GLOBALS_HTTP%% + server_tokens off; + server { + listen 127.0.0.1:${NginxPort}; + server_name localhost; + location / { + endpoints { + api service.pb.txt; + %%TEST_CONFIG%% + on; + } + proxy_pass http://127.0.0.1:${BackendPort}; + } + } +} +EOF + +$t->run_daemon( \&bookstore, $t, $BackendPort, 'bookstore.log' ); +$t->run_daemon( \&servicecontrol, $t, $ServiceControlPort, + 'servicecontrol.log' ); +is( $t->waitforsocket("127.0.0.1:${BackendPort}"), + 1, 'Bookstore socket ready.' ); +is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), + 1, 'Service control socket ready.' ); +$t->run(); + +################################################################################ + +my $response = + ApiManager::http_get( $NginxPort, '/shelves?key=this-is-an-api-key' ); + +$t->stop_daemons(); + +my ( $response_headers, $response_body ) = split /\r\n\r\n/, $response, 2; + +like( $response_headers, qr/HTTP\/1\.1 200 OK/, 'Returned HTTP 200.' ); +is( $response_body, <<'EOF', 'Shelves returned in the response body.' ); +{ "shelves": [ + { "name": "shelves/1", "theme": "Fiction" }, + { "name": "shelves/2", "theme": "Fantasy" } + ] +} +EOF + +my @requests = ApiManager::read_http_stream( $t, 'bookstore.log' ); +is( scalar @requests, 1, 'Backend received one request' ); + +my $r = shift @requests; + +is( $r->{verb}, 'GET', 'Backend request was a get' ); +is( $r->{uri}, '/shelves?key=this-is-an-api-key', 'Backend uri was /shelves' ); +is( $r->{headers}->{host}, "127.0.0.1:${BackendPort}", 'Host header was set' ); + +@requests = ApiManager::read_http_stream( $t, 'servicecontrol.log' ); +is( scalar @requests, 2, 'Service control received two requests' ); + +$r = shift @requests; +is( $r->{verb}, 'POST', ':check verb was post' ); +is( + $r->{uri}, + '/v1/services/endpoints-test.cloudendpointsapis.com:check', + ':check was called' +); +is( + $r->{headers}->{host}, + "127.0.0.1:${ServiceControlPort}", + 'Host header was set' +); +is( $r->{headers}->{'content-type'}, + 'application/x-protobuf', ':check Content-Type was protocol buffer' ); + +# test allocateQuota request was requested +$r = shift @requests; +is( $r->{verb}, 'POST', ':allocateQuota verb was post' ); +is( + $r->{uri}, + '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', + ':allocateQuota was called' +); +is( + $r->{headers}->{host}, + "127.0.0.1:${ServiceControlPort}", + 'Host header was set' +); +is( $r->{headers}->{'content-type'}, + 'application/x-protobuf', ':check Content-Type was protocol buffer' ); + +my $allocate_quota_request = + decode_json( + ServiceControl::convert_proto( $r->{body}, 'quota_request', 'json' ) ); + +my @quotaMetrics = + @{ $allocate_quota_request->{allocateOperation}->{quotaMetrics} }; + +is( @quotaMetrics, 2, "Quota metrics should have two elements" ); + +if ( $quotaMetrics[0]->{metricName} eq "metrics_first" ) { + is( $quotaMetrics[0]->{metricName}, + "metrics_first", "Quota metric name is 'metrics_first'" ); + is( $quotaMetrics[0]->{metricValues}[0]->{int64Value}, + 2, "Quota metric value is 2" ); + is( $quotaMetrics[1]->{metricName}, + "metrics_second", "Quota metric name is 'metrics_second'" ); + is( $quotaMetrics[1]->{metricValues}[0]->{int64Value}, + 1, "Quota metric value is 1" ); +} +else { + is( $quotaMetrics[0]->{metricName}, + "metrics_second", "Quota metric name is 'metrics_second'" ); + is( $quotaMetrics[0]->{metricValues}[0]->{int64Value}, + 1, "Quota metric value is 1" ); + is( $quotaMetrics[1]->{metricName}, + "metrics_first", "Quota metric name is 'metrics_first'" ); + is( $quotaMetrics[1]->{metricValues}[0]->{int64Value}, + 2, "Quota metric value is 2" ); +} + +################################################################################ + +sub bookstore { + my ( $t, $port, $file ) = @_; + my $server = HttpServer->new( $port, $t->testdir() . '/' . $file ) + or die "Can't create test server socket: $!\n"; + local $SIG{PIPE} = 'IGNORE'; + + $server->on( 'GET', '/shelves?key=this-is-an-api-key', <<'EOF'); +HTTP/1.1 200 OK +Connection: close + +{ "shelves": [ + { "name": "shelves/1", "theme": "Fiction" }, + { "name": "shelves/2", "theme": "Fantasy" } + ] +} +EOF + $server->run(); +} + +my @quota_responses = (); +my $quota_response_index = 0; + +sub servicecontrol { + my ( $t, $port, $file ) = @_; + my $server = HttpServer->new( $port, $t->testdir() . '/' . $file ) + or die "Can't create test server socket: $!\n"; + local $SIG{PIPE} = 'IGNORE'; + + my $quota_response_success = sprintf(<<'EOF'); +HTTP/1.1 200 OK +Connection: close + +EOF + + my $quota_response_success = + ServiceControl::convert_proto( <<'EOF', 'quota_response', 'binary' ); +operation_id: "a20ab01c-415e-46fb-953c-61c6768248ba" +quota_metrics { + metric_name: "serviceruntime.googleapis.com/api/consumer/quota_used_count" + metric_values { + labels { + key: "/quota_name" + value: "metrics_first" + } + int64_value: 2 + } + metric_values { + labels { + key: "/quota_name" + value: "metrics_second" + } + int64_value: 1 + } +} +EOF + + $server->on( 'POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:check', <<'EOF'); +HTTP/1.1 200 OK +Connection: close + +EOF + + $server->on( + 'POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', + $quota_response_success . $quota_response_success + ); + + $server->run(); +} + +################################################################################ diff --git a/src/nginx/t/quota_api_not_available.t b/src/nginx/t/quota_api_not_available.t new file mode 100644 index 000000000..3a1fccac2 --- /dev/null +++ b/src/nginx/t/quota_api_not_available.t @@ -0,0 +1,181 @@ +# Copyright (C) Extensible Service Proxy Authors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +################################################################################ +# +use strict; +use warnings; + +use JSON::PP; +use Data::Dumper; + +################################################################################ + +use src::nginx::t::ApiManager; # Must be first (sets up import path to the Nginx test module) +use src::nginx::t::HttpServer; +use src::nginx::t::ServiceControl; +use Test::Nginx; # Imports Nginx's test module +use Test::More; # And the test framework + +################################################################################ + +# Port assignments +my $NginxPort = ApiManager::pick_port(); +my $BackendPort = ApiManager::pick_port(); +my $ServiceControlPort = ApiManager::pick_port(); + +my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(9); + +# Save service name in the service configuration protocol buffer file. + +$t->write_file( 'service.pb.txt', + ApiManager::get_bookstore_service_config . <<"EOF"); +control { + environment: "http://127.0.0.1:${ServiceControlPort}" +} +quota { + metric_rules [ + { + selector: "ListShelves" + metric_costs: [ + { + key: "metrics_first" + value: 2 + }, + { + key: "metrics_second" + value: 1 + } + ] + } + ] +} +EOF + +ApiManager::write_file_expand( $t, 'nginx.conf', <<"EOF"); +%%TEST_GLOBALS%% +daemon off; +events { + worker_connections 32; +} +http { + %%TEST_GLOBALS_HTTP%% + server_tokens off; + server { + listen 127.0.0.1:${NginxPort}; + server_name localhost; + location / { + endpoints { + api service.pb.txt; + %%TEST_CONFIG%% + on; + } + proxy_pass http://127.0.0.1:${BackendPort}; + } + } +} +EOF + +$t->run_daemon( \&bookstore, $t, $BackendPort, 'bookstore.log' ); +$t->run_daemon( \&servicecontrol, $t, $ServiceControlPort, 'servicecontrol.log' ); +is( $t->waitforsocket("127.0.0.1:${BackendPort}"), 1, 'Bookstore socket ready.' ); +is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), 1, 'Service control socket ready.' ); +$t->run(); + +################################################################################ + +my $response = ApiManager::http_get( $NginxPort, '/shelves?key=this-is-an-api-key' ); + +$t->stop_daemons(); + +my ( $response_headers, $response_body ) = split /\r\n\r\n/, $response, 2; + +like( $response_headers, qr/HTTP\/1\.1 200 OK/, 'Returned HTTP 200.' ); +is( $response_body, <<'EOF', 'Shelves returned in the response body.' ); +{ "shelves": [ + { "name": "shelves/1", "theme": "Fiction" }, + { "name": "shelves/2", "theme": "Fantasy" } + ] +} +EOF + +my @requests = ApiManager::read_http_stream( $t, 'bookstore.log' ); +is( scalar @requests, 1, 'Backend received empty request' ); + +my $r = shift @requests; + +is( $r->{verb}, 'GET', 'Backend request was a get' ); +is( $r->{uri}, '/shelves?key=this-is-an-api-key', 'Backend uri was /shelves' ); +is( $r->{headers}->{host}, "127.0.0.1:${BackendPort}", 'Host header was set' ); + +@requests = ApiManager::read_http_stream( $t, 'servicecontrol.log' ); +is( scalar @requests, 2, 'Service control received two requests' ); + +################################################################################ + +sub bookstore { + my ( $t, $port, $file ) = @_; + my $server = HttpServer->new( $port, $t->testdir() . '/' . $file ) + or die "Can't create test server socket: $!\n"; + local $SIG{PIPE} = 'IGNORE'; + + $server->on( 'GET', '/shelves?key=this-is-an-api-key', <<'EOF'); +HTTP/1.1 200 OK +Connection: close + +{ "shelves": [ + { "name": "shelves/1", "theme": "Fiction" }, + { "name": "shelves/2", "theme": "Fantasy" } + ] +} +EOF + $server->run(); +} + +my @quota_responses = (); +my $quota_response_index = 0; + +sub servicecontrol { + my ( $t, $port, $file ) = @_; + my $server = HttpServer->new( $port, $t->testdir() . '/' . $file ) + or die "Can't create test server socket: $!\n"; + local $SIG{PIPE} = 'IGNORE'; + + $server->on( 'POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:check', <<'EOF'); +HTTP/1.1 200 OK +Connection: close + +EOF + + $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', <<'EOF'); +HTTP/1.1 404 Not Found +Connection: close + +EOF + + $server->run(); +} + +################################################################################ diff --git a/src/nginx/t/quota_exhausted.t b/src/nginx/t/quota_exhausted.t new file mode 100644 index 000000000..ee569b3d1 --- /dev/null +++ b/src/nginx/t/quota_exhausted.t @@ -0,0 +1,211 @@ +# Copyright (C) Extensible Service Proxy Authors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +################################################################################ +# +use strict; +use warnings; + +use JSON::PP; +use Data::Dumper; + +################################################################################ + +use src::nginx::t::ApiManager; # Must be first (sets up import path to the Nginx test module) +use src::nginx::t::HttpServer; +use src::nginx::t::ServiceControl; +use Test::Nginx; # Imports Nginx's test module +use Test::More; # And the test framework + +################################################################################ + +# Port assignments +my $NginxPort = ApiManager::pick_port(); +my $BackendPort = ApiManager::pick_port(); +my $ServiceControlPort = ApiManager::pick_port(); + +my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(5); + +# Save service name in the service configuration protocol buffer file. + +$t->write_file( 'service.pb.txt', + ApiManager::get_bookstore_service_config . <<"EOF"); +control { + environment: "http://127.0.0.1:${ServiceControlPort}" +} +quota { + metric_rules [ + { + selector: "ListShelves" + metric_costs: [ + { + key: "metrics_first" + value: 2 + }, + { + key: "metrics_second" + value: 1 + } + ] + } + ] +} +EOF + +ApiManager::write_file_expand( $t, 'nginx.conf', <<"EOF"); +%%TEST_GLOBALS%% +daemon off; +events { + worker_connections 32; +} +http { + %%TEST_GLOBALS_HTTP%% + server_tokens off; + server { + listen 127.0.0.1:${NginxPort}; + server_name localhost; + location / { + endpoints { + api service.pb.txt; + %%TEST_CONFIG%% + on; + } + proxy_pass http://127.0.0.1:${BackendPort}; + } + } +} +EOF + +$t->run_daemon( \&bookstore, $t, $BackendPort, 'bookstore.log' ); +$t->run_daemon( \&servicecontrol, $t, $ServiceControlPort, + 'servicecontrol.log' ); +is( $t->waitforsocket("127.0.0.1:${BackendPort}"), + 1, 'Bookstore socket ready.' ); +is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), + 1, 'Service control socket ready.' ); +$t->run(); + +################################################################################ + +my $response = + ApiManager::http_get( $NginxPort, '/shelves?key=this-is-an-api-key' ); + +$t->stop_daemons(); + +my ( $response_headers, $response_body ) = split /\r\n\r\n/, $response, 2; + +like( $response_headers, qr/HTTP\/1\.1 403 Forbidden/, 'Returned HTTP 403 Forbidden.' ); +is( $response_body, <<'EOF', 'Shelves returned in the response body.' ); +{ + "code": 7, + "message": "Quota allocation failed.", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.DebugInfo", + "stackEntries": [], + "detail": "internal" + } + ] +} +EOF + +my @requests = ApiManager::read_http_stream( $t, 'bookstore.log' ); +is( scalar @requests, 0, 'Backend received empty request' ); + +################################################################################ + +sub bookstore { + my ( $t, $port, $file ) = @_; + my $server = HttpServer->new( $port, $t->testdir() . '/' . $file ) + or die "Can't create test server socket: $!\n"; + local $SIG{PIPE} = 'IGNORE'; + + $server->on( 'GET', '/shelves?key=this-is-an-api-key', <<'EOF'); +HTTP/1.1 200 OK +Connection: close + +{ "shelves": [ + { "name": "shelves/1", "theme": "Fiction" }, + { "name": "shelves/2", "theme": "Fantasy" } + ] +} +EOF + $server->run(); +} + +my @quota_responses = (); +my $quota_response_index = 0; + +sub servicecontrol { + my ( $t, $port, $file ) = @_; + my $server = HttpServer->new( $port, $t->testdir() . '/' . $file ) + or die "Can't create test server socket: $!\n"; + local $SIG{PIPE} = 'IGNORE'; + + my $quota_response_success = sprintf(<<'EOF'); +HTTP/1.1 200 OK +Connection: close + +EOF + + my $quota_response_success = + ServiceControl::convert_proto( <<'EOF', 'quota_response', 'binary' ); + +operation_id: "a20ab01c-415e-46fb-953c-61c6768248ba" +quota_metrics { + metric_name: "serviceruntime.googleapis.com/api/consumer/quota_used_count" + metric_values { + labels { + key: "/quota_name" + value: "metrics_first" + } + int64_value: 1 + } +} +EOF + + my $quota_response_exhausted = + ServiceControl::convert_proto( <<'EOF', 'quota_response', 'binary' ); +operation_id: "006eaa26-5c2f-41bc-b6d8-0972eff8bdf6" +allocate_errors { + code: RESOURCE_EXHAUSTED + description: "Insufficient tokens for quota group and limit \'apiWriteQpsPerProject_LOW\' of service \'jaebonginternal.sandbox.google.com\', using the limit by ID \'container:1002409420961\'." +} +service_config_id: "2017-02-08r9" +EOF + + $server->on( 'POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:check', <<'EOF'); +HTTP/1.1 200 OK +Connection: close + +EOF + + $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', $quota_response_success . $quota_response_exhausted); + + + $server->run(); +} + +################################################################################ From 3359f2dd6a42b2a47cd3ddae141d73e46d984a61 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 22 Feb 2017 10:33:00 -0800 Subject: [PATCH 2/6] Fixed failed test cases caused by recent changes --- src/nginx/t/check_no_consumer.t | 1 - src/nginx/t/cloud_trace.t | 10 +++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/nginx/t/check_no_consumer.t b/src/nginx/t/check_no_consumer.t index c08f24f01..803e9f79b 100644 --- a/src/nginx/t/check_no_consumer.t +++ b/src/nginx/t/check_no_consumer.t @@ -131,7 +131,6 @@ my $expected_report_body = ServiceControl::gen_report_body({ 'serviceName' => 'endpoints-test.cloudendpointsapis.com', 'url' => '/shelves?key=this-is-an-api-key', 'location' => 'us-central1', - 'api_key' => 'this-is-an-api-key', 'api_method' => 'ListShelves', 'http_method' => 'GET', 'log_message' => 'Method: ListShelves', diff --git a/src/nginx/t/cloud_trace.t b/src/nginx/t/cloud_trace.t index de001b46c..01406ff6e 100644 --- a/src/nginx/t/cloud_trace.t +++ b/src/nginx/t/cloud_trace.t @@ -44,7 +44,7 @@ my $BackendPort = ApiManager::pick_port(); my $ServiceControlPort = ApiManager::pick_port(); my $CloudTracePort = ApiManager::pick_port(); -my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(27); +my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(29); my $config = ApiManager::get_bookstore_service_config_allow_unregistered . ApiManager::read_test_file('testdata/logs_metrics.pb.txt') . <<"EOF"; @@ -151,11 +151,15 @@ is($json_obj->{traces}->[0]->{spans}->[3]->{name}, 'Call ServiceControl server', 'Next trace span is Call ServiceControl server'); is($json_obj->{traces}->[0]->{spans}->[3]->{parentSpanId}, $check_service_control_cache_id, 'Parent of Call ServiceControl sever span is CheckServiceControlCache'); -is($json_obj->{traces}->[0]->{spans}->[4]->{name}, 'Backend', +is($json_obj->{traces}->[0]->{spans}->[4]->{name}, 'QuotaControl', 'Next trace span is Backend'); is($json_obj->{traces}->[0]->{spans}->[4]->{parentSpanId}, $rootid, 'Parent of Beckend span is root'); -my $backend_span_id = $json_obj->{traces}->[0]->{spans}->[4]->{spanId}; +is($json_obj->{traces}->[0]->{spans}->[5]->{name}, 'Backend', + 'Next trace span is Backend'); +is($json_obj->{traces}->[0]->{spans}->[5]->{parentSpanId}, $rootid, + 'Parent of Beckend span is root'); +my $backend_span_id = $json_obj->{traces}->[0]->{spans}->[5]->{spanId}; my @bookstore_requests = ApiManager::read_http_stream($t, 'bookstore.log'); is(scalar @bookstore_requests, 1, 'Bookstore received 1 request.'); From 97a1e17304e7b321ba4f328a43471d809bb7dd16 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 22 Feb 2017 12:25:50 -0800 Subject: [PATCH 3/6] Removed unnecessary variable and applied server config to disable report cache --- src/nginx/t/quota.t | 116 ++++++++++++---------------------- src/nginx/t/quota_exhausted.t | 32 ++-------- 2 files changed, 49 insertions(+), 99 deletions(-) diff --git a/src/nginx/t/quota.t b/src/nginx/t/quota.t index e4d180ca8..4448cd5c6 100644 --- a/src/nginx/t/quota.t +++ b/src/nginx/t/quota.t @@ -32,8 +32,7 @@ use Data::Dumper; ################################################################################ -use src::nginx::t::ApiManager - ; # Must be first (sets up import path to the Nginx test module) +use src::nginx::t::ApiManager; # Must be first (sets up import path to the Nginx test module) use src::nginx::t::HttpServer; use src::nginx::t::ServiceControl; use Test::Nginx; # Imports Nginx's test module @@ -46,9 +45,17 @@ my $NginxPort = ApiManager::pick_port(); my $BackendPort = ApiManager::pick_port(); my $ServiceControlPort = ApiManager::pick_port(); -my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(22); +my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(26); # Save service name in the service configuration protocol buffer file. +$t->write_file('server.pb.txt', <<"EOF"); +service_control_config { + report_aggregator_config { + cache_entries: 0 + flush_interval_ms: 1000 + } +} +EOF $t->write_file( 'service.pb.txt', ApiManager::get_bookstore_service_config . <<"EOF"); @@ -89,6 +96,7 @@ http { location / { endpoints { api service.pb.txt; + server_config server.pb.txt; %%TEST_CONFIG%% on; } @@ -99,18 +107,14 @@ http { EOF $t->run_daemon( \&bookstore, $t, $BackendPort, 'bookstore.log' ); -$t->run_daemon( \&servicecontrol, $t, $ServiceControlPort, - 'servicecontrol.log' ); -is( $t->waitforsocket("127.0.0.1:${BackendPort}"), - 1, 'Bookstore socket ready.' ); -is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), - 1, 'Service control socket ready.' ); +$t->run_daemon( \&servicecontrol, $t, $ServiceControlPort, 'servicecontrol.log' ); +is( $t->waitforsocket("127.0.0.1:${BackendPort}"), 1, 'Bookstore socket ready.' ); +is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), 1, 'Service control socket ready.' ); $t->run(); ################################################################################ -my $response = - ApiManager::http_get( $NginxPort, '/shelves?key=this-is-an-api-key' ); +my $response = ApiManager::http_get( $NginxPort, '/shelves?key=this-is-an-api-key' ); $t->stop_daemons(); @@ -135,68 +139,38 @@ is( $r->{uri}, '/shelves?key=this-is-an-api-key', 'Backend uri was /shelves' ); is( $r->{headers}->{host}, "127.0.0.1:${BackendPort}", 'Host header was set' ); @requests = ApiManager::read_http_stream( $t, 'servicecontrol.log' ); -is( scalar @requests, 2, 'Service control received two requests' ); +is( scalar @requests, 3, 'Service control received two requests' ); $r = shift @requests; is( $r->{verb}, 'POST', ':check verb was post' ); -is( - $r->{uri}, - '/v1/services/endpoints-test.cloudendpointsapis.com:check', - ':check was called' -); -is( - $r->{headers}->{host}, - "127.0.0.1:${ServiceControlPort}", - 'Host header was set' -); -is( $r->{headers}->{'content-type'}, - 'application/x-protobuf', ':check Content-Type was protocol buffer' ); +is( $r->{uri}, '/v1/services/endpoints-test.cloudendpointsapis.com:check', ':check was called'); +is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", 'Host header was set'); +is( $r->{headers}->{'content-type'}, 'application/x-protobuf', ':check Content-Type was protocol buffer'); # test allocateQuota request was requested $r = shift @requests; is( $r->{verb}, 'POST', ':allocateQuota verb was post' ); -is( - $r->{uri}, - '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', - ':allocateQuota was called' -); -is( - $r->{headers}->{host}, - "127.0.0.1:${ServiceControlPort}", - 'Host header was set' -); -is( $r->{headers}->{'content-type'}, - 'application/x-protobuf', ':check Content-Type was protocol buffer' ); - -my $allocate_quota_request = - decode_json( - ServiceControl::convert_proto( $r->{body}, 'quota_request', 'json' ) ); - -my @quotaMetrics = - @{ $allocate_quota_request->{allocateOperation}->{quotaMetrics} }; +is( $r->{uri}, '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', ':allocateQuota was called'); +is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", 'Host header was set'); +is( $r->{headers}->{'content-type'}, 'application/x-protobuf', ':check Content-Type was protocol buffer' ); +my $allocate_quota_request = decode_json(ServiceControl::convert_proto( $r->{body}, 'quota_request', 'json' ) ); +my @quotaMetrics = @{ $allocate_quota_request->{allocateOperation}->{quotaMetrics} }; is( @quotaMetrics, 2, "Quota metrics should have two elements" ); -if ( $quotaMetrics[0]->{metricName} eq "metrics_first" ) { - is( $quotaMetrics[0]->{metricName}, - "metrics_first", "Quota metric name is 'metrics_first'" ); - is( $quotaMetrics[0]->{metricValues}[0]->{int64Value}, - 2, "Quota metric value is 2" ); - is( $quotaMetrics[1]->{metricName}, - "metrics_second", "Quota metric name is 'metrics_second'" ); - is( $quotaMetrics[1]->{metricValues}[0]->{int64Value}, - 1, "Quota metric value is 1" ); -} -else { - is( $quotaMetrics[0]->{metricName}, - "metrics_second", "Quota metric name is 'metrics_second'" ); - is( $quotaMetrics[0]->{metricValues}[0]->{int64Value}, - 1, "Quota metric value is 1" ); - is( $quotaMetrics[1]->{metricName}, - "metrics_first", "Quota metric name is 'metrics_first'" ); - is( $quotaMetrics[1]->{metricValues}[0]->{int64Value}, - 2, "Quota metric value is 2" ); -} +my @sorted_quotaMetrics = sort { $a->{metricName} cmp $b->{metricName} } @quotaMetrics; +is( $sorted_quotaMetrics[0]->{metricName}, "metrics_first", "Quota metric name is 'metrics_first'" ); +is( $sorted_quotaMetrics[0]->{metricValues}[0]->{int64Value}, 2, "Quota metric value is 2" ); +is( $sorted_quotaMetrics[1]->{metricName}, "metrics_second", "Quota metric name is 'metrics_second'" ); +is( $sorted_quotaMetrics[1]->{metricValues}[0]->{int64Value}, 1, "Quota metric value is 1" ); + +# check report +$r = shift @requests; + +is( $r->{verb}, 'POST', ':report verb was post' ); +is( $r->{uri}, '/v1/services/endpoints-test.cloudendpointsapis.com:report', ':report was called'); +is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", 'Host header was set'); +is( $r->{headers}->{'content-type'}, 'application/x-protobuf', ':check Content-Type was protocol buffer' ); ################################################################################ @@ -228,12 +202,6 @@ sub servicecontrol { or die "Can't create test server socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; - my $quota_response_success = sprintf(<<'EOF'); -HTTP/1.1 200 OK -Connection: close - -EOF - my $quota_response_success = ServiceControl::convert_proto( <<'EOF', 'quota_response', 'binary' ); operation_id: "a20ab01c-415e-46fb-953c-61c6768248ba" @@ -263,11 +231,11 @@ Connection: close EOF - $server->on( - 'POST', - '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', - $quota_response_success . $quota_response_success - ); + $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', <<'EOF' . $quota_response_success); +HTTP/1.1 200 OK +Connection: close + +EOF $server->run(); } diff --git a/src/nginx/t/quota_exhausted.t b/src/nginx/t/quota_exhausted.t index ee569b3d1..20814fee1 100644 --- a/src/nginx/t/quota_exhausted.t +++ b/src/nginx/t/quota_exhausted.t @@ -115,10 +115,10 @@ $t->stop_daemons(); my ( $response_headers, $response_body ) = split /\r\n\r\n/, $response, 2; -like( $response_headers, qr/HTTP\/1\.1 403 Forbidden/, 'Returned HTTP 403 Forbidden.' ); +like( $response_headers, qr/HTTP\/1\.1 429/, 'Returned HTTP 429.' ); is( $response_body, <<'EOF', 'Shelves returned in the response body.' ); { - "code": 7, + "code": 8, "message": "Quota allocation failed.", "details": [ { @@ -163,28 +163,6 @@ sub servicecontrol { or die "Can't create test server socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; - my $quota_response_success = sprintf(<<'EOF'); -HTTP/1.1 200 OK -Connection: close - -EOF - - my $quota_response_success = - ServiceControl::convert_proto( <<'EOF', 'quota_response', 'binary' ); - -operation_id: "a20ab01c-415e-46fb-953c-61c6768248ba" -quota_metrics { - metric_name: "serviceruntime.googleapis.com/api/consumer/quota_used_count" - metric_values { - labels { - key: "/quota_name" - value: "metrics_first" - } - int64_value: 1 - } -} -EOF - my $quota_response_exhausted = ServiceControl::convert_proto( <<'EOF', 'quota_response', 'binary' ); operation_id: "006eaa26-5c2f-41bc-b6d8-0972eff8bdf6" @@ -202,7 +180,11 @@ Connection: close EOF - $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', $quota_response_success . $quota_response_exhausted); + $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', <<'EOF' . $quota_response_exhausted); +HTTP/1.1 200 OK +Connection: close + +EOF $server->run(); From 729edefcd45d071016f6f7e2f0f4ff4a454e6ff6 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 22 Feb 2017 12:27:36 -0800 Subject: [PATCH 4/6] Removed unnecessary variable and applied server config to disable report cache --- src/nginx/t/quota.t | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/nginx/t/quota.t b/src/nginx/t/quota.t index 4448cd5c6..ade03ce1a 100644 --- a/src/nginx/t/quota.t +++ b/src/nginx/t/quota.t @@ -202,28 +202,6 @@ sub servicecontrol { or die "Can't create test server socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; - my $quota_response_success = - ServiceControl::convert_proto( <<'EOF', 'quota_response', 'binary' ); -operation_id: "a20ab01c-415e-46fb-953c-61c6768248ba" -quota_metrics { - metric_name: "serviceruntime.googleapis.com/api/consumer/quota_used_count" - metric_values { - labels { - key: "/quota_name" - value: "metrics_first" - } - int64_value: 2 - } - metric_values { - labels { - key: "/quota_name" - value: "metrics_second" - } - int64_value: 1 - } -} -EOF - $server->on( 'POST', '/v1/services/endpoints-test.cloudendpointsapis.com:check', <<'EOF'); HTTP/1.1 200 OK @@ -231,7 +209,7 @@ Connection: close EOF - $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', <<'EOF' . $quota_response_success); + $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', <<'EOF'); HTTP/1.1 200 OK Connection: close From c9370b32959f2cce298db5c41cfce0788918ba42 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 22 Feb 2017 12:44:25 -0800 Subject: [PATCH 5/6] Updated commit id of istio/proxy repo --- WORKSPACE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WORKSPACE b/WORKSPACE index b9d9f1f72..03beb0eda 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -26,7 +26,7 @@ # # A Bazel (http://bazel.io) workspace for the Google Cloud Endpoints runtime. -ISTIO_PROXY = "5b25d57a71c5835d265e724e0ca39b5d73a97df6" +ISTIO_PROXY = "0d04d7a191f644ef384d9585a5b93d0ffb8e7f3d" git_repository( name = "nginx", From 69044a23ddaf3b3e7be0604ddbf9b869d66c38b8 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 22 Feb 2017 14:39:24 -0800 Subject: [PATCH 6/6] Set max column to 80, Added report request handler --- src/nginx/t/quota.t | 90 ++++++++++++++++++--------- src/nginx/t/quota_api_not_available.t | 49 ++++++++++----- src/nginx/t/quota_exhausted.t | 47 ++++++++++---- 3 files changed, 131 insertions(+), 55 deletions(-) diff --git a/src/nginx/t/quota.t b/src/nginx/t/quota.t index ade03ce1a..a4cc1998d 100644 --- a/src/nginx/t/quota.t +++ b/src/nginx/t/quota.t @@ -26,13 +26,12 @@ # use strict; use warnings; - use JSON::PP; -use Data::Dumper; ################################################################################ -use src::nginx::t::ApiManager; # Must be first (sets up import path to the Nginx test module) +use src::nginx::t::ApiManager; # Must be first (sets up import path to + # the Nginx test module) use src::nginx::t::HttpServer; use src::nginx::t::ServiceControl; use Test::Nginx; # Imports Nginx's test module @@ -47,7 +46,8 @@ my $ServiceControlPort = ApiManager::pick_port(); my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(26); -# Save service name in the service configuration protocol buffer file. +# Save servce configuration that disables the report cache. +# Report request will be sent for each client request $t->write_file('server.pb.txt', <<"EOF"); service_control_config { report_aggregator_config { @@ -57,6 +57,7 @@ service_control_config { } EOF +# Save service name in the service configuration protocol buffer file. $t->write_file( 'service.pb.txt', ApiManager::get_bookstore_service_config . <<"EOF"); control { @@ -67,13 +68,13 @@ quota { { selector: "ListShelves" metric_costs: [ - { + { key: "metrics_first" - value: 2 + value: 2 }, - { + { key: "metrics_second" - value: 1 + value: 1 } ] } @@ -109,12 +110,14 @@ EOF $t->run_daemon( \&bookstore, $t, $BackendPort, 'bookstore.log' ); $t->run_daemon( \&servicecontrol, $t, $ServiceControlPort, 'servicecontrol.log' ); is( $t->waitforsocket("127.0.0.1:${BackendPort}"), 1, 'Bookstore socket ready.' ); -is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), 1, 'Service control socket ready.' ); +is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), 1, + 'Service control socket ready.' ); $t->run(); ################################################################################ -my $response = ApiManager::http_get( $NginxPort, '/shelves?key=this-is-an-api-key' ); +my $response = ApiManager::http_get( + $NginxPort, '/shelves?key=this-is-an-api-key' ); $t->stop_daemons(); @@ -139,38 +142,58 @@ is( $r->{uri}, '/shelves?key=this-is-an-api-key', 'Backend uri was /shelves' ); is( $r->{headers}->{host}, "127.0.0.1:${BackendPort}", 'Host header was set' ); @requests = ApiManager::read_http_stream( $t, 'servicecontrol.log' ); -is( scalar @requests, 3, 'Service control received two requests' ); +is( scalar @requests, 3, 'Service control received three requests' ); +# check $r = shift @requests; is( $r->{verb}, 'POST', ':check verb was post' ); -is( $r->{uri}, '/v1/services/endpoints-test.cloudendpointsapis.com:check', ':check was called'); -is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", 'Host header was set'); -is( $r->{headers}->{'content-type'}, 'application/x-protobuf', ':check Content-Type was protocol buffer'); +is( $r->{uri}, '/v1/services/endpoints-test.cloudendpointsapis.com:check', + ':check was called'); +is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", + 'Host header was set'); +is( $r->{headers}->{'content-type'}, 'application/x-protobuf', + ':check Content-Type was protocol buffer'); # test allocateQuota request was requested $r = shift @requests; is( $r->{verb}, 'POST', ':allocateQuota verb was post' ); -is( $r->{uri}, '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', ':allocateQuota was called'); -is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", 'Host header was set'); -is( $r->{headers}->{'content-type'}, 'application/x-protobuf', ':check Content-Type was protocol buffer' ); - -my $allocate_quota_request = decode_json(ServiceControl::convert_proto( $r->{body}, 'quota_request', 'json' ) ); -my @quotaMetrics = @{ $allocate_quota_request->{allocateOperation}->{quotaMetrics} }; +is( $r->{uri}, + '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', + ':allocateQuota was called'); +is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", + 'Host header was set'); +is( $r->{headers}->{'content-type'}, 'application/x-protobuf', + ':check Content-Type was protocol buffer' ); + +my $allocate_quota_request = decode_json(ServiceControl::convert_proto( + $r->{body}, 'quota_request', 'json' ) ); + +my @quotaMetrics = + @{ $allocate_quota_request->{allocateOperation}->{quotaMetrics} }; is( @quotaMetrics, 2, "Quota metrics should have two elements" ); -my @sorted_quotaMetrics = sort { $a->{metricName} cmp $b->{metricName} } @quotaMetrics; -is( $sorted_quotaMetrics[0]->{metricName}, "metrics_first", "Quota metric name is 'metrics_first'" ); -is( $sorted_quotaMetrics[0]->{metricValues}[0]->{int64Value}, 2, "Quota metric value is 2" ); -is( $sorted_quotaMetrics[1]->{metricName}, "metrics_second", "Quota metric name is 'metrics_second'" ); -is( $sorted_quotaMetrics[1]->{metricValues}[0]->{int64Value}, 1, "Quota metric value is 1" ); +my @sorted_quotaMetrics = + sort { $a->{metricName} cmp $b->{metricName} } @quotaMetrics; + +is( $sorted_quotaMetrics[0]->{metricName}, "metrics_first", + "Quota metric name is 'metrics_first'" ); +is( $sorted_quotaMetrics[0]->{metricValues}[0]->{int64Value}, 2, + "Quota metric value is 2" ); +is( $sorted_quotaMetrics[1]->{metricName}, "metrics_second", + "Quota metric name is 'metrics_second'" ); +is( $sorted_quotaMetrics[1]->{metricValues}[0]->{int64Value}, 1, + "Quota metric value is 1" ); # check report $r = shift @requests; is( $r->{verb}, 'POST', ':report verb was post' ); -is( $r->{uri}, '/v1/services/endpoints-test.cloudendpointsapis.com:report', ':report was called'); -is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", 'Host header was set'); -is( $r->{headers}->{'content-type'}, 'application/x-protobuf', ':check Content-Type was protocol buffer' ); +is( $r->{uri}, '/v1/services/endpoints-test.cloudendpointsapis.com:report', + ':report was called'); +is( $r->{headers}->{host}, "127.0.0.1:${ServiceControlPort}", + 'Host header was set'); +is( $r->{headers}->{'content-type'}, 'application/x-protobuf', + ':check Content-Type was protocol buffer' ); ################################################################################ @@ -209,7 +232,16 @@ Connection: close EOF - $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', <<'EOF'); + $server->on('POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', + <<'EOF'); +HTTP/1.1 200 OK +Connection: close + +EOF + + $server->on( 'POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:report', <<'EOF'); HTTP/1.1 200 OK Connection: close diff --git a/src/nginx/t/quota_api_not_available.t b/src/nginx/t/quota_api_not_available.t index 3a1fccac2..277b1c3f7 100644 --- a/src/nginx/t/quota_api_not_available.t +++ b/src/nginx/t/quota_api_not_available.t @@ -32,7 +32,8 @@ use Data::Dumper; ################################################################################ -use src::nginx::t::ApiManager; # Must be first (sets up import path to the Nginx test module) +use src::nginx::t::ApiManager; # Must be first (sets up import path + # to the Nginx test module) use src::nginx::t::HttpServer; use src::nginx::t::ServiceControl; use Test::Nginx; # Imports Nginx's test module @@ -47,8 +48,18 @@ my $ServiceControlPort = ApiManager::pick_port(); my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(9); -# Save service name in the service configuration protocol buffer file. +# Save servce configuration that disables the report cache. +# Report request will be sent for each client request +$t->write_file('server.pb.txt', <<"EOF"); +service_control_config { + report_aggregator_config { + cache_entries: 0 + flush_interval_ms: 1000 + } +} +EOF +# Save service name in the service configuration protocol buffer file. $t->write_file( 'service.pb.txt', ApiManager::get_bookstore_service_config . <<"EOF"); control { @@ -59,13 +70,13 @@ quota { { selector: "ListShelves" metric_costs: [ - { + { key: "metrics_first" - value: 2 + value: 2 }, - { + { key: "metrics_second" - value: 1 + value: 1 } ] } @@ -88,6 +99,7 @@ http { location / { endpoints { api service.pb.txt; + server_config server.pb.txt; %%TEST_CONFIG%% on; } @@ -98,14 +110,18 @@ http { EOF $t->run_daemon( \&bookstore, $t, $BackendPort, 'bookstore.log' ); -$t->run_daemon( \&servicecontrol, $t, $ServiceControlPort, 'servicecontrol.log' ); -is( $t->waitforsocket("127.0.0.1:${BackendPort}"), 1, 'Bookstore socket ready.' ); -is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), 1, 'Service control socket ready.' ); +$t->run_daemon( \&servicecontrol, $t, $ServiceControlPort, + 'servicecontrol.log' ); +is( $t->waitforsocket("127.0.0.1:${BackendPort}"), 1, + 'Bookstore socket ready.' ); +is( $t->waitforsocket("127.0.0.1:${ServiceControlPort}"), 1, + 'Service control socket ready.' ); $t->run(); ################################################################################ -my $response = ApiManager::http_get( $NginxPort, '/shelves?key=this-is-an-api-key' ); +my $response = ApiManager::http_get( $NginxPort, + '/shelves?key=this-is-an-api-key' ); $t->stop_daemons(); @@ -130,7 +146,7 @@ is( $r->{uri}, '/shelves?key=this-is-an-api-key', 'Backend uri was /shelves' ); is( $r->{headers}->{host}, "127.0.0.1:${BackendPort}", 'Host header was set' ); @requests = ApiManager::read_http_stream( $t, 'servicecontrol.log' ); -is( scalar @requests, 2, 'Service control received two requests' ); +is( scalar @requests, 3, 'Service control received three requests' ); ################################################################################ @@ -161,16 +177,19 @@ sub servicecontrol { my $server = HttpServer->new( $port, $t->testdir() . '/' . $file ) or die "Can't create test server socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; - + $server->on( 'POST', '/v1/services/endpoints-test.cloudendpointsapis.com:check', <<'EOF'); HTTP/1.1 200 OK Connection: close EOF - - $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', <<'EOF'); -HTTP/1.1 404 Not Found + # The allocateQuota request receives HTTP 404 Not Found error code. + # This simulates QuotaController is not available. + + $server->on( 'POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:report', <<'EOF'); +HTTP/1.1 200 OK Connection: close EOF diff --git a/src/nginx/t/quota_exhausted.t b/src/nginx/t/quota_exhausted.t index 20814fee1..0fdd8f404 100644 --- a/src/nginx/t/quota_exhausted.t +++ b/src/nginx/t/quota_exhausted.t @@ -32,7 +32,8 @@ use Data::Dumper; ################################################################################ -use src::nginx::t::ApiManager; # Must be first (sets up import path to the Nginx test module) +use src::nginx::t::ApiManager; # Must be first (sets up import path to + # the Nginx test module) use src::nginx::t::HttpServer; use src::nginx::t::ServiceControl; use Test::Nginx; # Imports Nginx's test module @@ -45,10 +46,20 @@ my $NginxPort = ApiManager::pick_port(); my $BackendPort = ApiManager::pick_port(); my $ServiceControlPort = ApiManager::pick_port(); -my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(5); +my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(6); -# Save service name in the service configuration protocol buffer file. +# Save servce configuration that disables the report cache. +# Report request will be sent for each client request +$t->write_file('server.pb.txt', <<"EOF"); +service_control_config { + report_aggregator_config { + cache_entries: 0 + flush_interval_ms: 1000 + } +} +EOF +# Save service name in the service configuration protocol buffer file. $t->write_file( 'service.pb.txt', ApiManager::get_bookstore_service_config . <<"EOF"); control { @@ -59,13 +70,13 @@ quota { { selector: "ListShelves" metric_costs: [ - { + { key: "metrics_first" - value: 2 + value: 2 }, - { + { key: "metrics_second" - value: 1 + value: 1 } ] } @@ -88,6 +99,7 @@ http { location / { endpoints { api service.pb.txt; + server_config server.pb.txt; %%TEST_CONFIG%% on; } @@ -133,6 +145,11 @@ EOF my @requests = ApiManager::read_http_stream( $t, 'bookstore.log' ); is( scalar @requests, 0, 'Backend received empty request' ); + +@requests = ApiManager::read_http_stream( $t, 'servicecontrol.log' ); +is( scalar @requests, 3, 'Service control received three requests' ); + + ################################################################################ sub bookstore { @@ -162,7 +179,7 @@ sub servicecontrol { my $server = HttpServer->new( $port, $t->testdir() . '/' . $file ) or die "Can't create test server socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; - + my $quota_response_exhausted = ServiceControl::convert_proto( <<'EOF', 'quota_response', 'binary' ); operation_id: "006eaa26-5c2f-41bc-b6d8-0972eff8bdf6" @@ -172,20 +189,28 @@ allocate_errors { } service_config_id: "2017-02-08r9" EOF - + $server->on( 'POST', '/v1/services/endpoints-test.cloudendpointsapis.com:check', <<'EOF'); HTTP/1.1 200 OK Connection: close EOF - - $server->on('POST', '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', <<'EOF' . $quota_response_exhausted); + + $server->on('POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:allocateQuota', + <<'EOF' . $quota_response_exhausted); HTTP/1.1 200 OK Connection: close EOF + $server->on( 'POST', + '/v1/services/endpoints-test.cloudendpointsapis.com:report', <<'EOF'); +HTTP/1.1 200 OK +Connection: close + +EOF $server->run(); }