diff --git a/README.md b/README.md index 1285cdb73..23c834c4d 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,10 @@ To find out more about building, running, and testing ESPv2: * [Run ESPv2 on Google GKE](/doc/esp-v2-on-k8s.md) +## Examples + +How to configure ESPv2? See [examples](/examples/README.md) + ## Disclaimer ESPv2 is in Beta currently. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..575c9380e --- /dev/null +++ b/examples/README.md @@ -0,0 +1,26 @@ +# ESPv2 Configurations + +This directory contains examples of how to configure ESPv2. + +## Service Control + +Includes how to configure Authorization by API Key, and Quota, controlled by ServiceControl filter. + +* [Producer defined OpenAPI Specification](/service_control/openapi_swagger.json) + +* [Google Cloud Endpoints generated Service configuration]( +/service_control/service_config_generated.json + ): Service configuration generated by ServiceManagement Service. + Can be accessible by running: + +``` +gcloud endpoints configs describe "${CONFIG_ID}" --project="${PROJECT}" \ + --service="${SERVICE}" --format=json > service.json +``` + +* [ESPv2 generated Envoy configuration]( +/service_control/envoy_config.json + ): Equivalent Envoy static Bootstrap configuration. + +## Dynamic Routing(TBD) + diff --git a/examples/service_control/envoy_config.json b/examples/service_control/envoy_config.json new file mode 100644 index 000000000..6bce2b657 --- /dev/null +++ b/examples/service_control/envoy_config.json @@ -0,0 +1,776 @@ +{ + "node": { + "id": "ESPv2", + "cluster": "ESPv2_cluster" + }, + "staticResources": { + "listeners": [ + { + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 8080 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager", + "statPrefix": "ingress_http", + "routeConfig": { + "name": "local_route", + "virtualHosts": [ + { + "name": "backend", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "1.bookstore_endpoints_apiproxy_231719_cloud_goog" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.path_matcher", + "typedConfig": { + "@type": "type.googleapis.com/google.api.envoy.http.path_matcher.FilterConfig", + "rules": [ + { + "pattern": { + "uriTemplate": "/shelves", + "httpMethod": "POST" + }, + "operation": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.CreateShelf" + }, + { + "pattern": { + "uriTemplate": "/shelves", + "httpMethod": "GET" + }, + "operation": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.ListShelves" + } + ], + "segmentNames": [ + { + "snakeName": "null_value", + "jsonName": "nullValue" + }, + { + "snakeName": "number_value", + "jsonName": "numberValue" + }, + { + "snakeName": "string_value", + "jsonName": "stringValue" + }, + { + "snakeName": "bool_value", + "jsonName": "boolValue" + }, + { + "snakeName": "struct_value", + "jsonName": "structValue" + }, + { + "snakeName": "list_value", + "jsonName": "listValue" + } + ] + } + }, + { + "name": "envoy.filters.http.service_control", + "typedConfig": { + "@type": "type.googleapis.com/google.api.envoy.http.service_control.FilterConfig", + "services": [ + { + "serviceName": "bookstore.endpoints.apiproxy-231719.cloud.goog", + "serviceConfigId": "2019-12-16r0", + "producerProjectId": "apiproxy-231719", + "serviceConfig": { + "@type": "type.googleapis.com/google.api.Service", + "logs": [ + { + "name": "endpoints_log" + } + ], + "metrics": [ + { + "name": "read-requests", + "type": "read-requests", + "metricKind": "DELTA", + "valueType": "INT64", + "displayName": "Read requests" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/request_count", + "type": "serviceruntime.googleapis.com/api/consumer/request_count", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/protocol" + }, + { + "key": "/response_code" + }, + { + "key": "/response_code_class" + }, + { + "key": "/status_code" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/error_count", + "type": "serviceruntime.googleapis.com/api/consumer/error_count", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/error_type" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/total_latencies", + "type": "serviceruntime.googleapis.com/api/consumer/total_latencies", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/request_count", + "type": "serviceruntime.googleapis.com/api/producer/request_count", + "labels": [ + { + "key": "/protocol" + }, + { + "key": "/response_code" + }, + { + "key": "/response_code_class" + }, + { + "key": "/status_code" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/error_count", + "type": "serviceruntime.googleapis.com/api/producer/error_count", + "labels": [ + { + "key": "/error_type" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/total_latencies", + "type": "serviceruntime.googleapis.com/api/producer/total_latencies", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user", + "type": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/end_user" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user_country", + "type": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user_country", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/end_user_country" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_referer", + "type": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_referer", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/referer" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/top_request_count_by_consumer", + "type": "serviceruntime.googleapis.com/api/producer/top_request_count_by_consumer", + "labels": [ + { + "key": "/protocol" + }, + { + "key": "/response_code" + }, + { + "key": "/consumer_id" + }, + { + "key": "/status_code" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/quota_used_count", + "type": "serviceruntime.googleapis.com/api/consumer/quota_used_count", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/quota_group_name" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/request_overhead_latencies", + "type": "serviceruntime.googleapis.com/api/consumer/request_overhead_latencies", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/backend_latencies", + "type": "serviceruntime.googleapis.com/api/consumer/backend_latencies", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/request_sizes", + "type": "serviceruntime.googleapis.com/api/consumer/request_sizes", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/response_sizes", + "type": "serviceruntime.googleapis.com/api/consumer/response_sizes", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/request_overhead_latencies", + "type": "serviceruntime.googleapis.com/api/producer/request_overhead_latencies", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/backend_latencies", + "type": "serviceruntime.googleapis.com/api/producer/backend_latencies", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/request_sizes", + "type": "serviceruntime.googleapis.com/api/producer/request_sizes", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/response_sizes", + "type": "serviceruntime.googleapis.com/api/producer/response_sizes", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/top_request_sizes_by_consumer", + "type": "serviceruntime.googleapis.com/api/producer/top_request_sizes_by_consumer", + "labels": [ + { + "key": "/consumer_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/top_response_sizes_by_consumer", + "type": "serviceruntime.googleapis.com/api/producer/top_response_sizes_by_consumer", + "labels": [ + { + "key": "/consumer_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/request_count", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/request_count", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/protocol" + }, + { + "key": "/response_code" + }, + { + "key": "/response_code_class" + }, + { + "key": "/status_code" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/error_count", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/error_count", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/error_type" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/total_latencies", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/total_latencies", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/quota_used_count", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/quota_used_count", + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/quota_group_name" + } + ], + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/request_overhead_latencies", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/request_overhead_latencies", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/backend_latencies", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/backend_latencies", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/request_sizes", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/request_sizes", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/response_sizes", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/response_sizes", + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/streaming_request_message_counts", + "type": "serviceruntime.googleapis.com/api/producer/streaming_request_message_counts", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/streaming_response_message_counts", + "type": "serviceruntime.googleapis.com/api/producer/streaming_response_message_counts", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/streaming_request_message_counts", + "type": "serviceruntime.googleapis.com/api/consumer/streaming_request_message_counts", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/streaming_response_message_counts", + "type": "serviceruntime.googleapis.com/api/consumer/streaming_response_message_counts", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/streaming_durations", + "type": "serviceruntime.googleapis.com/api/producer/streaming_durations", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/streaming_durations", + "type": "serviceruntime.googleapis.com/api/consumer/streaming_durations", + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/request_bytes", + "type": "serviceruntime.googleapis.com/api/producer/request_bytes", + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/producer/response_bytes", + "type": "serviceruntime.googleapis.com/api/producer/response_bytes", + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/request_bytes", + "type": "serviceruntime.googleapis.com/api/consumer/request_bytes", + "metricKind": "DELTA", + "valueType": "INT64" + }, + { + "name": "serviceruntime.googleapis.com/api/consumer/response_bytes", + "type": "serviceruntime.googleapis.com/api/consumer/response_bytes", + "metricKind": "DELTA", + "valueType": "INT64" + } + ], + "monitoredResources": [ + { + "type": "api", + "labels": [ + { + "key": "cloud.googleapis.com/location" + }, + { + "key": "cloud.googleapis.com/uid" + }, + { + "key": "serviceruntime.googleapis.com/api_version" + }, + { + "key": "serviceruntime.googleapis.com/api_method" + }, + { + "key": "serviceruntime.googleapis.com/consumer_project" + }, + { + "key": "cloud.googleapis.com/project" + }, + { + "key": "cloud.googleapis.com/service" + } + ] + } + ], + "logging": { + "producerDestinations": [ + { + "monitoredResource": "api", + "logs": [ + "endpoints_log" + ] + } + ] + }, + "monitoring": { + "producerDestinations": [ + { + "monitoredResource": "api", + "metrics": [ + "serviceruntime.googleapis.com/api/producer/request_count", + "serviceruntime.googleapis.com/api/producer/error_count", + "serviceruntime.googleapis.com/api/producer/total_latencies", + "serviceruntime.googleapis.com/api/producer/request_overhead_latencies", + "serviceruntime.googleapis.com/api/producer/backend_latencies", + "serviceruntime.googleapis.com/api/producer/request_sizes", + "serviceruntime.googleapis.com/api/producer/response_sizes", + "serviceruntime.googleapis.com/api/producer/top_request_count_by_consumer", + "serviceruntime.googleapis.com/api/producer/top_request_sizes_by_consumer", + "serviceruntime.googleapis.com/api/producer/top_response_sizes_by_consumer", + "serviceruntime.googleapis.com/api/producer/streaming_request_message_counts", + "serviceruntime.googleapis.com/api/producer/streaming_response_message_counts", + "serviceruntime.googleapis.com/api/producer/streaming_durations", + "serviceruntime.googleapis.com/api/producer/request_bytes", + "serviceruntime.googleapis.com/api/producer/response_bytes", + "serviceruntime.googleapis.com/api/producer/by_consumer/request_count", + "serviceruntime.googleapis.com/api/producer/by_consumer/error_count", + "serviceruntime.googleapis.com/api/producer/by_consumer/total_latencies", + "serviceruntime.googleapis.com/api/producer/by_consumer/quota_used_count", + "serviceruntime.googleapis.com/api/producer/by_consumer/request_overhead_latencies", + "serviceruntime.googleapis.com/api/producer/by_consumer/backend_latencies", + "serviceruntime.googleapis.com/api/producer/by_consumer/request_sizes", + "serviceruntime.googleapis.com/api/producer/by_consumer/response_sizes" + ] + } + ], + "consumerDestinations": [ + { + "monitoredResource": "api", + "metrics": [ + "serviceruntime.googleapis.com/api/consumer/request_count", + "serviceruntime.googleapis.com/api/consumer/error_count", + "serviceruntime.googleapis.com/api/consumer/quota_used_count", + "serviceruntime.googleapis.com/api/consumer/total_latencies", + "serviceruntime.googleapis.com/api/consumer/request_overhead_latencies", + "serviceruntime.googleapis.com/api/consumer/backend_latencies", + "serviceruntime.googleapis.com/api/consumer/request_sizes", + "serviceruntime.googleapis.com/api/consumer/response_sizes", + "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user", + "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user_country", + "serviceruntime.googleapis.com/api/consumer/top_request_count_by_referer", + "serviceruntime.googleapis.com/api/consumer/streaming_request_message_counts", + "serviceruntime.googleapis.com/api/consumer/streaming_response_message_counts", + "serviceruntime.googleapis.com/api/consumer/streaming_durations", + "serviceruntime.googleapis.com/api/consumer/request_bytes", + "serviceruntime.googleapis.com/api/consumer/response_bytes" + ] + } + ] + } + }, + "backendProtocol": "http1", + "jwtPayloadMetadataName": "jwt_payloads" + } + ], + "requirements": [ + { + "serviceName": "bookstore.endpoints.apiproxy-231719.cloud.goog", + "operationName": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.CreateShelf" + }, + { + "serviceName": "bookstore.endpoints.apiproxy-231719.cloud.goog", + "operationName": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.ListShelves", + "apiKey": { + "allowWithoutApiKey": true + }, + "metricCosts": [ + { + "name": "read-requests", + "cost": "1" + } + ] + } + ], + "accessToken": { + "remoteToken": { + "uri": "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token", + "cluster": "metadata-cluster", + "timeout": "5s" + } + }, + "scCallingConfig": { + "networkFailOpen": true + }, + "serviceControlUri": { + "uri": "https://servicecontrol.googleapis.com/v1/services/", + "cluster": "service-control-cluster", + "timeout": "5s" + } + } + }, + { + "name": "envoy.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.http.router.v2.Router" + } + } + ], + "useRemoteAddress": false, + "xffNumTrustedHops": 2 + } + } + ] + } + ] + } + ], + "clusters": [ + { + "name": "1.bookstore_endpoints_apiproxy_231719_cloud_goog", + "type": "STRICT_DNS", + "connectTimeout": "20s", + "loadAssignment": { + "clusterName": "127.0.0.1", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8082 + } + } + } + } + ] + } + ] + } + }, + { + "name": "metadata-cluster", + "type": "STRICT_DNS", + "connectTimeout": "20s", + "loadAssignment": { + "clusterName": "169.254.169.254", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "169.254.169.254", + "portValue": 80 + } + } + } + } + ] + } + ] + } + }, + { + "name": "service-control-cluster", + "type": "LOGICAL_DNS", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "servicecontrol.googleapis.com", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "servicecontrol.googleapis.com", + "portValue": 443 + } + } + } + } + ] + } + ] + }, + "dnsLookupFamily": "V4_ONLY", + "transportSocket": { + "name": "envoy.transport_sockets.tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext", + "commonTlsContext": { + "validationContext": { + "trustedCa": { + "filename": "/etc/ssl/certs/ca-certificates.crt" + } + } + }, + "sni": "servicecontrol.googleapis.com" + } + } + } + ] + }, + "admin": {} +} \ No newline at end of file diff --git a/examples/service_control/openapi_swagger.json b/examples/service_control/openapi_swagger.json new file mode 100644 index 000000000..872092d26 --- /dev/null +++ b/examples/service_control/openapi_swagger.json @@ -0,0 +1,120 @@ +{ + "swagger": "2.0", + "info": { + "description": "A simple Google Cloud Endpoints Bookstore API example.", + "title": "Bookstore", + "version": "1.0.0" + }, + "host": "bookstore.endpoints.apiproxy-231719.cloud.goog", + "basePath": "/", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "https" + ], + "paths": { + "/shelves": { + "get": { + "description": "Returns all shelves in the bookstore.", + "operationId": "listShelves", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "List of shelves in the bookstore.", + "schema": { + "$ref": "#/definitions/listShelvesResponse" + } + } + }, + "x-google-quota": { + "metricCosts": { + "read-requests": 1 + } + }, + "security": [] + }, + "post": { + "description": "Creates a new shelf in the bookstore.", + "operationId": "createShelf", + "parameters": [ + { + "description": "A shelf resource to create.", + "in": "body", + "name": "shelf", + "required": true, + "schema": { + "$ref": "#/definitions/shelf" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A newly created shelf resource.", + "schema": { + "$ref": "#/definitions/shelf" + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "definitions": { + "shelf": { + "properties": { + "name": { + "type": "string" + }, + "theme": { + "type": "string" + } + }, + "required": [ + "name", + "theme" + ] + } + }, + "securityDefinitions": { + "api_key": { + "in": "query", + "name": "key", + "type": "apiKey" + } + }, + "x-google-management": { + "metrics": [ + { + "name": "read-requests", + "displayName": "Read requests", + "value_type": "INT64", + "metric_kind": "DELTA" + } + ], + "quota": { + "limits": [ + { + "name": "read-limit", + "metric": "read-requests", + "unit": "1/min/{project}", + "values": { + "STANDARD": 1000 + } + } + ] + } + } +} \ No newline at end of file diff --git a/examples/service_control/service_config_generated.json b/examples/service_control/service_config_generated.json new file mode 100644 index 000000000..7c1e3f882 --- /dev/null +++ b/examples/service_control/service_config_generated.json @@ -0,0 +1,797 @@ +{ + "apis": [ + { + "methods": [ + { + "name": "ListShelves", + "requestTypeUrl": "type.googleapis.com/google.protobuf.Empty", + "responseTypeUrl": "type.googleapis.com/google.protobuf.Value" + }, + { + "name": "CreateShelf", + "requestTypeUrl": "type.googleapis.com/CreateShelfRequest", + "responseTypeUrl": "type.googleapis.com/Shelf" + } + ], + "name": "1.bookstore_endpoints_apiproxy_231719_cloud_goog", + "sourceContext": { + "fileName": "openapi_swagger.json" + }, + "version": "1.0.0" + } + ], + "authentication": { + "rules": [ + { + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.ListShelves" + }, + { + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.CreateShelf" + } + ] + }, + "backend": { + "rules": [ + { + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.ListShelves" + }, + { + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.CreateShelf" + } + ] + }, + "configVersion": 3, + "control": { + "environment": "servicecontrol.googleapis.com" + }, + "documentation": { + "summary": "A simple Google Cloud Endpoints Bookstore API example." + }, + "endpoints": [ + { + "features": [ + "googleapis.com/endpoint/grpc-autobahn" + ], + "name": "bookstore.endpoints.apiproxy-231719.cloud.goog" + } + ], + "enums": [ + { + "enumvalue": [ + { + "name": "NULL_VALUE" + } + ], + "name": "google.protobuf.NullValue", + "sourceContext": { + "fileName": "struct.proto" + } + } + ], + "http": { + "rules": [ + { + "get": "/shelves", + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.ListShelves" + }, + { + "body": "shelf", + "post": "/shelves", + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.CreateShelf" + } + ] + }, + "id": "2019-12-17r2", + "legacy": { + "apiV1Name": "bookstore.endpoints.apiproxy-231719.cloud.goog" + }, + "logging": { + "producerDestinations": [ + { + "logs": [ + "endpoints_log" + ], + "monitoredResource": "api" + } + ] + }, + "logs": [ + { + "name": "endpoints_log" + } + ], + "metrics": [ + { + "displayName": "Read requests", + "metricKind": "DELTA", + "name": "read-requests", + "type": "read-requests", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/protocol" + }, + { + "key": "/response_code" + }, + { + "key": "/response_code_class" + }, + { + "key": "/status_code" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/request_count", + "type": "serviceruntime.googleapis.com/api/consumer/request_count", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/error_type" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/error_count", + "type": "serviceruntime.googleapis.com/api/consumer/error_count", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/total_latencies", + "type": "serviceruntime.googleapis.com/api/consumer/total_latencies", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/protocol" + }, + { + "key": "/response_code" + }, + { + "key": "/response_code_class" + }, + { + "key": "/status_code" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/request_count", + "type": "serviceruntime.googleapis.com/api/producer/request_count", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/error_type" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/error_count", + "type": "serviceruntime.googleapis.com/api/producer/error_count", + "valueType": "INT64" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/total_latencies", + "type": "serviceruntime.googleapis.com/api/producer/total_latencies", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/end_user" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user", + "type": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/end_user_country" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user_country", + "type": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user_country", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/referer" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_referer", + "type": "serviceruntime.googleapis.com/api/consumer/top_request_count_by_referer", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/protocol" + }, + { + "key": "/response_code" + }, + { + "key": "/consumer_id" + }, + { + "key": "/status_code" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/top_request_count_by_consumer", + "type": "serviceruntime.googleapis.com/api/producer/top_request_count_by_consumer", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/quota_group_name" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/quota_used_count", + "type": "serviceruntime.googleapis.com/api/consumer/quota_used_count", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/request_overhead_latencies", + "type": "serviceruntime.googleapis.com/api/consumer/request_overhead_latencies", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/backend_latencies", + "type": "serviceruntime.googleapis.com/api/consumer/backend_latencies", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/request_sizes", + "type": "serviceruntime.googleapis.com/api/consumer/request_sizes", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/response_sizes", + "type": "serviceruntime.googleapis.com/api/consumer/response_sizes", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/request_overhead_latencies", + "type": "serviceruntime.googleapis.com/api/producer/request_overhead_latencies", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/backend_latencies", + "type": "serviceruntime.googleapis.com/api/producer/backend_latencies", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/request_sizes", + "type": "serviceruntime.googleapis.com/api/producer/request_sizes", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/response_sizes", + "type": "serviceruntime.googleapis.com/api/producer/response_sizes", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/consumer_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/top_request_sizes_by_consumer", + "type": "serviceruntime.googleapis.com/api/producer/top_request_sizes_by_consumer", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/consumer_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/top_response_sizes_by_consumer", + "type": "serviceruntime.googleapis.com/api/producer/top_response_sizes_by_consumer", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/protocol" + }, + { + "key": "/response_code" + }, + { + "key": "/response_code_class" + }, + { + "key": "/status_code" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/request_count", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/request_count", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/error_type" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/error_count", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/error_count", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/total_latencies", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/total_latencies", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + }, + { + "key": "/quota_group_name" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/quota_used_count", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/quota_used_count", + "valueType": "INT64" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/request_overhead_latencies", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/request_overhead_latencies", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/backend_latencies", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/backend_latencies", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/request_sizes", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/request_sizes", + "valueType": "DISTRIBUTION" + }, + { + "labels": [ + { + "key": "/credential_id" + } + ], + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/by_consumer/response_sizes", + "type": "serviceruntime.googleapis.com/api/producer/by_consumer/response_sizes", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/streaming_request_message_counts", + "type": "serviceruntime.googleapis.com/api/producer/streaming_request_message_counts", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/streaming_response_message_counts", + "type": "serviceruntime.googleapis.com/api/producer/streaming_response_message_counts", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/streaming_request_message_counts", + "type": "serviceruntime.googleapis.com/api/consumer/streaming_request_message_counts", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/streaming_response_message_counts", + "type": "serviceruntime.googleapis.com/api/consumer/streaming_response_message_counts", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/streaming_durations", + "type": "serviceruntime.googleapis.com/api/producer/streaming_durations", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/streaming_durations", + "type": "serviceruntime.googleapis.com/api/consumer/streaming_durations", + "valueType": "DISTRIBUTION" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/request_bytes", + "type": "serviceruntime.googleapis.com/api/producer/request_bytes", + "valueType": "INT64" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/producer/response_bytes", + "type": "serviceruntime.googleapis.com/api/producer/response_bytes", + "valueType": "INT64" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/request_bytes", + "type": "serviceruntime.googleapis.com/api/consumer/request_bytes", + "valueType": "INT64" + }, + { + "metricKind": "DELTA", + "name": "serviceruntime.googleapis.com/api/consumer/response_bytes", + "type": "serviceruntime.googleapis.com/api/consumer/response_bytes", + "valueType": "INT64" + } + ], + "monitoredResources": [ + { + "labels": [ + { + "key": "cloud.googleapis.com/location" + }, + { + "key": "cloud.googleapis.com/uid" + }, + { + "key": "serviceruntime.googleapis.com/api_version" + }, + { + "key": "serviceruntime.googleapis.com/api_method" + }, + { + "key": "serviceruntime.googleapis.com/consumer_project" + }, + { + "key": "cloud.googleapis.com/project" + }, + { + "key": "cloud.googleapis.com/service" + } + ], + "type": "api" + } + ], + "monitoring": { + "consumerDestinations": [ + { + "metrics": [ + "serviceruntime.googleapis.com/api/consumer/request_count", + "serviceruntime.googleapis.com/api/consumer/error_count", + "serviceruntime.googleapis.com/api/consumer/quota_used_count", + "serviceruntime.googleapis.com/api/consumer/total_latencies", + "serviceruntime.googleapis.com/api/consumer/request_overhead_latencies", + "serviceruntime.googleapis.com/api/consumer/backend_latencies", + "serviceruntime.googleapis.com/api/consumer/request_sizes", + "serviceruntime.googleapis.com/api/consumer/response_sizes", + "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user", + "serviceruntime.googleapis.com/api/consumer/top_request_count_by_end_user_country", + "serviceruntime.googleapis.com/api/consumer/top_request_count_by_referer", + "serviceruntime.googleapis.com/api/consumer/streaming_request_message_counts", + "serviceruntime.googleapis.com/api/consumer/streaming_response_message_counts", + "serviceruntime.googleapis.com/api/consumer/streaming_durations", + "serviceruntime.googleapis.com/api/consumer/request_bytes", + "serviceruntime.googleapis.com/api/consumer/response_bytes" + ], + "monitoredResource": "api" + } + ], + "producerDestinations": [ + { + "metrics": [ + "serviceruntime.googleapis.com/api/producer/request_count", + "serviceruntime.googleapis.com/api/producer/error_count", + "serviceruntime.googleapis.com/api/producer/total_latencies", + "serviceruntime.googleapis.com/api/producer/request_overhead_latencies", + "serviceruntime.googleapis.com/api/producer/backend_latencies", + "serviceruntime.googleapis.com/api/producer/request_sizes", + "serviceruntime.googleapis.com/api/producer/response_sizes", + "serviceruntime.googleapis.com/api/producer/top_request_count_by_consumer", + "serviceruntime.googleapis.com/api/producer/top_request_sizes_by_consumer", + "serviceruntime.googleapis.com/api/producer/top_response_sizes_by_consumer", + "serviceruntime.googleapis.com/api/producer/streaming_request_message_counts", + "serviceruntime.googleapis.com/api/producer/streaming_response_message_counts", + "serviceruntime.googleapis.com/api/producer/streaming_durations", + "serviceruntime.googleapis.com/api/producer/request_bytes", + "serviceruntime.googleapis.com/api/producer/response_bytes", + "serviceruntime.googleapis.com/api/producer/by_consumer/request_count", + "serviceruntime.googleapis.com/api/producer/by_consumer/error_count", + "serviceruntime.googleapis.com/api/producer/by_consumer/total_latencies", + "serviceruntime.googleapis.com/api/producer/by_consumer/quota_used_count", + "serviceruntime.googleapis.com/api/producer/by_consumer/request_overhead_latencies", + "serviceruntime.googleapis.com/api/producer/by_consumer/backend_latencies", + "serviceruntime.googleapis.com/api/producer/by_consumer/request_sizes", + "serviceruntime.googleapis.com/api/producer/by_consumer/response_sizes" + ], + "monitoredResource": "api" + } + ] + }, + "name": "bookstore.endpoints.apiproxy-231719.cloud.goog", + "producerProjectId": "apiproxy-231719", + "quota": { + "limits": [ + { + "metric": "read-requests", + "name": "read-limit", + "unit": "1/min/{project}", + "values": { + "STANDARD": "1000" + } + } + ], + "metricRules": [ + { + "metricCosts": { + "read-requests": "1" + }, + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.ListShelves" + } + ] + }, + "systemParameters": {}, + "title": "Bookstore", + "types": [ + { + "fields": [ + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "name", + "kind": "TYPE_STRING", + "name": "name", + "number": 1 + }, + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "theme", + "kind": "TYPE_STRING", + "name": "theme", + "number": 2 + } + ], + "name": "Shelf", + "sourceContext": {} + }, + { + "fields": [ + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "shelf", + "kind": "TYPE_MESSAGE", + "name": "shelf", + "number": 1, + "typeUrl": "type.googleapis.com/Shelf" + } + ], + "name": "CreateShelfRequest", + "sourceContext": {} + }, + { + "fields": [ + { + "cardinality": "CARDINALITY_REPEATED", + "jsonName": "values", + "kind": "TYPE_MESSAGE", + "name": "values", + "number": 1, + "typeUrl": "type.googleapis.com/google.protobuf.Value" + } + ], + "name": "google.protobuf.ListValue", + "sourceContext": { + "fileName": "struct.proto" + } + }, + { + "fields": [ + { + "cardinality": "CARDINALITY_REPEATED", + "jsonName": "fields", + "kind": "TYPE_MESSAGE", + "name": "fields", + "number": 1, + "typeUrl": "type.googleapis.com/google.protobuf.Struct.FieldsEntry" + } + ], + "name": "google.protobuf.Struct", + "sourceContext": { + "fileName": "struct.proto" + } + }, + { + "fields": [ + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "key", + "kind": "TYPE_STRING", + "name": "key", + "number": 1 + }, + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "value", + "kind": "TYPE_MESSAGE", + "name": "value", + "number": 2, + "typeUrl": "type.googleapis.com/google.protobuf.Value" + } + ], + "name": "google.protobuf.Struct.FieldsEntry", + "sourceContext": { + "fileName": "struct.proto" + } + }, + { + "name": "google.protobuf.Empty", + "sourceContext": { + "fileName": "struct.proto" + } + }, + { + "fields": [ + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "nullValue", + "kind": "TYPE_ENUM", + "name": "null_value", + "number": 1, + "typeUrl": "type.googleapis.com/google.protobuf.NullValue" + }, + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "numberValue", + "kind": "TYPE_DOUBLE", + "name": "number_value", + "number": 2 + }, + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "stringValue", + "kind": "TYPE_STRING", + "name": "string_value", + "number": 3 + }, + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "boolValue", + "kind": "TYPE_BOOL", + "name": "bool_value", + "number": 4 + }, + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "structValue", + "kind": "TYPE_MESSAGE", + "name": "struct_value", + "number": 5, + "typeUrl": "type.googleapis.com/google.protobuf.Struct" + }, + { + "cardinality": "CARDINALITY_OPTIONAL", + "jsonName": "listValue", + "kind": "TYPE_MESSAGE", + "name": "list_value", + "number": 6, + "typeUrl": "type.googleapis.com/google.protobuf.ListValue" + } + ], + "name": "google.protobuf.Value", + "sourceContext": { + "fileName": "struct.proto" + } + } + ], + "usage": { + "rules": [ + { + "allowUnregisteredCalls": true, + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.ListShelves" + }, + { + "selector": "1.bookstore_endpoints_apiproxy_231719_cloud_goog.CreateShelf" + } + ] + } +} diff --git a/src/go/bootstrap/static/bootstrap_test.go b/src/go/bootstrap/static/bootstrap_test.go index 93a9d367f..e1b51bb09 100644 --- a/src/go/bootstrap/static/bootstrap_test.go +++ b/src/go/bootstrap/static/bootstrap_test.go @@ -15,64 +15,103 @@ package static import ( - "fmt" - "strings" + "bytes" + "flag" + "io/ioutil" "testing" - "github.com/GoogleCloudPlatform/esp-v2/src/go/bootstrap/static/testdata" - "github.com/GoogleCloudPlatform/esp-v2/src/go/options" + "github.com/GoogleCloudPlatform/esp-v2/src/go/configmanager/flags" "github.com/GoogleCloudPlatform/esp-v2/src/go/util" + "github.com/GoogleCloudPlatform/esp-v2/tests/env/platform" "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/proto" bootstrappb "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v2" + confpb "google.golang.org/genproto/googleapis/api/serviceconfig" ) -func TestServiceToBootstrapConfig(t *testing.T) { - opts := options.DefaultConfigGeneratorOptions() - opts.BackendProtocol = "HTTP1" - opts.DisableTracing = true - opts.EnableAdmin = true - - // Function under test - gotBootstrap, err := ServiceToBootstrapConfig(testdata.FakeBookstoreConfig, testdata.FakeConfigID, opts) - if err != nil { - t.Fatal(err) - } +var ( + FakeConfigID = "2019-12-16r0" +) - if err := verifyBootstrapConfig(gotBootstrap, testdata.ExpectedBookstoreEnvoyConfig); err != nil { - t.Fatalf("Normal ServiceToBootstrapConfig error: %v", err) +func TestServiceToBootstrapConfig(t *testing.T) { + testData := []struct { + desc string + flags map[string]string + serviceConfigPath string + envoyConfigPath string + want *bootstrappb.Admin + }{ + { + desc: "envoy config with service control, no tracing", + flags: map[string]string{ + "backend_protocol": "http1", + "disable_tracing": "true", + }, + serviceConfigPath: platform.GetFilePath(platform.ScServiceConfig), + envoyConfigPath: platform.GetFilePath(platform.ScEnvoyConfig), + }, } -} -func verifyBootstrapConfig(got *bootstrappb.Bootstrap, want string) error { - unmarshaler := &jsonpb.Unmarshaler{ - AnyResolver: util.Resolver, - } + for _, tc := range testData { + for key, value := range tc.flags { + flag.Set(key, value) + } - // Convert want string to a proto to compare with got - wantReader := strings.NewReader(want) - wantBootstrap := &bootstrappb.Bootstrap{} - err := unmarshaler.Unmarshal(wantReader, wantBootstrap) - if err != nil { - return err - } + configBytes, err := ioutil.ReadFile(tc.serviceConfigPath) + if err != nil { + t.Fatalf("ReadFile failed, got %v", err) + } + unmarshaler := &jsonpb.Unmarshaler{ + AnyResolver: util.Resolver, + AllowUnknownFields: true, + } - if !proto.Equal(got, wantBootstrap) { - // Marshal both protos back to json-strings to pretty print them - marshaler := &jsonpb.Marshaler{ - AnyResolver: util.Resolver, + var s confpb.Service + if err := unmarshaler.Unmarshal(bytes.NewBuffer(configBytes), &s); err != nil { + t.Fatalf("Unmarshal() returned error %v, want nil", err) } - gotString, err := marshaler.MarshalToString(got) + + opts := flags.EnvoyConfigOptionsFromFlags() + + // Function under test + gotBootstrap, err := ServiceToBootstrapConfig(&s, FakeConfigID, opts) if err != nil { - return err + t.Fatal(err) } - wantString, err := marshaler.MarshalToString(wantBootstrap) + + envoyConfig, err := ioutil.ReadFile(tc.envoyConfigPath) if err != nil { - return err + t.Fatalf("ReadFile failed, got %v", err) + } + + var expectedBootstrap bootstrappb.Bootstrap + if err := unmarshaler.Unmarshal(bytes.NewBuffer(envoyConfig), &expectedBootstrap); err != nil { + t.Fatalf("Unmarshal() returned error %v, want nil", err) + } + + if !proto.Equal(gotBootstrap, &expectedBootstrap) { + gotString, err := bootstrapToJson(gotBootstrap) + if err != nil { + t.Fatal(err) + } + wantString, err := bootstrapToJson(&expectedBootstrap) + if err != nil { + t.Fatal(err) + } + t.Errorf("\ngot : %v, \nwant: %v", gotString, wantString) } - return fmt.Errorf("\ngot : %v, \nwant: %v", gotString, wantString) } +} - return nil +func bootstrapToJson(protoMsg *bootstrappb.Bootstrap) (string, error) { + // Marshal both protos back to json-strings to pretty print them + marshaler := &jsonpb.Marshaler{ + AnyResolver: util.Resolver, + } + gotString, err := marshaler.MarshalToString(protoMsg) + if err != nil { + return "", err + } + return gotString, nil } diff --git a/src/go/bootstrap/static/testdata/fake_service_config.go b/src/go/bootstrap/static/testdata/fake_service_config.go deleted file mode 100644 index 30af8109a..000000000 --- a/src/go/bootstrap/static/testdata/fake_service_config.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2019 Google LLC -// -// 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. - -package testdata - -import ( - annotationspb "google.golang.org/genproto/googleapis/api/annotations" - confpb "google.golang.org/genproto/googleapis/api/serviceconfig" - apipb "google.golang.org/genproto/protobuf/api" -) - -var ( - FakeConfigID = "2019-06-25r0" - - FakeBookstoreConfig = &confpb.Service{ - Name: "bookstore.endpoints.cloudesf-testing.cloud.goog", - Title: "Bookstore gRPC API", - ProducerProjectId: "producer project", - Apis: []*apipb.Api{ - { - Name: "endpoints.examples.bookstore.Bookstore", - Methods: []*apipb.Method{ - { - Name: "ListShelves", - RequestTypeUrl: "type.googleapis.com/google.protobuf.Empty", - ResponseTypeUrl: "type.googleapis.com/endpoints.examples.bookstore.ListShelvesResponse", - }, - }, - }, - }, - Http: &annotationspb.Http{ - Rules: []*annotationspb.HttpRule{ - { - Selector: "endpoints.examples.bookstore.Bookstore.ListShelves", - Pattern: &annotationspb.HttpRule_Get{ - Get: "/v1/shelves", - }, - }, - }, - }, - Authentication: &confpb.Authentication{ - Rules: []*confpb.AuthenticationRule{ - { - Selector: "endpoints.examples.bookstore.Bookstore.ListShelves", - Requirements: []*confpb.AuthRequirement{ - { - ProviderId: "google_service_account", - Audiences: "bookstore_test_client.cloud.goog", - }, - }, - }, - }, - }, - Usage: &confpb.Usage{ - Rules: []*confpb.UsageRule{}, - }, - Control: &confpb.Control{ - Environment: "servicecontrol.googleapis.com", - }, - } - - ExpectedBookstoreEnvoyConfig = ` -{ - "node":{ - "id":"ESPv2", - "cluster":"ESPv2_cluster" - }, - "staticResources":{ - "listeners":[ - { - "address":{ - "socketAddress":{ - "address":"0.0.0.0", - "portValue":8080 - } - }, - "filterChains":[ - { - "filters":[ - { - "name":"envoy.http_connection_manager", - "typedConfig":{ - "@type":"type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager", - "statPrefix":"ingress_http", - "routeConfig":{ - "name":"local_route", - "virtualHosts":[ - { - "name":"backend", - "domains":[ - "*" - ], - "routes":[ - { - "match":{ - "prefix":"/" - }, - "route":{ - "cluster":"endpoints.examples.bookstore.Bookstore" - } - } - ] - } - ] - }, - "httpFilters":[ - { - "name":"envoy.filters.http.path_matcher", - "typedConfig":{ - "@type":"type.googleapis.com/google.api.envoy.http.path_matcher.FilterConfig", - "rules":[ - { - "pattern":{ - "uriTemplate":"/v1/shelves", - "httpMethod":"GET" - }, - "operation":"endpoints.examples.bookstore.Bookstore.ListShelves" - } - ] - } - }, - { - "name":"envoy.filters.http.service_control", - "typedConfig":{ - "@type":"type.googleapis.com/google.api.envoy.http.service_control.FilterConfig", - "services":[ - { - "serviceName":"bookstore.endpoints.cloudesf-testing.cloud.goog", - "serviceConfigId":"2019-06-25r0", - "producerProjectId":"producer project", - "serviceConfig":{ - "@type":"type.googleapis.com/google.api.Service" - }, - "backendProtocol":"http1", - "jwtPayloadMetadataName":"jwt_payloads" - } - ], - "requirements":[ - { - "serviceName":"bookstore.endpoints.cloudesf-testing.cloud.goog", - "operationName":"endpoints.examples.bookstore.Bookstore.ListShelves" - } - ], - "accessToken":{ - "remoteToken":{ - "uri":"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token", - "cluster":"metadata-cluster", - "timeout":"5s" - } - }, - "scCallingConfig":{ - "networkFailOpen":true - }, - "serviceControlUri":{ - "uri":"https://servicecontrol.googleapis.com/v1/services/", - "cluster":"service-control-cluster", - "timeout":"5s" - } - } - }, - { - "name":"envoy.router", - "typedConfig":{ - "@type":"type.googleapis.com/envoy.config.filter.http.router.v2.Router" - } - } - ], - "useRemoteAddress":false, - "xffNumTrustedHops":2 - } - } - ] - } - ] - } - ], - "clusters":[ - { - "name":"endpoints.examples.bookstore.Bookstore", - "type":"STRICT_DNS", - "connectTimeout":"20s", - "loadAssignment":{ - "clusterName":"127.0.0.1", - "endpoints":[ - { - "lbEndpoints":[ - { - "endpoint":{ - "address":{ - "socketAddress":{ - "address":"127.0.0.1", - "portValue":8082 - } - } - } - } - ] - } - ] - } - }, - { - "name":"metadata-cluster", - "type":"STRICT_DNS", - "connectTimeout":"20s", - "loadAssignment":{ - "clusterName":"169.254.169.254", - "endpoints":[ - { - "lbEndpoints":[ - { - "endpoint":{ - "address":{ - "socketAddress":{ - "address":"169.254.169.254", - "portValue":80 - } - } - } - } - ] - } - ] - } - }, - { - "name":"service-control-cluster", - "type":"LOGICAL_DNS", - "connectTimeout":"5s", - "loadAssignment":{ - "clusterName":"servicecontrol.googleapis.com", - "endpoints":[ - { - "lbEndpoints":[ - { - "endpoint":{ - "address":{ - "socketAddress":{ - "address":"servicecontrol.googleapis.com", - "portValue":443 - } - } - } - } - ] - } - ] - }, - "dnsLookupFamily":"V4_ONLY", - "transportSocket":{ - "name":"envoy.transport_sockets.tls", - "typedConfig":{ - "@type":"type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext", - "sni":"servicecontrol.googleapis.com", - "commonTlsContext": { - "validationContext": { - "trustedCa": { - "filename": "/etc/ssl/certs/ca-certificates.crt" - } - } - } - } - } - } - ] - }, - "admin":{ - "accessLogPath":"/dev/null", - "address":{ - "socketAddress":{ - "address":"0.0.0.0", - "portValue":8001 - } - } - } -} - ` -) diff --git a/src/go/configgenerator/listener_generator.go b/src/go/configgenerator/listener_generator.go index 8137cca53..b1602460b 100644 --- a/src/go/configgenerator/listener_generator.go +++ b/src/go/configgenerator/listener_generator.go @@ -68,7 +68,7 @@ func MakeListener(serviceInfo *sc.ServiceInfo) (*v2pb.Listener, error) { pathMathcherFilter := makePathMatcherFilter(serviceInfo) if pathMathcherFilter != nil { httpFilters = append(httpFilters, pathMathcherFilter) - glog.V(1).Infof("adding Path Matcher Filter config: %v", pathMathcherFilter) + glog.Infof("adding Path Matcher Filter config: %v", pathMathcherFilter) } // Add JWT Authn filter if needed. @@ -76,7 +76,7 @@ func MakeListener(serviceInfo *sc.ServiceInfo) (*v2pb.Listener, error) { jwtAuthnFilter := makeJwtAuthnFilter(serviceInfo) if jwtAuthnFilter != nil { httpFilters = append(httpFilters, jwtAuthnFilter) - glog.V(1).Infof("adding JWT Authn Filter config: %v", jwtAuthnFilter) + glog.Infof("adding JWT Authn Filter config: %v", jwtAuthnFilter) } } @@ -85,7 +85,7 @@ func MakeListener(serviceInfo *sc.ServiceInfo) (*v2pb.Listener, error) { serviceControlFilter := makeServiceControlFilter(serviceInfo) if serviceControlFilter != nil { httpFilters = append(httpFilters, serviceControlFilter) - glog.V(1).Infof("adding Service Control Filter config: %v", serviceControlFilter) + glog.Infof("adding Service Control Filter config: %v", serviceControlFilter) } } @@ -94,7 +94,7 @@ func MakeListener(serviceInfo *sc.ServiceInfo) (*v2pb.Listener, error) { transcoderFilter := makeTranscoderFilter(serviceInfo) if transcoderFilter != nil { httpFilters = append(httpFilters, transcoderFilter) - glog.V(1).Infof("adding Transcoder Filter config...") + glog.Infof("adding Transcoder Filter config...") } grpcWebFilter := &hcmpb.HttpFilter{ @@ -110,10 +110,10 @@ func MakeListener(serviceInfo *sc.ServiceInfo) (*v2pb.Listener, error) { } backendAuthFilter := makeBackendAuthFilter(serviceInfo) httpFilters = append(httpFilters, backendAuthFilter) - glog.V(1).Infof("adding Backend Auth Filter config: %v", backendAuthFilter) + glog.Infof("adding Backend Auth Filter config: %v", backendAuthFilter) backendRoutingFilter := makeBackendRoutingFilter(serviceInfo) httpFilters = append(httpFilters, backendRoutingFilter) - glog.V(1).Infof("adding Backend Routing Filter config: %v", backendRoutingFilter) + glog.Infof("adding Backend Routing Filter config: %v", backendRoutingFilter) } // Add Envoy Router filter so requests are routed upstream. @@ -583,6 +583,7 @@ func makeBackendRoutingFilter(serviceInfo *sc.ServiceInfo) *hcmpb.HttpFilter { Name: util.BackendRouting, ConfigType: &hcmpb.HttpFilter_TypedConfig{backendRoutingConfigStruct}, } + return backendRoutingFilter } diff --git a/src/go/configgenerator/route_generator.go b/src/go/configgenerator/route_generator.go index a1b8d6108..424b7b707 100644 --- a/src/go/configgenerator/route_generator.go +++ b/src/go/configgenerator/route_generator.go @@ -19,6 +19,7 @@ import ( "regexp" "github.com/GoogleCloudPlatform/esp-v2/src/go/configinfo" + "github.com/golang/glog" commonpb "github.com/GoogleCloudPlatform/esp-v2/src/go/proto/api/envoy/http/common" v2pb "github.com/envoyproxy/go-control-plane/envoy/api/v2" @@ -127,6 +128,7 @@ func makeDynamicRoutingConfig(serviceInfo *configinfo.ServiceInfo) ([]*routepb.R }, }, } + glog.Infof("Add Dynamic Routing configuration: %v", r) backendRoutes = append(backendRoutes, &r) } } diff --git a/src/go/configmanager/config_manager.go b/src/go/configmanager/config_manager.go index 24596dffd..d116ed1b3 100644 --- a/src/go/configmanager/config_manager.go +++ b/src/go/configmanager/config_manager.go @@ -123,7 +123,7 @@ func NewConfigManager(mf *metadata.MetadataFetcher, opts options.ConfigGenerator // Create secured http client with rootCertsPath. if serviceConfigFetcherClient, err = newServiceConfigFetcherClient(time.Duration(*commonflags.HttpRequestTimeoutS) * time.Second); err != nil { - return nil, fmt.Errorf(`failed to create https client to call ServiceManagement service`) + return nil, fmt.Errorf(`failed to create https client to call ServiceManagement service, got error: %v`, err) } if rolloutStrategy == util.ManagedRolloutStrategy { diff --git a/src/go/util/marshal.go b/src/go/util/marshal.go index 065ff122a..12802c038 100644 --- a/src/go/util/marshal.go +++ b/src/go/util/marshal.go @@ -28,6 +28,7 @@ import ( pmpb "github.com/GoogleCloudPlatform/esp-v2/src/go/proto/api/envoy/http/path_matcher" scpb "github.com/GoogleCloudPlatform/esp-v2/src/go/proto/api/envoy/http/service_control" authpb "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" + jwtpb "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/jwt_authn/v2alpha" routerpb "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/router/v2" hcmpb "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2" structpb "github.com/golang/protobuf/ptypes/struct" @@ -70,6 +71,8 @@ var Resolver = FuncResolver(func(url string) (proto.Message, error) { return new(wrapperspb.UInt32Value), nil case "type.googleapis.com/google.api.Service": return new(confpb.Service), nil + case "type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication": + return new(jwtpb.JwtAuthentication), nil case "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager": return new(hcmpb.HttpConnectionManager), nil case "type.googleapis.com/google.api.envoy.http.path_matcher.FilterConfig": diff --git a/tests/env/platform/files.go b/tests/env/platform/files.go index 8d9721c4b..ab3378963 100644 --- a/tests/env/platform/files.go +++ b/tests/env/platform/files.go @@ -44,6 +44,10 @@ const ( ProxyKey LogMetrics Version + + // Configurations + ScServiceConfig + ScEnvoyConfig ) var fileMap = map[RuntimeFile]string{ @@ -66,6 +70,8 @@ var fileMap = map[RuntimeFile]string{ ProxyKey: "../../env/testdata/proxy.key", LogMetrics: "../../env/testdata/logs_metrics.pb.txt", Version: "../../../VERSION", + ScServiceConfig: "../../../../examples/service_control/service_config_generated.json", + ScEnvoyConfig: "../../../../examples/service_control/envoy_config.json", } // Get the runtime file path for the specified file.