Skip to content

Out of process Attribute Generating Adapter Developer Walkthrough

Kuat edited this page Oct 29, 2018 · 10 revisions

This guide should take less than 30 minutes to follow.

Istio 1.1 introduces support for out-of-process attribute generating adapters (occasionally abbreviated as APAs). Attribute generating adapters implement templates of ATTRIBUTE_GENERATOR variety. Their purpose is to produce additional attributes as a pre-processing step prior to execution of Check() and Report() calls. In this guide, we implement a simple mapper attribute generating adapter, that supplies an extra attribute value using a pre-defined mapping.

Step 0: Before you start

You will need a development environment set-up with Go and Kubernetes per Dev Guide.

Download and build a local copy of Istio:

mkdir -p $GOPATH/src/istio.io/ && \
cd $GOPATH/src/istio.io/  && \
git clone https://github.com/istio/istio
cd istio
go build ./...

Install protoc (version 3.5.1 or higher) from https://github.com/google/protobuf/releases and make it available as an executable from $PATH.

Step 1: Define a template

The following template takes a string as input and produces a string as output:

syntax = "proto3";
package mapper;
import "mixer/adapter/model/v1beta1/extensions.proto";
option (istio.mixer.adapter.model.v1beta1.template_variety) = TEMPLATE_VARIETY_ATTRIBUTE_GENERATOR;
message Template {
  string key = 1;
}
message OutputTemplate {
  string value = 1;
}

Note the template variety declaration; it indicates that the adapter RPC interface should produce an OutputMsg corresponding to the OutputTemplate.

Save this file as mixer/adapter/mapper/template.proto in istio root directory. Run the following command to generate the code artifacts from the template:

bin/mixer_codegen.sh -t mixer/adapter/mapper/template.proto

After running this command, you should see several new files in mixer/adapter/mapper directory, including a file called template.yaml.

Note we generally recommend using pre-defined templates. Istio comes with a kubernetesenv APA out of the box, which has a template for populating workload metadata from Kubernetes.

Step 2: Implement the adapter

Let us write a simple mapper adapter that uses a fixed lookup table. Save the following file as mixer/adapter/mapper/mapper.go:

package mapper

import context "golang.org/x/net/context"

type MyAdapter struct{}

func (MyAdapter) HandleMapper(_ context.Context, req *HandleMapperRequest) (*OutputMsg, error) {
        lookup := map[string]string{
                "hello": "world",
        }
        return &OutputMsg{Value: lookup[req.Instance.Key]}, nil
}

We also need a main function to run the adapter as a standalone process. Save the following file as mixer/adapter/mapper/main/main.go:

package main

import (
        "net"

        "google.golang.org/grpc"
        "istio.io/istio/mixer/adapter/mapper"
)

func main() {
        listener, err := net.Listen("tcp", ":9070")
        if err != nil {
                panic(err)
        }
        server := grpc.NewServer()
        mapper.RegisterHandleMapperServiceServer(server, mapper.MyAdapter{})
        server.Serve(listener)
}

Try running this adapter:

go run mixer/adapter/mapper/main/main.go

If all goes well, the adapter should be waiting to accept requests! If binding to a port fails, feel free to adjust the port number (from 9070).

Step 3: Configure the adapter

For Mixer to accept the adapter, we also need to provide a configuration schema for the mapper adapter. We will use the following empty schema:

syntax = "proto3";
package config;
message Params{}

Save this snippet as file mixer/adapter/mapper/config/config.proto and run the following command to generate the adapter configuration:

bin/mixer_codegen.sh -a mixer/adapter/mapper/config/config.proto -x "-s=false -n myadapter -t mapper"

Note that this command should produce a file called mixer/adapter/mapper/config/myadapter.yaml. The command above requests that this adapter is called myadapter, it implements mapper template, and it is not session-based.

Step 4: Connect Mixer to the adapter

Now that we have all the pieces, we are ready to connect our new adapter to Mixer. Let's start Mixer locally using a Kubernetes cluster for configuration with debug logging:

go run mixer/cmd/mixs/main.go server --configStoreURL=k8s://$HOME/.kube/config --log_output_level="api:debug"

In another terminal window, start the adapter:

go run mixer/adapter/mapper/main/main.go

Now that we have both Mixer and the adapter running, load the new template, the adapter definitions, and the handler:

kubectl create namespace istio-system
kubectl apply -f mixer/adapter/mapper/template.yaml
kubectl apply -f mixer/adapter/mapper/config/myadapter.yaml
cat <<EOF | kubectl create -f -
apiVersion: config.istio.io/v1alpha2
kind: handler
metadata:
  name: h1
  namespace: istio-system
spec:
  adapter: myadapter
  connection:
    address: ":9070"
  params: {}
EOF

To activate the handler, we need to supply an input instance and the output mapping. First, let us load the attribute declarations:

kubectl apply -f mixer/testdata/config/attributes.yaml

Then define the following input instance:

cat <<EOF | kubectl create -f -
apiVersion: config.istio.io/v1alpha2
kind: instance
metadata:
  name: i1
  namespace: istio-system
spec:
  template: mapper
  params:
    key: destination.namespace
  attribute_bindings:
    source.namespace: output.value | "unknown"
EOF

This instance sets the key field in the mapper template to the value of destination.namespace. After myadapter executes on this instance, it applies the output as the value for source.namespace attribute.

Finally, let us add an unconditional rule to define an action application for myadapter:

cat <<EOF | kubectl create -f -
apiVersion: config.istio.io/v1alpha2
kind: rule
metadata:
  name: r1
  namespace: istio-system
spec:
  actions:
  - handler: h1.istio-system
    instances: ["i1"]
EOF

Step 5: Try the new adapter!

We can use mixc command line tool to validate the new adapter. Run the following command to test myadapter:

go run mixer/cmd/mixc/main.go report -s destination.namespace="hello"

Take a look at mixc debug log. You should see the following attribute bag created during pre-processing:

debug   api     Dispatching Preprocess
debug   api     Dispatching to main adapters after running preprocessors
debug   api     Attribute Bag: 
destination.namespace         : hello
---
source.namespace              : world

Congratulations! We successfully implemented a new attribute generating adapter that automatically binds source.namespace attribute.

Let us try different input attribute values:

go run mixer/cmd/mixc/main.go report -s destination.namespace="hell"

The output of mixs should be:

Attribute Bag:
destination.namespace         : hell
---
source.namespace              : unknown

Now let us try to set source.namespace value in the input:

go run mixer/cmd/mixc/main.go report -s destination.namespace="hello",source.namespace="hello"

The output of mixs should be:

Attribute Bag: 
destination.namespace         : hello
source.namespace              : hello

This points to the fact that attribute-generating adapter do not override existing values if they are already set.

Step 6: What's next?

You can follow the rest of the adapter development guide to set up an integration test, or to package and distribute your custom adapter.

To clean up, delete the directory istio/mixer/adapter/mapper, and remove the kubernetes resources:

kubectl delete rule/r1 handler/h1 instance/i1 adapter/myadapter template/mapper -n istio-system

Dev Environment

Writing Code

Pull Requests

Testing

Performance

Releases

Misc

Central Istiod

Security

Mixer

Pilot

Telemetry

Clone this wiki locally