Strategies for Parsing Service Information In Cloud Foundry

Cloud Foundry’s service marketplace provides self-service access to a curated set of services that have their lifecycle automatically managed by the platform.  Part of the lifecycle of any service instance involved connecting that service to an application through a process called “binding”.  There are a variety of mechanisms that an application can use to lookup the information that a binding represents, and I’ll try to write up some of the best practices.

When a service instance is bound to an application in Cloud Foundry, the connection information and credentials for that service instance get exposed to the application as a block of JSON in an environment variable called VCAP_SERVICES.

The format of that JSON is similar to the following:
{
  "service-short-name": [
    {
      "binding_name": null,
      "credentials": {
        "service-specific-key1": "service-specific-value1",
        "service-specific-key2": "service-specific-value2"
      },
      "instance_name": "MyService",
      "label": "service-short-name",
      "name": "MyService",
      "plan": "service-plan-short-name",
      "provider": null,
      "syslog_drain_url": null,
      "tags": [
        "tag1",
        "tag2"
      ],
      "volume_mounts": []
    }
  ]
}

Use a Library

When looking up service information, you can simplify the code you have to write by using a library like Spring Cloud Connectors (https://cloud.spring.io/spring-cloud-connectors/) for both Spring and Non-Spring based Java applications or Steeltoe Connectors (http://steeltoe.io/docs/steeltoe-connectors/) for .NET and .NET Core based applications.  These libraries have strategies for looking up services that are resilient enough to work with many different service providers and types, and can even automate connections to User Provided Services (https://docs.cloudfoundry.org/devguide/services/user-provided.html) provided those service instances contain credentials that look similar to a brokered service of the same type.  These libraries also take care of building connection objects for these services and (if applicable) injects those objects into a Dependency Injection context to make it easy for your application to use those services.

Please, Use a Library

If you don't like the way that Spring Cloud Connectors, or Steeltoe Connectors does all the work for you, you could simple use a library that simply parses out the VCAP_SERVICES into for you.  Steeltoe Configuration (http://steeltoe.io/docs/steeltoe-configuration/#1-2-3-access-configuration-data) and Spring Boot (https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.html) will allow you to automatically parse out the VCAP_SERVICES into framework specific configuration information that you can reference easily via the name of the service that is bound to the application.

Manual Parsing

If you can't use these libraries for some reason, you need to be careful in the way you parse out the JSON data.  You want to ensure that you at least don't hard code the keys used to traverse the JSON service information that could change without your knowledge. You also want your logic to be flexible enough to use other service brokers than might provide the same core service, but implement that service in different ways.

To start with, all service bindings that get exposed to your application in Cloud Foundry are broken out by the short name of the service type used to provision them.  For example, the broker for Redis Labs Enterprise Cluster uses the short name "redislabs" for its service offering, Pivotal uses "p-redis" for its service offering, and all user provided services use the short name "user-provided-service".  Each of these service types could provide the information about a Redis endpoint for your application to talk to, so you want your application to be flexible enough to use any of these options to make it easier for the people who have to deploy and operate your application.  However, if you write your parsing code to only look for the key "p-redis" in the top level object your wouldn't find your service information when someone used a user provided service or RLEC service for your application.

One strategy to deal with this is to simply loop over all the top level keys, then to loop over each array that each of the top level keys is associated with, and then to look within each object in the array for information that your would have some control over.  One simple approach would be to look for a service bound to your application that had a particular name.

Pseudocode for that might look something like this:
var vcap_services = Get the value of the VCAP_SERVICES environment variable;
foreach ( service_type in vcap_service.keys ) {
  foreach ( service_instance in service_type[service_type] ) {
    if( service_instance.name == "MyService" ) {
      Extract service info and build connector to the service;
    }
  }
}

The Cloud Foundry Cloud Controller API v2.99.0+ (PAS 2.1+) includes the ability to allow the name that a service is exposed to an application as to be set at binding time.  In this way, an application could have a specific name it looks for like "MyService", but the actual service name could be something completely different.  When binding a service via the CF CLI, you could execute the following command to specify a "binding name":
cf bind-service MyAppName SomeServiceNamedStrangely --binding-name MyService
Doing the above sort of command when binding would allow your application to still lookup the service by the name MyService, no matter what the service instance was actually named.

A More Flexible Method

Naming is the hardest problem in Computer Science, so it is no surprise that there are conflicting interests that might come into play around how your services are named.  If you want to make your application able to handle the most flexible service possibilities, you could actually replicate the same pattern that the above libraries use to find bound services.  Both Steeltoe Connectors and Spring Cloud Connectors apply a strategy which involves looking at the service's tags or for the scheme of a URI in the service's credentials section.

The tags are a multi-valued array that is commonly supplied by the service broker for a particular service.  You can supply additional tags to add to the service on creation, or you can update a service with additional tags after you have created it.  You need to be using Cloud Foundry Cloud Controller API v2.104.0+ (PAS 2.2+) to have support for setting tags on user provided services.

For many services, you can express the connection information via a URI.  Steeltoe Connectors and Spring Cloud Connectors both look for the keys "uri" or "url" _or_ for those keys prefixed with the "scheme" that matches the service type (like "redisUri", or "redisUrl").  If your service type supports a URI style for connectors, this might be a great way to lookup your services no matter how they were provisioned.

Either of these lookup strategies could be much more robust than the previous methods as they rely on data that is in your direct control.  You could combine some of the above techniques as well.  For instance, you could look for URIs with specific schemes, but then disambiguate them with their service name if you had multiple services of the same type bound to your app.

Comments

Popular posts from this blog

Ghetto Cloud Foundry Home Lab

Using Snapshot Isolation with SQL Server and Hibernate

Fedora, Ant, and Optional Tasks