Unclear documentation for gRPC plugin setup

I’m trying to work through setting up a gRPC plugin, getting stuck at this point in the docs :
“To use a bundle plugin on one of your specified APIs, you must add the following parameter to its configuration block:”

Where might I find this “configuration block”? Is it the “Config Data” entry on the “Advanced Options” tab of the API designer? (i did try adding data here, to no effect)

thanks

Do I need to manually export an existing api JSON, and then import it again through the UI?
I tried simply copying an appropriate JSON in the tyk-gateway/apps directory, but it does not seem to be detected on the Dashboard as a new api (I did remove the id field)

This attribute is in the root of api definition, and you can also set it via UI in advanced section

I tried, but again, not sure the docs are correct here.
First - there IS no root api definition (on disk) - it’s hidden in a Mongodb table. Docs do not mention when the apps/myservice.json is used, if ever. Putting stuff there seems to have no effect.

Second, from your screenshot above “Plugin Bundle ID” is not actually an id.
If I put in that field “steve” - it will request a bundle from my http server called “steve”.
If I put in the id field “steve.zip” it will then load the file “steve.zip” from my bundle server.
So is this really an ID? Or is it actually a zip file name?

So if you are using the dashboard, the app folders are not used - those are for tyk running without the dashboard.

The way to interact with the data in the dashboards is via the UI or the dashboard API.

The bundle ID is just a filename reference, so you create your bundle - push it to an artefact server, then thebgateway will deploy it for you.

Leon said at the root of api definition, I.e. The root of the object of any given API Definition. Bundles are per API.

So I would like to use the gRPC method, but I’m wondering if we can setup gRPC using the custom_middleware property rather than the bundle download.
EX:
“custom_middleware”: {
“pre”: [
{
“name”: “MyPreMiddleware”,
“path”: “”,
“require_session”: false
}
],
“post”: [],
“driver”: “grpc”,
“id_extractor”: {
“extract_from”: “”,
“extract_with”: “”,
}
},
“custom_middleware_bundle”: “”,

vs.

    "custom_middleware_bundle": "gRPGManifest.zip",

HI,

You can - the JSON data in the manifest file can be put directly into the api definition into the custom middleware section, and the bundle ID can be left empty.

On mobile at the moment so can’t find a good link

M.

Thank you, can you recommend a good way to triage gRPC with Tyk? My gRPC server never seems to get called. I’ve included the API definition and the tyk.conf. Any insight would be great.
API Defintion:
{
“api_model”: {},
“api_definition”: {
“name”: “grpc-test”,
“slug”: “grpc-test”,
“use_keyless”: true,
“use_oauth2”: false,
“use_openid”: false,
“openid_options”: {
“providers”: [],
“segregate_by_client”: false
},
“oauth_meta”: {
“allowed_access_types”: [],
“allowed_authorize_types”: [],
“auth_login_redirect”: “”
},
“auth”: {
“use_param”: false,
“param_name”: “”,
“use_cookie”: false,
“cookie_name”: “”,
“auth_header_name”: “”,
“use_certificate”: false
},
“use_basic_auth”: false,
“use_mutual_tls_auth”: false,
“client_certificates”: [],
“upstream_certificates”: {},
“enable_jwt”: false,
“use_standard_auth”: false,
“enable_coprocess_auth”: true,
“jwt_signing_method”: “”,
“jwt_source”: “”,
“jwt_identity_base_field”: “”,
“jwt_client_base_field”: “”,
“jwt_policy_field_name”: “”,
“notifications”: {
“shared_secret”: “”,
“oauth_on_keychange_url”: “”
},
“enable_signature_checking”: false,
“hmac_allowed_clock_skew”: -1,
“base_identity_provided_by”: “”,
“definition”: {
“location”: “header”,
“key”: “x-api-version”
},
“version_data”: {
“not_versioned”: true,
“versions”: {
“Default”: {
“name”: “Default”,
“expires”: “”,
“paths”: {
“ignored”: [],
“white_list”: [],
“black_list”: []
},
“use_extended_paths”: true,
“extended_paths”: {
“virtual”: [
{
“response_function_name”: “up”,
“function_source_type”: “blob”,
“function_source_uri”: “ZnVuY3Rpb24gdXAgKHJlcXVlc3QsIHNlc3Npb24sIGNvbmZpZykgewoKICAgIHZhciByZXNwb25zZU9iamVjdCA9IHsKICAgICAgICBCb2R5OiAiVVAgYW5kIFJVTk5JTkciLAogICAgICAgIENvZGU6IDIwMAogICAgfQoKICAgIHJldHVybiBUeWtKc1Jlc3BvbnNlKHJlc3BvbnNlT2JqZWN0LCBzZXNzaW9uLm1ldGFfZGF0YSkgICAKfQ==”,
“path”: “up”,
“method”: “GET”,
“use_session”: false
}
]
},
“global_headers”: {},
“global_headers_remove”: [],
“global_size_limit”: 0,
“override_target”: “”
}
}
},
“uptime_tests”: {
“check_list”: [],
“config”: {
“expire_utime_after”: 0,
“service_discovery”: {
“use_discovery_service”: false,
“query_endpoint”: “”,
“use_nested_query”: false,
“parent_data_path”: “”,
“data_path”: “”,
“port_data_path”: “”,
“target_path”: “”,
“use_target_list”: false,
“cache_timeout”: 60,
“endpoint_returns_list”: false
},
“recheck_wait”: 0
}
},
“proxy”: {
“preserve_host_header”: false,
“listen_path”: “/grpc-test/”,
“target_url”: “http://httpbin.org/”,
“strip_listen_path”: true,
“enable_load_balancing”: false,
“target_list”: [],
“check_host_against_uptime_tests”: false,
“service_discovery”: {
“use_discovery_service”: false,
“query_endpoint”: “”,
“use_nested_query”: false,
“parent_data_path”: “”,
“data_path”: “”,
“port_data_path”: “”,
“target_path”: “”,
“use_target_list”: false,
“cache_timeout”: 0,
“endpoint_returns_list”: false
}
},
“disable_rate_limit”: false,
“disable_quota”: false,
“custom_middleware”: {
“pre”: [
{
“name”: “MyPreMiddleware”,
“path”: “”,
“require_session”: false
}
],
“post”: [],
“driver”: “grpc”,
“id_extractor”: {
“extract_from”: “”,
“extract_with”: “”
}
},
“custom_middleware_bundle”: “”,
“cache_options”: {
“cache_timeout”: 60,
“enable_cache”: false,
“cache_all_safe_requests”: false,
“cache_response_codes”: [],
“enable_upstream_cache_control”: false
},
“session_lifetime”: 0,
“active”: true,
“auth_provider”: {
“name”: “”,
“storage_engine”: “”,
“meta”: {}
},
“session_provider”: {
“name”: “”,
“storage_engine”: “”,
“meta”: {}
},
“event_handlers”: {
“events”: {}
},
“enable_batch_request_support”: false,
“enable_ip_whitelisting”: false,
“allowed_ips”: [],
“dont_set_quota_on_create”: false,
“expire_analytics_after”: 0,
“response_processors”: [],
“CORS”: {
“enable”: false,
“allowed_origins”: [],
“allowed_methods”: [],
“allowed_headers”: [],
“exposed_headers”: [],
“allow_credentials”: false,
“max_age”: 24,
“options_passthrough”: false,
“debug”: false
},
“domain”: “”,
“do_not_track”: false,
“tags”: [],
“enable_context_vars”: false,
“config_data”: {},
“tag_headers”: [],
“global_rate_limit”: {
“rate”: 0,
“per”: 0
},
“strip_auth_data”: false
},
“hook_references”: [],
“is_site”: false,
“sort_by”: 0
}

tyk.conf:
{
“listen_address”: “”,
“listen_port”: 8080,
“secret”: “352d20ee67be67f6340b4c0605b044b7”,
“node_secret”: “352d20ee67be67f6340b4c0605b044b7”,
“template_path”: “/opt/tyk-gateway/templates”,
“middleware_path”: “/opt/tyk-gateway/middleware”,
“tyk_js_path”: “/opt/tyk-gateway/js/tyk.js”,
“use_db_app_configs”: true,
“db_app_conf_options”: {
“connection_string”: “”,
“node_is_segmented”: false,
“tags”: []
},
“app_path”: “/opt/tyk-gateway/apps/”,
“storage”: {
“type”: “redis”,
“host”: “tyk-redis”,
“port”: 6379,
“username”: “”,
“password”: “”,
“database”: 0,
“optimisation_max_idle”: 100
},
“enable_analytics”: true,
“analytics_config”: {
“type”: “mongo”,
“csv_dir”: “/tmp”,
“mongo_url”: “”,
“mongo_db_name”: “”,
“mongo_collection”: “”,
“purge_delay”: -1,
“ignored_ips”: []
},
“health_check”: {
“enable_health_checks”: false,
“health_check_value_timeouts”: 60
},
“optimisations_use_async_session_write”: true,
“enable_non_transactional_rate_limiter”: true,
“enable_sentinel_rate_limiter”: false,
“allow_master_keys”: false,
“policies”: {
“policy_source”: “service”,
“policy_connection_string”: “http://tyk-dashboard:3000”,
“policy_record_name”: “tyk_policies”
},
“hash_keys”: false,
“close_connections”: true,
“http_server_options”: {
“enable_websockets”: true
},
“allow_insecure_configs”: true,
“enable_custom_domains”: true,
“enable_jsvm”: true,
“coprocess_options”: {
“enable_coprocess”: true,
“coprocess_grpc_server”: “tcp://grpc-middleware:5555”
},
“enable_bundle_downloader”: false,
“bundle_base_url”: “http://127.0.0.1:8000/”,
“global_session_lifetime”: 100,
“force_global_session_lifetime”: false,
“max_idle_connections_per_host”: 100,
“use_redis_log”: true,
“experimental_process_org_off_thread”: true
}

Hi @matt, what language are you using for the gRPC server?

You could use netstat to see if there’s an active connection to your gRPC server. The gRPC driver we’re using (for Go) it’s pretty verbose so if there’s an error you would see it on the log file, have you checked your logs?

OK, I turned on --debug logging now I’m seeing this
CP Driver not supported: grpc

I thought grpc was enabled as the conf file does enable the coprocessor. Is this a case of the binary? We’re using tyk-python binary

And - we’re using Java, I was able to connect with a Java client

Can you try the standard tyk binary? What Tyk version are you using?

Best.

I need to use a python middleware and grpc middleware concurrently for a while, is this possible? In the meantime I will try the standard binary

At the moment, gRPC builds are separate from Python ones, we have the following ticket in our plan:

It will enable gRPC by default (we currently use a special build instruction for this) and could make things easier to allow different types of plugins in the same build.

Ok I understand. Are you saying that at this time, we need to use tyk binary if using grpc plugins and tyk-python if using python plugins?

OK we have a connection to the grpc server, thanks the netstat command and using the plain tyk binary seemed to do the trick. These are some things that really should be defined better in the documentation.

1 Like

Ok I understand. Are you saying that at this time, we need to use tyk binary if using grpc plugins and tyk-python if using python plugins?

That’s right

OK, I guess we’d have to do our changeover all at once. I have one more question. I verified this in version 2.4.1 and 2.5.3, It seems only the “pre” hook ever gets called. I’ve verified everything else. Here is the api def, middleware and output…
API definition
{
“api_model”: {},
“api_definition”: {
“name”: “grpc-test”,
“slug”: “grpc-test”,
“use_keyless”: true,
“use_oauth2”: false,
“use_openid”: false,
“openid_options”: {
“providers”: [],
“segregate_by_client”: false
},
“oauth_meta”: {
“allowed_access_types”: [],
“allowed_authorize_types”: [],
“auth_login_redirect”: “”
},
“auth”: {
“use_param”: false,
“param_name”: “”,
“use_cookie”: false,
“cookie_name”: “”,
“auth_header_name”: “”,
“use_certificate”: false
},
“use_basic_auth”: false,
“use_mutual_tls_auth”: false,
“enable_signature_checking”: false,
“definition”: {
“location”: “header”,
“key”: “x-api-version”
},
“version_data”: {
“not_versioned”: true,
“versions”: {
“Default”: {
“name”: “Default”,
“expires”: “”,
“paths”: {
“ignored”: [],
“white_list”: [],
“black_list”: []
},
“use_extended_paths”: true,
“extended_paths”: {
“virtual”: [
{
“response_function_name”: “up”,
“function_source_type”: “blob”,
“function_source_uri”: “ZnVuY3Rpb24gdXAgKHJlcXVlc3QsIHNlc3Npb24sIGNvbmZpZykgewoKICAgIGZvciAodmFyIGhlYWRlciBpbiByZXF1ZXN0LkhlYWRlcnMpewogICAgICAgIGNvbnNvbGUubG9nKCJIZWFkZXIgIiArIGhlYWRlciArICI6ICIgKyByZXF1ZXN0LkhlYWRlcnNbaGVhZGVyXSk7CiAgICB9CgogICAgdmFyIHJlc3BvbnNlT2JqZWN0ID0gewogICAgICAgIEJvZHk6ICJVUCAiICsgSlNPTi5zdHJpbmdpZnkocmVxdWVzdC5IZWFkZXJzKSwKICAgICAgICBIZWFkZXJzOnsKICAgICAgICAgICAgInZpcnR1YWwtZW5kcG9pbnQtYWRkZWQiOiJVUCIKICAgICAgICB9LAogICAgICAgIENvZGU6IDIwMAogICAgfQoKICAgIHJldHVybiBUeWtKc1Jlc3BvbnNlKHJlc3BvbnNlT2JqZWN0LCBzZXNzaW9uLm1ldGFfZGF0YSkgICAKfQ==”,
“path”: “up”,
“method”: “GET”,
“use_session”: false
}
]
},
“global_headers”: {},
“global_headers_remove”: [],
“global_size_limit”: 0,
“override_target”: “”
}
}
},
“uptime_tests”: {
“check_list”: [],
“config”: {
“expire_utime_after”: 0,
“service_discovery”: {
“use_discovery_service”: false,
“query_endpoint”: “”,
“use_nested_query”: false,
“parent_data_path”: “”,
“data_path”: “”,
“port_data_path”: “”,
“target_path”: “”,
“use_target_list”: false,
“cache_timeout”: 60,
“endpoint_returns_list”: false
},
“recheck_wait”: 0
}
},
“proxy”: {
“preserve_host_header”: false,
“listen_path”: “/grpc-test/”,
“target_url”: “http://httpbin.org/”,
“strip_listen_path”: true,
“enable_load_balancing”: false,
“target_list”: [],
“check_host_against_uptime_tests”: false,
“service_discovery”: {
“use_discovery_service”: false,
“query_endpoint”: “”,
“use_nested_query”: false,
“parent_data_path”: “”,
“data_path”: “”,
“port_data_path”: “”,
“target_path”: “”,
“use_target_list”: false,
“cache_timeout”: 0,
“endpoint_returns_list”: false
}
},
“disable_rate_limit”: false,
“disable_quota”: false,
“enable_coprocess_auth”: true,
“custom_middleware”: {
“pre”: [
{
“name”: “MyPreMiddleware”
}
],
“post”: [
{
“name”: “MyPostMiddleware”
}
],
“auth_check”: {
“name”: “MyAuthCheck”
},
“driver”: “grpc”
},
“custom_middleware_bundle”: “”,
“cache_options”: {
“cache_timeout”: 60,
“enable_cache”: false,
“cache_all_safe_requests”: false,
“cache_response_codes”: [],
“enable_upstream_cache_control”: false
},
“session_lifetime”: 0,
“active”: true,
“enable_batch_request_support”: false,
“enable_ip_whitelisting”: false,
“allowed_ips”: [],
“dont_set_quota_on_create”: false,
“expire_analytics_after”: 0,
“response_processors”: [],
“CORS”: {
“enable”: false,
“allowed_origins”: [],
“allowed_methods”: [],
“allowed_headers”: [],
“exposed_headers”: [],
“allow_credentials”: false,
“max_age”: 24,
“options_passthrough”: false,
“debug”: false
},
“domain”: “”,
“do_not_track”: false,
“tags”: [],
“enable_context_vars”: false,
“config_data”: {},
“tag_headers”: [],
“global_rate_limit”: {
“rate”: 0,
“per”: 0
},
“strip_auth_data”: false
},
“hook_references”: [],
“is_site”: false,
“sort_by”: 0
}

And the Middleware
package com.testorg.testplugin;

import coprocess.DispatcherGrpc;
import coprocess.CoprocessObject;

public class PluginDispatcher extends DispatcherGrpc.DispatcherImplBase {

@Override
public void dispatch(CoprocessObject.Object request,
        io.grpc.stub.StreamObserver<CoprocessObject.Object> responseObserver) {
    CoprocessObject.Object modifiedRequest = null;

    switch (request.getHookName()) {
        case "MyPreMiddleware":
            modifiedRequest = MyPreHook(request);
            responseObserver.onNext(modifiedRequest);
            break;
        case "MyPostMiddleware":
            modifiedRequest = MyPostHook(request);
            responseObserver.onNext(modifiedRequest);
            break;
        case "MyAuthCheck":
            modifiedRequest = MyAuthCheck(request);
            responseObserver.onNext(modifiedRequest);
            break;
        default:
        // Do nothing, the hook name isn't implemented!
    }

    responseObserver.onCompleted();
}

CoprocessObject.Object MyPreHook(CoprocessObject.Object request) {
    CoprocessObject.Object.Builder builder = request.toBuilder();
    builder.getRequestBuilder().putHeaders("Pre", "customvalue");
    builder.getRequestBuilder().putSetHeaders("Pre1", "customvalue");
    return builder.build();
}
CoprocessObject.Object MyPostHook(CoprocessObject.Object request) {
    CoprocessObject.Object.Builder builder = request.toBuilder();
    builder.getRequestBuilder().putHeaders("Post", "customvalue");
    builder.getRequestBuilder().putSetHeaders("Post1", "customvalue");
    return builder.build();
}
CoprocessObject.Object MyAuthCheck(CoprocessObject.Object request) {
    CoprocessObject.Object.Builder builder = request.toBuilder();
    builder.getRequestBuilder().putSetHeaders("Auth-Header", "grpcset");
    return builder.build();
}

}

and the output (its a virtual endpoint that just dumps headers):
{
“Accept”: [
/
],
“Accept-Encoding”: [
“gzip, deflate”
],
“Cache-Control”: [
“no-cache”
],
“Connection”: [
“close”
],
“Postman-Token”: [
“d6d3921e-5194-4b4f-8304-cea5fc7f0bf5”
],
“Pre1”: [
“customvalue”
],
“User-Agent”: [
“PostmanRuntime/7.1.1”
],
“X-Forwarded-For”: [
“192.168.64.1”
],
“X-Forwarded-Host”: [
“tyk-gateway.minikube.local”
],
“X-Forwarded-Port”: [
“80”
],
“X-Forwarded-Proto”: [
“http”
],
“X-Original-Uri”: [
“/grpc-test/up”
],
“X-Real-Ip”: [
“192.168.64.1”
],
“X-Scheme”: [
“http”
]
}
You see that only the “pre” hook is deployed. I can’t see anything else wrong and I know grpc is working since the pre hook is executed. Why not the post and auth_hook???