Making Web Assembly a first-class citizen on Gloo Mesh Enterprise Beta

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

At Solo.io, we are very excited about Web Assembly as a way to extend an Envoy-based data plane in frameworks like API Gateways (Gloo) and Service Meshes (Istio, AppMesh, etc). Back in December 2019 we announced tooling to improve the experience of working with Web Assembly (Wasm) called WebAssembly Hub. Later in March 2020 we announced the evolution of that tooling to support Istio 1.5 Wasm extensions in collaboration with Google. Most recently, we announced an OCI-compatible spec for packaging and distributing Wasm modules.

It’s now time to accelerate and to make it possible for users to build, push and deploy their own Wasm filters on Istio for either North/South or East/West traffic.

We’ve just announced the availability of the Beta release of our Gloo Mesh Enterprise product, which includes Istio support, Role based API, a global admin Dashboard and Wasm support. In this Blog post I’ll focus on the Wasm support.

Develop

The main advantage of building a Wasm Envoy filter is that you can manipulate requests (and responses) exactly the way it makes sense for your specific use cases.

Perhaps you want to gather some metrics only when the request contain specific headers, or you want to enrich the request by getting information from another API, it doesn’t matter, you’re now free to do exactly what you want.

The first decision you need to take is to decide which SDK (so which language) you want to use. SDKs are currently available for C++, AssemblyScript, RUST and TinyGo.

Not all the languages can be compiled to WebAssembly and don’t expect that you’ll be able to import any external packages (like the Amazon SDK).

There are 2 main reasons why you won’t be able to do that:

  • The first one is that you’ll need to tell Envoy to send HTTP requests for you (if you need to get information from an API, for example).
  • The second one is that most of these languages are not supporting all the standard packages you expect. For example, TinyGo doesn’t have a JSON package and AssemblyScript doesn’t have a Regexp package.

So, here is my advice: determine what you want your filter to do, look at what kind of packages you’ll need (Regexp, …) and check which one of the language you already know is matching your requirements.

For example, if you want to manipulate the response headers with a regular expression and you have some experience with Golang, then you’ll probably chose TinyGo.

In this Blog post, I won’t focus on developing a filter, but on how to build, push and deploy filters.

The Gloo Mesh CLI, meshctl can be used to create the skeleton for you.

Let’s take a look at the help of the meshctl wasme option:

$ meshctl wasm
The interface for managing Gloo Mesh WASM filters

Usage:
  wasm [command]

Available Commands:
  build       Build a wasm image from the filter source directory.
  deploy      Deploy an Envoy WASM Filter to Istio Sidecar Proxies (Envoy).
  help        Help about any command
  init        Initialize a project directory for a new Envoy WASM Filter.
  list        List Envoy WASM Filters stored locally or published to webassemblyhub.io.
  login       Log in so you can push images to the remote server.
  pull        Pull wasm filters from remote registry
  push        Push a wasm filter to remote registry

The following command will create the skeleton to build a Wasm filter using AssemblyScript:

meshctl wasm init myfilter --language=assemblyscript

It will ask what platform you will run your filter on (because the SDK version can be different based on the ABI corresponding to the version of Envoy used by this Platform).

And it will create the following file structure under the directory you have indicated:

./package-lock.json
./.gitignore
./assembly
./assembly/index.ts
./assembly/tsconfig.json
./package.json
./runtime-config.json

The most interesting file is the index.ts one, where you’ll write the code corresponding to your filter:

export * from "@solo-io/proxy-runtime/proxy";
import { RootContext, Context, RootContextHelper, ContextHelper, registerRootContext, FilterHeadersStatusValues, stream_context } from "@solo-io/proxy-runtime";

class AddHeaderRoot extends RootContext {
  configuration : string;

  onConfigure(): bool {
    let conf_buffer = super.getConfiguration();
    let result = String.UTF8.decode(conf_buffer);
    this.configuration = result;
    return true;
  }

  createContext(): Context {
    return ContextHelper.wrap(new AddHeader(this));
  }
}

class AddHeader extends Context {
  root_context : AddHeaderRoot;
  constructor(root_context:AddHeaderRoot){
    super();
    this.root_context = root_context;
  }
  onResponseHeaders(a: u32): FilterHeadersStatusValues {
    const root_context = this.root_context;
    if (root_context.configuration == "") {
      stream_context.headers.response.add("hello", "world!");
    } else {
      stream_context.headers.response.add("hello", root_context.configuration);
    }
    return FilterHeadersStatusValues.Continue;
  }
}

registerRootContext(() => { return RootContextHelper.wrap(new AddHeaderRoot()); }, "add_header");

We’ll keep the default content, so the filter will add a new Header in all the Responses with the key hello and the value passed to the filter (or world! if no value is passed to it).

Build

We’re ready to compile our code into WebAssembly.

The Gloo Mesh Enterprise CLI will make your life easier again.

You simply need to run the following command:

meshctl wasm build assemblyscript -t webassemblyhub.io/djannot/myfilter:0.1 .

You can see that I’ve indicated that I wanted to use `webassemblyhub.io/djannot/myfilter:0.1` for the Image reference.

meshctl will create an OCI compliant image with this tag. It’s exactly the same as when you use the Docker CLI and the Docker Hub.

Push

The image has been built, so we can now push it to the Web Assembly Hub.

You just need to create a free account and to run meshctl login to authenticate.

To push the Image, simply run the following command:

meshctl wasm push webassemblyhub.io/djannot/myfilter:0.2

Then, if you go to the Web Assembly Hub, you’ll be able to see the Image of your Wasm filter

Deploy

It’s now time to deploy your Wasm filter on Istio !

Note that you can also deploy it on Gloo Edge.

You can deploy it using `meshctl wasm deploy`, but we now live in a Declarative world, so let’s do it the proper way.

Gloo Mesh Enteprise creates a `WasmDeployment` CRD (Custom Resource Definition).

To deploy your Wasm filter on all the Pods corresponding to the version v1 of the reviews service and running in the default namespace of the cluster1 cluster, you use the following yaml:

apiVersion: networking.enterprise.mesh.gloo.solo.io/v1alpha1
kind: WasmDeployment
metadata:
  name: reviews-wasm
  namespace: gloo-mesh
spec:
  filters:
  - filterContext: SIDECAR_INBOUND
    wasmImageSource:
      wasmImageTag: webassemblyhub.io/djannot/myfilter:0.2
    staticFilterConfig:
      '@type': type.googleapis.com/google.protobuf.StringValue
      value: "Gloo Mesh Enterprise Beta"
  workloadSelector:
  - clusters:
    - cluster1
    labels:
      app: reviews
      version: v1
    namespaces:
    - default

Now, when I send a request from the productpage service to the reviews service, I get either:

{'x-powered-by': 'Servlet/3.1', 'content-type': 'application/json', 'date': 'Tue, 15 Dec 2020 08:23:24 GMT', 'content-language': 'en-US', 'content-length': '295', 'x-envoy-upstream-service-time': '10', 'server': 'envoy'}

or:

{'x-powered-by': 'Servlet/3.1', 'content-type': 'application/json', 'date': 'Tue, 15 Dec 2020 08:23:25 GMT', 'content-language': 'en-US', 'content-length': '295', 'x-envoy-upstream-service-time': '17', 'hello': 'Gloo Mesh Enterprise Beta', 'server': 'envoy'}

I have deployed the Istio Bookinfo application with the versions v1 and v2 of the reviews service, so the new header is added half of the time.

Observe

Gloo Mesh Enterprise has processed the `WasmDeployment` object and has added status information on it:

status:
  observedGeneration: 1
  workloadStates:
    reviews-v1-default-cluster1-deployment.gloo-mesh.: FILTERS_DEPLOYED

Very useful, no ?

You can also use the Gloo Mesh Enterprise UI to see the different Wasm filters that have been deployed globally:

And you can even see the workloads were a Wasm filter has been deployed on:

Get started

We invite you to check out the project and join the community. Solo.io also offers enterprise support for Istio service mesh for those looking to operationalize service mesh environments, request a meeting to learn more here.

Watch this video to see it in action: