Mixer Out Of Process Adapter Dev Guide
WARNING: This feature is under active development.
This document is for developers looking to build a gRPC back-end (a.k.a gRPC adapter) for Istio's Mixer. Adapters integrate Mixer with different infrastructure backends that deliver core functionality, such as logging, monitoring, quotas, ACL checking, and more. This guide explains the adapter model and adapter lifecycle, and also walks through the step-by-step instructions for creating a simple gRPC adapter.
- Background
- Template overview
- Adapter lifecycle
- Plug adapter into Mixer
- Testing
- Built-in templates
- Implementation walkthrough
Mixer provides Istio's generic intermediation layer between application code and infrastructure backends such as quota systems, access control systems, logging, and so on. It is an attribute processing engine that uses operator-supplied configuration to map request attributes from the proxy into calls to these backend systems via a pluggable set of adapters. Adapters enable Mixer to expose a single consistent API, independent of the infrastructure backends in use. The exact set of adapters used at runtime is determined through operator configuration and can easily be extended to target new or custom infrastructure backends.
Mixer structures its incoming attributes into a more useful form for adapters using templates. Templates describe the form of data dispatched to adapters when processing a request and the gRPC service interface that the adapter must implement to receive this data. The operator that configures Istio controls how this template-specific data is constructed and dispatched to adapters.
Out of the box, Mixer provides a suite of default templates. We strongly recommend that, when implementing adapters, you use Mixer's default templates. Though if they are not suitable for your particular needs you can also create your own templates along with adapters to consume the relevant data. Mixer also includes some (TODO point this to production gRPC adapters) built-in sample gRPC adapters by default, but users may need to implement their own to let Mixer send data to their chosen backend.
The roles of the template author, adapter author, and the operator can be summarized as:
-
The template author defines a template, which describes the data Mixer dispatches to adapters, and the interface that the adapter must implement to process that data. The supported set of templates within Mixer determines the various types of data an operator can configure Mixer to create and dispatch to the adapters.
-
The adapter author selects the templates he/she wants to support based on the functionality the adapter must provide. The adapter author's role is to implement the required set of template-specific interfaces to process the data dispatched by Mixer at runtime.
-
The operator defines what data should be collected (instances), where it can be sent (handlers), and when to send it (rules).
To understand how an adapter receives and processes a template-specific instance, this section first provides details about various artifacts of a template that are relevant for adapter development.
As we saw in the previous section, a build of Mixer supports a set of templates, and every template defines a kind of data Mixer dispatches to adapters when processing a request, and also defines the interface for adapters to consume that data.
The following diagram shows the various components of a template.
We'll look at each of these in more detail below.
Templates are defined using a proto file with a message named 'Template'
. Template
is a simple proto message with
no associated code. All of the Go artifacts used by adapters are automatically generated from the template protos.
Every template also has two additional properties associated with it:
-
Name: Every template has a unique name. Adapter code uses the name of the template to register with Mixer that it wants to consume
Instance
objects associated with a particular template. The template name is also used within operator config to provide template-specific fields to attribute mapping, which is used to createInstance
objects. -
Template Variety: Every template has a specific
template_variety
which can be either Check, Report, Quota or AttributeGenerator. The template and its variety determine the signatures of the methods that adapters must implement for consuming the associated instances. Thetemplate_variety
also determines under which of the core Mixer behaviors, theinstances
for the templates should be created and dispatched to adapters. For example:- Check template variety instances are created and dispatched only during Mixer's Check API call.
- Report template variety instances are created and dispatched only during Mixer's Report API call.
- Quota template variety instances are created and dispatched only during Mixer's Check API call when queried for quota allocation.
-
AttributeGenerator template variety instances are created and dispatched to adapters for both Check, Report Mixer
API calls. The processing of these templates happen during the supplementary attribute generation
phase which happens before processing any other variety of templates. Adapters that handle
AttributeGenerator
templates are called attribute generating adapters. These adapters are responsible for generating output data, dictated by the template, which operators can use to create new attributes. These new attributes are combined with the attributes from the request to form the total set of attributes for the operation. These new attributes can therefore now be used by operators to configure instances of other check, report and quota variety templates.
Individual templates are processed in order to produce five Go artifacts:
-
InstanceMsg struct: This defines the data that is passed to the adapters at request time. Mixer constructs objects of the
Instance
type, based on the request attributes and operator configuration. -
OutputMsg struct (Only for ATTRIBUTE_GENERATOR templates): This defines the data that is returned by the adapters during the attribute generation phase (before other check, report, quota handling adapters are invoked). Based on operator configuration, Mixer constructs new attributes using the
OutputMsg
object. -
Handler service: This defines the gRPC service RPC interface that Mixer uses to dispatch created
InstanceMsg
objects to the adapters at request time. Adapters must implement Handler services for all templates they support. -
Type struct: If the datatype of a field in the
InstanceMsg
is dynamic (istio.policy.v1beta1.Value
), the datatype of the value it will hold during request time is determined based on operator-supplied configuration. The type struct expresses the datatype of dynamic fields using the ValueType enum, which has 1:1 mapping between Go data types and its enum values.
These examples show three templates, one for each of the possible template_variety
types. Each example shows a
Template
message, and the resulting generated Go code.
Template.proto
syntax = "proto3";
package metric;
import "mixer/adapter/model/v1beta1/type.proto";
import "mixer/adapter/model/v1beta1/extensions.proto";
option (istio.mixer.v1.config.template.template_variety) = TEMPLATE_VARIETY_REPORT;
// Metric represents a single piece of data to report.
message Template {
// The value being reported.
istio.mixer.adapter.model.v1beta1.Value value = 1;
// The unique identity of the particular metric to report.
map<string, istio.mixer.adapter.model.v1beta1.Value> dimensions = 2;
}
Auto-generated Go code used by adapter implementation
// HandleMetricService is implemented by backends that wants to handle request-time 'metric' instances.
service HandleMetricService {
// HandleMetric is called by Mixer at request-time to deliver 'metric' instances to the backend.
rpc HandleMetric(HandleMetricRequest) returns (istio.mixer.adapter.model.v1beta1.ReportResult);
}
// Request message for HandleMetric method.
message HandleMetricRequest {
// 'metric' instances.
repeated InstanceMsg instances = 1;
// Adapter specific handler configuration.
//
// Note: Backends can also implement [InfrastructureBackend][https://github.com/istio/api/blob/master/mixer/adapter/model/v1beta1/infrastructure_backend.proto]
// service and therefore opt to receive handler configuration during session creation through `InfrastructureBackend.CreateSession`
// call. In that case, adapter_config will have type_url as 'google.protobuf.Any.type_url' and would contain string
// value of session_id (returned from InfrastructureBackend.CreateSession).
google.protobuf.Any adapter_config = 2;
// Id to dedupe identical requests from Mixer.
string dedup_id = 3;
}
// Contains instance payload for 'metric' template. This is passed to infrastructure backends during request-time
// through HandleMetricService.HandleMetric.
message InstanceMsg {
// Name of the instance as specified in configuration.
string name = 72295727;
// The value being reported.
istio.policy.v1beta1.Value value = 1;
// The unique identity of the particular metric to report.
map<string, istio.policy.v1beta1.Value> dimensions = 2;
}
// Contains inferred type information about specific instance of 'metric' template. This is passed to
// infrastructure backends during configuration-time through [InfrastructureBackend.CreateSession][TODO: Link to this fragment].
message Type {
// The value being reported.
istio.policy.v1beta1.ValueType value = 1;
// The unique identity of the particular metric to report.
map<string, istio.policy.v1beta1.ValueType> dimensions = 2;
}
Template.proto
syntax = "proto3";
package listentry;
import "mixer/adapter/model/v1beta1/extensions.proto";
option (istio.mixer.v1.config.template.template_variety) = TEMPLATE_VARIETY_CHECK;
// ListEntry is used to verify the presence/absence of a string
// within a list.
message Template {
// Specifies the entry to verify in the list.
string value = 1;
}
Auto-generated Go code used by adapter implementation
// HandleListEntryService is implemented by backends that wants to handle request-time 'listentry' instances.
service HandleListEntryService {
// HandleListEntry is called by Mixer at request-time to deliver 'listentry' instances to the backend.
rpc HandleListEntry(HandleListEntryRequest) returns (istio.mixer.adapter.model.v1beta1.CheckResult);
}
// Request message for HandleListEntry method.
message HandleListEntryRequest {
// 'listentry' instance.
InstanceMsg instance = 1;
// Adapter specific handler configuration.
//
// Note: Backends can also implement [InfrastructureBackend](https://github.com/istio/api/blob/master/mixer/adapter/model/v1beta1/infrastructure_backend.proto)
// service and therefore opt to receive handler configuration during session creation through `InfrastructureBackend.CreateSession`
// call. In that case, adapter_config will have type_url as 'google.protobuf.Any.type_url' and would contain string
// value of session_id (returned from InfrastructureBackend.CreateSession).
google.protobuf.Any adapter_config = 2;
// Id to dedupe identical requests from Mixer.
string dedup_id = 3;
}
// Contains instance payload for 'listentry' template. This is passed to infrastructure backends during request-time
// through HandleListEntryService.HandleListEntry.
message InstanceMsg {
// Name of the instance as specified in configuration.
string name = 72295727;
// Specifies the entry to verify in the list.
string value = 1;
}
// Contains inferred type information about specific instance of 'listentry' template. This is passed to
// infrastructure backends during configuration-time through [InfrastructureBackend.CreateSession][TODO: Link to this fragment].
message Type {
}
Template.proto
package quota;
import "policy/v1beta1/type.proto";
import "mixer/adapter/model/v1beta1/extensions.proto";
option (istio.mixer.adapter.model.v1beta1.template_variety) = TEMPLATE_VARIETY_QUOTA;
// The `quota` template represents a piece of data to check Quota for.
//
// When writing the configuration, the value for the fields associated with this template can either be a
// literal or an [expression](https://istio.io/docs/reference//config/policy-and-telemetry/expression-language/). Please note that if the datatype of a field is not istio.policy.v1beta1.Value,
// then the expression's [inferred type](https://istio.io/docs/reference//config/policy-and-telemetry/expression-language/#type-checking) must match the datatype of the field.
message Template {
// The unique identity of the particular quota to manipulate.
map<string, istio.policy.v1beta1.Value> dimensions = 1;
}
Auto-generated Go code used by adapter implementation
// HandleQuotaService is implemented by backends that wants to handle request-time 'quota' instances.
service HandleQuotaService {
// HandleQuota is called by Mixer at request-time to deliver 'quota' instances to the backend.
rpc HandleQuota(HandleQuotaRequest) returns (istio.mixer.adapter.model.v1beta1.QuotaResult);
}
// Request message for HandleQuota method.
message HandleQuotaRequest {
// 'quota' instance.
InstanceMsg instance = 1;
// Adapter specific handler configuration.
//
// Note: Backends can also implement [InfrastructureBackend][https://istio.io/docs/reference/config/mixer/istio.mixer.adapter.model.v1beta1.html#InfrastructureBackend]
// service and therefore opt to receive handler configuration during session creation through [InfrastructureBackend.CreateSession][TODO: Link to this fragment]
// call. In that case, adapter_config will have type_url as 'google.protobuf.Any.type_url' and would contain string
// value of session_id (returned from InfrastructureBackend.CreateSession).
google.protobuf.Any adapter_config = 2;
// Id to dedupe identical requests from Mixer.
string dedup_id = 3;
// Expresses the quota allocation request.
istio.mixer.adapter.model.v1beta1.QuotaRequest quota_request = 4;
}
// Contains instance payload for 'quota' template. This is passed to infrastructure backends during request-time
// through HandleQuotaService.HandleQuota.
message InstanceMsg {
// Name of the instance as specified in configuration.
string name = 72295727;
// The unique identity of the particular quota to manipulate.
map<string, istio.policy.v1beta1.Value> dimensions = 1;
}
// Contains inferred type information about specific instance of 'quota' template. This is passed to
// infrastructure backends during configuration-time through [InfrastructureBackend.CreateSession][TODO: Link to this fragment].
message Type {
// The unique identity of the particular quota to manipulate.
map<string, istio.policy.v1beta1.ValueType> dimensions = 1;
}
This section explains various Mixer states during which it interacts with adapters. This is important in order to understand how to implement adapter code and manage the states of various objects within the adapter code itself, such as remote connections and local caches. This section explains how an adapter implementation is usually structured to achieve clear separation of concerns between configuration-time and request-time responsibilities.
gRPC adapters can be either session based or no-session based.
-
session based model: NOTE: Not implemented yet. Mixer passes the adapter specific configuration to the gRPC adapter only once and establishes a session using a session identifier. All future communications between Mixer and the adapter uses the session identifier which refers to the previously passed in configuration data. Session based adapters must also implement the
InfrastructureBackend
service, along with template specificHandle
service. Mixer creates and closes a session with the adapter using the InfrastructureBackend rpcs. Mixer calls theValidate
function, followed byCreateSession
. Thesession_id
returned fromCreateSession
is used to make subsequent request-time Handle calls and the eventualCloseSession
function. Mixer assumes that, given thesession_id
, the backend can retrieve the necessary context to serve the Handle calls that contains the request-time payload (template-specific InstanceMsg protobuf). -
no-session based model: Adapter only implements template specific Handle service (example
HandleMetricService
,HandlerQuotaService
etc.). Mixer only communicates with adapter during request time. On each Handle rpc call the Request message contains the entire adapter configuration along with the InstanceMsg payload. For example, theHandleMetricRequest
is passed on each request-timeHandleMetric rpc
and contains themetric.InstanceMsg
as well as theadapter configuration
.
High level flow between Mixer and adapters.
Mixer dispatches InstanceMsg
objects to the adapter based on the rules configured by the operator.
Mixer does this by invoking the Handle RPC on the adapter gRPC service.
Every adapter needs to provide a resource config that the operator need to ingest into Mixer's configuration store. Creating resource config is easy; Use the mixer/tool/mixgen tool to create an adapter resource, or use the mixer_codegen.sh with -a
flag passing the adapter configuration. mixer_codegen.sh
will create the <adapter name>.yaml
file that defines the adapter's resource definition. Adapter developer makes this file available for operators to use and ingest into their Mixer configuration store.
//go:generate $REPO_ROOT/bin/mixer_codegen.sh -a mixer/adapter/prometheus/config/config.proto -x "-n prometheus -s=false -t metric "
In both cases the command is executed as part of the go generate
step and both generate prometheus
adapter's configuration. The generated resource defines the adapter as no-session
that supports metric
template and also embeds the adapter's configuration proto. Here is how the generated resource yaml looks
# this config is created through command
# mixgen adapter -c $GOPATH/src/istio.io/istio/mixer/adapter/prometheus/config/config.proto_descriptor -o $GOPATH/src/istio.io/istio/mixer/adapter/prometheus/config -n prometheus -t metric -s=false
apiVersion: "config.istio.io/v1alpha2"
kind: adapter
metadata:
name: prometheus
namespace: istio-system
spec:
description:
session_based: false
templates:
- metric
config: CsD3AgogZ29vZ2xlL3Byb3RvYnVmL2Rlc2NyaXB0b3.....
Once this resource is ingested into Mixer, operators can write handler
configurations that references the adapter based on the name specified by the -n
flag.
Notice::
- The template name
metric
is referencing to a template resource which must also be ingested into Mixer's configuration store. metric template resource is always available for a default Mixer deployment. However, in case the template is custom or the custom Mixer build does not have the necessary template resource, operator will have to explicitly add it. It is a good practice for adapters to provide the template resources they depend on along side their adapter resource. Here is how a template resource yaml is created
//go:generate $REPO_ROOT/bin/mixer_codegen.sh -t mixer/template/apikey/template.proto
mixer_codegen.sh
takes the template proto and generates the template-specific artifacts (template specific service along with request messages, as well as the template resource yaml file). Almost always every template proto has a corresponding mixer_codegen.sh
invoked on it which generates the template specific artifacts within the same directory.
- Connection information for the gRPC adapter: Operator needs the endpoint address of the gRPC adapter to write the
handler's connection
configuration. Using this information Mixer connects to the gRPC adapter. Adapter must therefore also document, in their config.proto or some web portal, the gRPC address that operator can use in their configuration.
We provide a simple adapter test framework. The framework instantiates an in-process Mixer gRPC server with a config store backed by the local filesystem, and also a Mixer gRPC client in test process, which allows stepping through adapter code in test cases. The test framework is implemented in pkg/adapter/test/integration.go. A sample test is provided to show how to use this test framework to test a prometheus reporting gRPC adapter.
Mixer ships with a set of built-in templates that are ready to use by adapters:
Please refer to Out-Of-Process Adapter Walkthrough
Visit istio.io to learn how to use Istio.
- Preparing for Development Mac
- Preparing for Development Linux
- Troubleshooting Development Environment
- Repository Map
- GitHub Workflow
- Github Gmail Filters
- Using the Code Base
- Developing with Minikube
- Remote Debugging
- Verify your Docker Environment
- Istio Test Framework
- Working with Prow
- Test Grid
- Code Coverage FAQ
- Writing Good Integration Tests
- Test Flakes
- Release Manager Expectations
- Preparing Istio Releases
- 1.5 Release Information
- 1.6 Release Information
- 1.7 Release Information
- 1.8 Release Information
- 1.9 Release Information
- 1.10 Release Information
- 1.11 Release Information
- 1.12 Release Information
- 1.13 Release Information
- 1.14 Release Information
- 1.15 Release Information
- 1.16 Release Information
- 1.17 Release Information
- 1.18 Release Information
- 1.19 Release Information
- 1.20 Release Information
- 1.21 Release Information
- 1.22 Release Information
- Collecting Logs and Debug Info
- Dependency FAQ
- Working with discuss.istio.io
- Developing with and hosting upon OpenShift
- Adapter Dev Guide
- Adapter Walkthrough
- Attribute Generating Adapter Walkthrough
- Route Directive Adapter Development Guide
- Out of Tree Adapter Walkthrough
- Running a Local Instance
- Template Dev Guide
- Using a Custom Adapter
- Publishing Adapters and Templates to istio.io
- Enabling Envoy Authorization Service and gRPC Access Log Service With Mixer