Transformation

Alter a request before matching and routing, such as with an Inja header template. You might transform the request or response to match a different routing destination based on the transformed content.

You can also apply special transformations, such as via Inja templates. With Inja, you can write loops, conditional logic, and other functions to transform requests and responses.

For more information, see the following resources.

Before you begin

This guide assumes that you use the same names for components like clusters, workspaces, and namespaces as in the getting started. If you have different names, make sure to update the sample configuration files in this guide.
  1. Complete the multicluster getting started guide to set up the following testing environment.
    • Three clusters along with environment variables for the clusters and their Kubernetes contexts.
    • The Gloo Platform CLI, meshctl, along with other CLI tools such as kubectl and istioctl.
    • The Gloo management server in the management cluster, and the Gloo agents in the workload clusters.
    • Istio installed in the workload clusters.
    • A simple Gloo workspace setup.
  2. Install Bookinfo and other sample apps.

Configure transformation policies

You can apply a transformation policy at the route level. For more information, see Apply policies.

When you attempt to apply multiple transformation policies, all policies are first sorted ascending by creation time, and then grouped by stage (preAuthz and postAuthz). For each stage, only the oldest policy is applied. All subsequent policies are ignored. Note that this behavior applies even if you specify the prioritizedPhase field in your transformation policy.

Review the following sample configuration files.

The following example is to inject a header with a simple Inja template into a request.

apiVersion: trafficcontrol.policy.gloo.solo.io/v2
kind: TransformationPolicy
metadata:
  annotations:
    cluster.solo.io/cluster: ""
  name: basic-auth
  namespace: bookinfo
spec:
  applyToRoutes:
  - route:
      labels:
        route: ratings
  config:
    request:
      injaTemplate:
        headers:
          foo:
            text: bar

Review the following table to understand this configuration. For more information, see the API docs.

Setting Description
spec.applyToRoutes Configure which routes to apply the policy to, by using labels. The label matches the app and the route from the route table. If omitted, the policy applies to all routes in the workspace.
config.request Transform the request before sending to the upstream service. The example adds a foo: bar header.

The following example is to inject a header with a simple Inja template into a response.

apiVersion: trafficcontrol.policy.gloo.solo.io/v2
kind: TransformationPolicy
metadata:
  annotations:
    cluster.solo.io/cluster: ""
  name: basic-auth
  namespace: bookinfo
spec:
  applyToRoutes:
  - route:
      labels:
        route: ratings
  config:
    response:
      injaTemplate:
        headers:
          foo:
            text: bar

Review the following table to understand this configuration. For more information, see the API docs.

Setting Description
spec.applyToRoutes Configure which routes to apply the policy to, by using labels. The label matches the app and the route from the route table. If omitted, the policy applies to all routes in the workspace.
config.response Transform the response before returning to the client. The example adds a foo: bar header.

You can use a replace_with_random function in a transformation policy to replace a string in a request and response with a random, unique value per request-response pair in the header, body, or metadata. For example, you might have a request with an ID, nonce, or other sanitized value that shares the value with other requests. You want to replace this value with a new, random value that is unique to the request so that you can build response-specific logic in your app.

  • The function is in the format: replace_with_random(source_string, pattern_to_replace_string).
  • The pattern_to_replace_string pattern is an exact match string.
  • The replaced value is a 128-bit, base64-encoded random number.

Review the following example Inja template.

apiVersion: trafficcontrol.policy.gloo.solo.io/v2
kind: TransformationPolicy
metadata:
  annotations:
    cluster.solo.io/cluster: ""
  name: transformation-one
  namespace: bookinfo
spec:
  applyToRoutes:
  - route:
      labels:
        route: ratings
  config:
    phase:
      postAuthz:
        priority: 10
    response:
      injaTemplate:
        advancedTemplates: false
        parseBodyBehavior: DontParse
        headers:
          baz:
            text: '{{ replace_with_random(header("baz"), "pattern-to-replace") }}'          
          foo:
            text: '{{ replace_with_random(header("x-foo"), "another-pattern-to-replace") }}'   
        body:
          text: ' {{ replace_with_random(body(), "pattern-to-replace") }}'

Review the following table to understand this configuration. For more information, see the API docs.

Setting Description
spec.applyToRoutes Configure which routes to apply the policy to, by using labels. The label matches the app and the route from the route table. If omitted, the policy applies to all routes in the workspace.
config.phase Set when to apply the transformation filter in the request chain, either before (preAuthz) or after (postAuthz) authorization. You can also set the priority if you have multiple policies in the same phase. The lowest numbered priority is run first. For more information, see Order of applied policies. This example sets postAuthz so that the policy can extract authorization information such as an external auth ID or JWT token.
config.response.injaTemplate The Inja template with header and body rules to use to transform data in the response that is received from the upstream service before returning the response to the client. To configure an Inja template to transform data before sending a request to an upstream, you can use config.request.injaTemplate.
advancedTemplates: false Set to false to use simple naming and dot notation (such as time.start) for the extraction rules in the template. For more information, see the Envoy transformation proto.
parseBodyBehavior: DontParse Set to DontParse to treat the response and request bodies as plain text, not JSON (ParseAsJson).
headers Configure the replacement rules for request headers. The example replaces the pattern-to-replace pattern in the baz header and the another-pattern-to-replace pattern in the x-foo header.
body Configure the replacement rules for the request body. The example replaces the pattern-to-replace pattern anywhere in the request body. The replacement value for the pattern-to-replace pattern in the body is the same as the replacement value in the baz header because the patterns match. However, the replacement value for the body differs from the foo header because the patterns do not match.

Verify transformation policies

  1. Log in to the reviews app and send a request to the ratings app. Make sure that a 200 HTTP response code is returned.

    Use the kubectl debug command to create an ephemeral curl container in the deployment. This way, the curl container inherits any permissions from the app that you want to test. If you don't run Kubernetes 1.23 or later, you can deploy a separate curl pod or manually add the curl container as shown in the other tab.

    kubectl --context ${REMOTE_CONTEXT1} -n bookinfo debug -i pods/$(kubectl get pod --context ${REMOTE_CONTEXT1} -l app=reviews -A -o jsonpath='{.items[0].metadata.name}') --image=curlimages/curl -- curl -v http://ratings:9080/ratings/1
    

    Example output:

    < HTTP/1.1 200 OK
    < content-type: application/json
    < date: Wed, 17 May 2023 14:45:24 GMT
    < x-envoy-upstream-service-time: 22
    < server: envoy
    < transfer-encoding: chunked
    

    Connection #0 to host ratings left intact {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}%

    Create a temporary curl pod in the bookinfo namespace, so that you can test the app setup. You can also use this method in Kubernetes 1.23 or later, but an ephemeral container might be simpler, as shown in the other tab.

    1. Create the curl pod.

      kubectl run -it -n bookinfo curl \
        --image=curlimages/curl:7.73.0 --rm  -- sh
      
    2. Send a request to the ratings app.

      curl -v http://ratings:9080/ratings/1
      

      Example output:

      < HTTP/1.1 200 OK
      < content-type: application/json
      < date: Wed, 17 May 2023 14:45:24 GMT
      < x-envoy-upstream-service-time: 22
      < server: envoy
      < transfer-encoding: chunked
      

      Connection #0 to host ratings left intact {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}%

    3. Exit the temporary pod. The pod deletes itself.

      exit
      

  2. Apply a transformation policy to the ratings app that adds the foo: bar response header.

    kubectl apply --context ${REMOTE_CONTEXT1} -f- <<EOF
    apiVersion: trafficcontrol.policy.gloo.solo.io/v2
    kind: TransformationPolicy
    metadata:
      annotations:
        cluster.solo.io/cluster: ""
      name: basic-auth
      namespace: bookinfo
    spec:
      applyToRoutes:
      - route:
          labels:
            route: ratings
      config:
        response:
          injaTemplate:
            headers:
              foo:
                text: bar
    EOF
    
  3. Create a route table for the ratings app.

    kubectl apply --context ${REMOTE_CONTEXT1} -f- <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: RouteTable
    metadata:
      name: ratings-rt
      namespace: bookinfo
    spec:
      hosts:
      - ratings
      http:
      - forwardTo:
          destinations:
          - ref:
              name: ratings
              namespace: bookinfo
              cluster: ${REMOTE_CLUSTER1}
        labels:
          route: ratings
      workloadSelectors:
      - {}
    EOF
    
  4. Send another request to the ratings app by using the command in step 1. Verify that you now see the foo: bar header in your response.

    Example output:

       < HTTP/1.1 200 OK
       < content-type: application/json
       < date: Wed, 17 May 2023 16:48:51 GMT
       < x-envoy-upstream-service-time: 2
       < server: envoy
       < foo: bar
       < transfer-encoding: chunked
    
       * Connection #0 to host ratings left intact
       {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}% 
       

  5. Optional: Clean up the resources that you created.

    kubectl -n bookinfo --context $REMOTE_CONTEXT1 delete transformationpolicy basic-auth
    kubectl -n bookinfo --context $REMOTE_CONTEXT1 delete routetable ratings-rt