-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Mixer Out of Process Adapter Walkthrough
This document walks through step-by-step instructions to implement, test and plug a simple gRPC adapter (adapter that runs outside Mixer process as a separate service).
This adapter:
-
Supports the
metric
template which ships with Mixer. -
For every request, prints to a file the data it receives from Mixer at request time.
-
Runs as a separate service that Mixer interacts with.
It should take approximately 30 minutes to finish this task
You may look at Prometheus Out of process adapter for a working example
- Before you start
- Step 1: Write basic adapter skeleton code
- Step 2: Write adapter configuration
- Step 3: Link adapter config with adapter code and add business logic
- Step 4: Write sample operator config
- Step 5: Start Mixer and validate the adapter
- Step 6: Write test and validate your adapter
- Step 7: Encrypt connection between Mixer and GRPC adapter
- Step 8: Cleanup
You'll need Docker, Kubernetes, and Go installed to follow this walkthrough. If you need details see the Dev Guide.
Start Docker with Kubernetes enabled on local machine
Download a local copy of the istio/istio repo:
mkdir -p $GOPATH/src/istio.io/ && \
cd $GOPATH/src/istio.io/ && \
git clone https://github.com/istio/istio
Set the MIXER_REPO variable to the path where the mixer repository is on the local machine. Also, $ISTIO should point to $GOPATH/src/istio.io.
export MIXER_REPO=$GOPATH/src/istio.io/istio/mixer
export ISTIO=$GOPATH/src/istio.io
Successfully build the mixer server.
pushd $ISTIO/istio && make mixs
Create the mygrpcadapter
directory and navigate to it.
cd $MIXER_REPO/adapter && mkdir mygrpcadapter && cd mygrpcadapter
Create the file named mygrpcadapter.go
with the following content
It defines the adapter as a gRPC service that implements the service interface for metric template. This code so far does not add any functionality for printing details in a file. It is done in later steps.
// Copyright 2018 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mygrpcadapter
import (
"context"
"fmt"
"net"
"google.golang.org/grpc"
"istio.io/api/mixer/adapter/model/v1beta1"
"istio.io/istio/mixer/template/metric"
)
type (
// Server is basic server interface
Server interface {
Addr() string
Close() error
Run(shutdown chan error)
}
// MyGrpcAdapter supports metric template.
MyGrpcAdapter struct {
listener net.Listener
server *grpc.Server
}
)
var _ metric.HandleMetricServiceServer = &MyGrpcAdapter{}
// HandleMetric records metric entries
func (s *MyGrpcAdapter) HandleMetric(ctx context.Context, r *metric.HandleMetricRequest) (*v1beta1.ReportResult, error) {
return nil, nil
}
// Addr returns the listening address of the server
func (s *MyGrpcAdapter) Addr() string {
return s.listener.Addr().String()
}
// Run starts the server run
func (s *MyGrpcAdapter) Run(shutdown chan error) {
shutdown <- s.server.Serve(s.listener)
}
// Close gracefully shuts down the server; used for testing
func (s *MyGrpcAdapter) Close() error {
if s.server != nil {
s.server.GracefulStop()
}
if s.listener != nil {
_ = s.listener.Close()
}
return nil
}
// NewMyGrpcAdapter creates a new IBP adapter that listens at provided port.
func NewMyGrpcAdapter(addr string) (Server, error) {
if addr == "" {
addr = "0"
}
listener, err := net.Listen("tcp", fmt.Sprintf(":%s", addr))
if err != nil {
return nil, fmt.Errorf("unable to listen on socket: %v", err)
}
s := &MyGrpcAdapter{
listener: listener,
}
fmt.Printf("listening on \"%v\"\n", s.Addr())
s.server = grpc.NewServer()
metric.RegisterHandleMetricServiceServer(s.server, s)
return s, nil
}
Just to ensure everything is good, let's build the code
go build ./...
Now we have the basic skeleton of an adapter with empty implementation for interfaces for the 'metric' templates. Later steps adds the core code for this adapter.
Since this adapter just prints the data it receives from Mixer into a file, the adapter configuration will take the path of that file as a configuration field.
Create the config proto file under the 'config' dir
mkdir config
Create a new config.proto file inside the config directory with the following content:
syntax = "proto3";
// config for mygrpcadapter
package adapter.mygrpcadapter.config;
import "gogoproto/gogo.proto";
option go_package="config";
// config for mygrpcadapter
message Params {
// Path of the file to save the information about runtime requests.
string file_path = 1;
}
Let's now generate the corresponding go file from the config.proto as well as the adapter resource that contains the adapter's info (configuration descriptor and name). To do this, add the following go generate comment to the adapter code. The bold text shows the new added text.
// nolint:lll
// Generates the mygrpcadapter adapter's resource yaml. It contains the adapter's configuration, name,
// supported template names (metric in this case), and whether it is session or no-session based.
//go:generate $REPO_ROOT/bin/mixer_codegen.sh -a mixer/adapter/mygrpcadapter/config/config.proto -x "-s=false -n mygrpcadapter -t metric"
package mygrpcadapter
..
..
Just to ensure everything is good, let's generate the file and build the code. Go to the top of the repo and do:
BUILD_WITH_CONTAINER=1 make gen
Then go back to the adapter's directory and do:
go build ./...
Let's look at the generate files.
config/mygrpcadapter.yaml
is the adapter's resource that is provided to Mixer for it to know about this adapter. The resource name, mygrpcadapter
in this case, is used by handler configurations to refer to the adapter.
# this config is created through command
# mixgen adapter -c $GOPATH/src/istio.io/istio/mixer/adapter/mygrpcadapter/config/config.proto_descriptor -o $GOPATH/src/istio.io/istio/mixer/adapter/mygrpcadapter/config -s=false -n mygrpcadapter -t metric
apiVersion: "config.istio.io/v1alpha2"
kind: adapter
metadata:
name: mygrpcadapter
namespace: istio-system
spec:
description:
session_based: false
templates:
- metric
Config: .......
Config.pb.go
is the generated go file for the configuration proto
Adapter. mysampleadapter.config.pb.html
is the generated documentation for the adapter.
Config.proto_descriptor
is an intermediate file not directly used by the adapter code.
Modify the adapter code (mygrpcadapter.go
) to use the adapter-specific configuration
(defined in mygrpcadapter/config/config.proto
) to instantiate the file to write to. The code also prints Instances in the file configured via handler configuration and includes auth related code which is covered by step 7. HandleMetric
contains the new changes.
// Copyright 2018 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// nolint:lll
// Generates the mygrpcadapter adapter's resource yaml. It contains the adapter's configuration, name, supported template
// names (metric in this case), and whether it is session or no-session based.
//go:generate $REPO_ROOT/bin/mixer_codegen.sh -a mixer/adapter/mygrpcadapter/config/config.proto -x "-s=false -n mygrpcadapter -t metric"
package mygrpcadapter
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"bytes"
"os"
"istio.io/api/mixer/adapter/model/v1beta1"
policy "istio.io/api/policy/v1beta1"
"istio.io/istio/mixer/adapter/mygrpcadapter/config"
"istio.io/istio/mixer/template/metric"
"istio.io/istio/pkg/log"
)
type (
// Server is basic server interface
Server interface {
Addr() string
Close() error
Run(shutdown chan error)
}
// MyGrpcAdapter supports metric template.
MyGrpcAdapter struct {
listener net.Listener
server *grpc.Server
}
)
var _ metric.HandleMetricServiceServer = &MyGrpcAdapter{}
// HandleMetric records metric entries
func (s *MyGrpcAdapter) HandleMetric(ctx context.Context, r *metric.HandleMetricRequest) (*v1beta1.ReportResult, error) {
log.Infof("received request %v\n", *r)
var b bytes.Buffer
cfg := &config.Params{}
if r.AdapterConfig != nil {
if err := cfg.Unmarshal(r.AdapterConfig.Value); err != nil {
log.Errorf("error unmarshalling adapter config: %v", err)
return nil, err
}
}
b.WriteString(fmt.Sprintf("HandleMetric invoked with:\n Adapter config: %s\n Instances: %s\n",
cfg.String(), instances(r.Instances)))
if cfg.FilePath == "" {
fmt.Println(b.String())
} else {
_, err := os.OpenFile("out.txt", os.O_RDONLY|os.O_CREATE, 0666)
if err != nil {
log.Errorf("error creating file: %v", err)
}
f, err := os.OpenFile(cfg.FilePath, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
log.Errorf("error opening file for append: %v", err)
}
defer f.Close()
log.Infof("writing instances to file %s", f.Name())
if _, err = f.Write(b.Bytes()); err != nil {
log.Errorf("error writing to file: %v", err)
}
}
log.Infof("success!!")
return &v1beta1.ReportResult{}, nil
}
func decodeDimensions(in map[string]*policy.Value) map[string]interface{} {
out := make(map[string]interface{}, len(in))
for k, v := range in {
out[k] = decodeValue(v.GetValue())
}
return out
}
func decodeValue(in interface{}) interface{} {
switch t := in.(type) {
case *policy.Value_StringValue:
return t.StringValue
case *policy.Value_Int64Value:
return t.Int64Value
case *policy.Value_DoubleValue:
return t.DoubleValue
default:
return fmt.Sprintf("%v", in)
}
}
func instances(in []*metric.InstanceMsg) string {
var b bytes.Buffer
for _, inst := range in {
b.WriteString(fmt.Sprintf("'%s':\n"+
" {\n"+
" Value = %v\n"+
" Dimensions = %v\n"+
" }", inst.Name, decodeValue(inst.Value.GetValue()), decodeDimensions(inst.Dimensions)))
}
return b.String()
}
// Addr returns the listening address of the server
func (s *MyGrpcAdapter) Addr() string {
return s.listener.Addr().String()
}
// Run starts the server run
func (s *MyGrpcAdapter) Run(shutdown chan error) {
shutdown <- s.server.Serve(s.listener)
}
// Close gracefully shuts down the server; used for testing
func (s *MyGrpcAdapter) Close() error {
if s.server != nil {
s.server.GracefulStop()
}
if s.listener != nil {
_ = s.listener.Close()
}
return nil
}
func getServerTLSOption(credential, privateKey, caCertificate string) (grpc.ServerOption, error) {
certificate, err := tls.LoadX509KeyPair(
credential,
privateKey,
)
if err != nil {
return nil, fmt.Errorf("failed to load key cert pair")
}
certPool := x509.NewCertPool()
bs, err := ioutil.ReadFile(caCertificate)
if err != nil {
return nil, fmt.Errorf("failed to read client ca cert: %s", err)
}
ok := certPool.AppendCertsFromPEM(bs)
if !ok {
return nil, fmt.Errorf("failed to append client certs")
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{certificate},
ClientCAs: certPool,
}
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
return grpc.Creds(credentials.NewTLS(tlsConfig)), nil
}
// NewMyGrpcAdapter creates a new IBP adapter that listens at provided port.
func NewMyGrpcAdapter(addr string) (Server, error) {
if addr == "" {
addr = "0"
}
listener, err := net.Listen("tcp", fmt.Sprintf(":%s", addr))
if err != nil {
return nil, fmt.Errorf("unable to listen on socket: %v", err)
}
s := &MyGrpcAdapter{
listener: listener,
}
fmt.Printf("listening on \"%v\"\n", s.Addr())
credential := os.Getenv("GRPC_ADAPTER_CREDENTIAL")
privateKey := os.Getenv("GRPC_ADAPTER_PRIVATE_KEY")
certificate := os.Getenv("GRPC_ADAPTER_CERTIFICATE")
if credential != "" {
so, err := getServerTLSOption(credential, privateKey, certificate)
if err != nil {
return nil, err
}
s.server = grpc.NewServer(so)
} else {
s.server = grpc.NewServer()
}
metric.RegisterHandleMetricServiceServer(s.server, s)
return s, nil
}
Just to ensure everything is good, let's build the code
go build ./...
Let's also write the main
function that can start the gRPC service.
Create a file cmd/main.go
with the following content
// Copyright 2018 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"os"
mygrpcadapter "istio.io/istio/mixer/adapter/mygrpcadapter"
)
func main() {
addr := ""
if len(os.Args) > 1 {
addr = os.Args[1]
}
s, err := mygrpcadapter.NewMyGrpcAdapter(addr)
if err != nil {
fmt.Printf("unable to start server: %v", err)
os.Exit(-1)
}
shutdown := make(chan error, 1)
go func() {
s.Run(shutdown)
}()
_ = <-shutdown
}
This concludes the implementation part of the adapter code. Next steps show how to plug an adapter into a build of Mixer and to verify your code's behavior.
To see if your adapter works, we will need a sample operator configuration. So, let's write a simple operator configuration that we will give to Mixer for it to dispatch data to your sample adapter. We will need instance, handler and rule configuration to be passed to the Mixers configuration server.
Create a sample operator config file with name sample_operator_cfg.yaml
inside the $MIXER_REPO/adapter/mygrpcadapter
directory with the following content:
Add the following content to the file $MIXER_REPO/adapter/mygrpcadapter/sample_operator_cfg.yaml
# handler for adapter mygrpcadapter
apiVersion: "config.istio.io/v1alpha2"
kind: handler
metadata:
name: h1
namespace: istio-system
spec:
adapter: mygrpcadapter
connection:
address: "{ADDRESS}" #replaces at runtime by the test
params:
file_path: "out.txt"
---
# instance for template metric
apiVersion: "config.istio.io/v1alpha2"
kind: instance
metadata:
name: i1metric
namespace: istio-system
spec:
template: metric
params:
value: request.size | 0
dimensions:
response_code: "200"
---
# rule to dispatch to handler h1
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: r1
namespace: istio-system
spec:
actions:
- handler: h1.istio-system
instances:
- i1metric
---
First let's copy the configs associated with mygrpcadapter into a separate directory from where Mixer can read it along with other important sample resources like attribute vocabulary.
mkdir testdata
cp sample_operator_cfg.yaml $MIXER_REPO/adapter/mygrpcadapter/testdata
cp config/mygrpcadapter.yaml $MIXER_REPO/adapter/mygrpcadapter/testdata
cp $MIXER_REPO/testdata/config/attributes.yaml $MIXER_REPO/adapter/mygrpcadapter/testdata
cp $MIXER_REPO/template/metric/template.yaml $MIXER_REPO/adapter/mygrpcadapter/testdata
Now let's start the gRPC adapter. On a new terminal run
export ISTIO=$GOPATH/src/istio.io
export MIXER_REPO=$GOPATH/src/istio.io/istio/mixer
cd $MIXER_REPO/adapter/mygrpcadapter
go run cmd/main.go
Note the listening address printed on the console. Copy the content within the quota. For below output the listener address is [::]:38355
listening on "[::]:38355"
On a new terminal export the listener address in an ADDRESS variable.
export ISTIO=$GOPATH/src/istio.io
export MIXER_REPO=$GOPATH/src/istio.io/istio/mixer
export ADDRESS=[::]:38355
Change the handler's connection parameters to point to the adapter address $ADDRESS. Replace the {ADDRESS} string in testdata/sample_operator_cfg.yaml with value of $ADDRESS
sed -i -e "s/{ADDRESS}/${ADDRESS}/g" $MIXER_REPO/adapter/mygrpcadapter/testdata/sample_operator_cfg.yaml
Start the mixer pointing it to the testdata operator configuration
pushd $ISTIO/istio && make mixs
// locate mixs binary, should be $GOPATH/out/linux_amd64/release/mixs on linux os and
// $GOPATH/out/darwin_amd64/release/mixs on mac os.
// Choose command below according to your os:
$GOPATH/out/linux_amd64/release/mixs server --configStoreURL=fs://$(pwd)/mixer/adapter/mygrpcadapter/testdata
The terminal will have the following output and will be blocked waiting to serve requests. There might be errors related to other unrelated configurations; we can ignore them for now.
..
..
Mixer started with
MaxMessageSize: 1048576
MaxConcurrentStreams: 1024
APIWorkerPoolSize: 1024
AdapterWorkerPoolSize: 1024
ExpressionEvalCacheSize: 1024
APIPort: 9091
MonitoringPort: 9093
SingleThreaded: false
ConfigStore2URL: fs:///usr/local/google/home/guptasu/go/src/istio.io/istio/mixer/adapter/mygrpcadapter/sampleoperatorconfig
ConfigDefaultNamespace: istio-system
ConfigIdentityAttribute: destination.service
ConfigIdentityAttributeDomain: svc.cluster.local
LoggingOptions: log.Options{OutputPaths:[]string{"stdout"}, ErrorOutputPaths:[]string{"stderr"}, RotateOutputPath:"", RotationMaxSize:104857600, RotationMaxAge:30, RotationMaxBackups:1000, JSONEncoding:false, IncludeCallerSourceLocation:false, stackTraceLevel:"none", outputLevel:"info"}
TracingOptions: tracing.Options{ZipkinURL:"", JaegerURL:"", LogTraceSpans:false}
2018-01-06T01:43:12.305995Z info template Kind: kubernetesenv, &InstanceParam{SourceUid:,SourceIp:,DestinationUid:,DestinationIp:,OriginUid:,OriginIp:,AttributeBindings:map[string]string{},}
...
Now let's call 'report' using mixer client. This step should cause the mixer server to call your sample adapter with instance objects constructed using the operator configuration.
On a new terminal, set the $ISTIO
and $MIXER_REPO
variables.
export ISTIO=$GOPATH/src/istio.io
export MIXER_REPO=$GOPATH/src/istio.io/istio/mixer
Then run the following.
pushd $ISTIO/istio && make mixc
// locate mixc binary, should be $GOPATH/out/linux_amd64/release/mixc on linux os and
// $GOPATH/out/darwin_amd64/release/mixc on mac os.
// Choose command below according to your os:
$GOPATH/out/linux_amd64/release/mixc report -s destination.service="svc.cluster.local" -i request.size=1235
Inspect the out.txt
file that your adapter would have printed. If you have followed the above steps, then the out.txt
should be in your $MIXER_REPO/adapter/mygrpcadapter/
directory.
tail $MIXER_REPO/adapter/mygrpcadapter/out.txt
You should see something like:
HandleMetric invoked with:
Adapter config: &Params{FilePath:out.txt,}
Instances: 'i1metric.instance.istio-system':
{
Value = 1235
Dimensions = map[response_code:200]
}
You can even try passing other attributes to mixer server and inspect your out.txt file to see how the data passed to the adapter changes. For example,
pushd $ISTIO/istio && make mixc && mixc report -s destination.service="svc.cluster.local" -i request.size=9999999999
If you have reached this far, congratulate yourself !!. You have successfully created a Mixer adapter. You can close (ctrl + c) on your terminals that were running mixer server and gRPC adapter to shut them down.
The above steps 5 (start mixer server and validate ..) were mainly to test your adapter code. You can achieve the same thing by writing a simple test that uses the Mixer's 'pkg/adapter/test' package to start an in-proc Mixer and make calls to it via mixer client.
Add a test file for your adapter code
touch $MIXER_REPO/adapter/mygrpcadapter/mygrpcadapter_integration_test.go
Add the following content to that file
package mygrpcadapter
import (
"fmt"
"io/ioutil"
"testing"
adapter_integration "istio.io/istio/mixer/pkg/adapter/test"
"os"
"strings"
)
func TestReport(t *testing.T) {
adptCrBytes, err := ioutil.ReadFile("config/mygrpcadapter.yaml")
if err != nil {
t.Fatalf("could not read file: %v", err)
}
operatorCfgBytes, err := ioutil.ReadFile("sample_operator_cfg.yaml")
if err != nil {
t.Fatalf("could not read file: %v", err)
}
operatorCfg := string(operatorCfgBytes)
shutdown := make(chan error, 1)
var outFile *os.File
outFile, err = os.OpenFile("out.txt", os.O_RDONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatal(err)
}
defer func() {
if removeErr := os.Remove(outFile.Name()); removeErr != nil {
t.Logf("Could not remove temporary file %s: %v", outFile.Name(), removeErr)
}
}()
adapter_integration.RunTest(
t,
nil,
adapter_integration.Scenario{
Setup: func() (ctx interface{}, err error) {
pServer, err := NewMyGrpcAdapter("")
if err != nil {
return nil, err
}
go func() {
pServer.Run(shutdown)
_ = <-shutdown
}()
return pServer, nil
},
Teardown: func(ctx interface{}) {
s := ctx.(Server)
s.Close()
},
ParallelCalls: []adapter_integration.Call{
{
CallKind: adapter_integration.REPORT,
Attrs: map[string]interface{}{"request.size": int64(555)},
},
},
GetState: func(ctx interface{}) (interface{}, error) {
// validate if the content of "out.txt" is as expected
bytes, err := ioutil.ReadFile("out.txt")
if err != nil {
return nil, err
}
s := string(bytes)
wantStr := `HandleMetric invoked with:
Adapter config: &Params{FilePath:out.txt,}
Instances: 'i1metric.instance.istio-system':
{
Value = 555
Dimensions = map[response_code:200]
}
`
if normalize(s) != normalize(wantStr) {
return nil, fmt.Errorf("got adapters state as : '%s'; want '%s'", s, wantStr)
}
return nil, nil
},
GetConfig: func(ctx interface{}) ([]string, error) {
s := ctx.(Server)
return []string{
// CRs for built-in templates (metric is what we need for this test)
// are automatically added by the integration test framework.
string(adptCrBytes),
strings.Replace(operatorCfg, "{ADDRESS}", s.Addr(), 1),
}, nil
},
Want: `
{
"AdapterState": null,
"Returns": [
{
"Check": {
"Status": {},
"ValidDuration": 0,
"ValidUseCount": 0
},
"Quota": null,
"Error": null
}
]
}`,
},
)
}
func normalize(s string) string {
s = strings.TrimSpace(s)
s = strings.Replace(s, "\t", "", -1)
s = strings.Replace(s, "\n", "", -1)
s = strings.Replace(s, " ", "", -1)
return s
}
Now run the test
cd $MIXER_REPO/adapter/mygrpcadapter && go build ./... && go test *.go
The test starts a inproc mixer, provides it with all the configuration to route metrics to the mygrpcadapter
and invoke it via mixer client.
This is an optional step and this feature is introduced in release 1.1. Istio provides Mutual TLS feature to secure connection between workloads, which is also supported by Mixer for connections to GRPC adpater. mygrpcadapter code already supports MTLS. It loads key/cert from files passed in by environment variables and requires Mixer to present certificate if credential is provided. To test it out, we need some sample key cert. Mixer code base already has some test key certs and we will reuse them in this step.
export MIXER_REPO=$GOPATH/src/istio.io/istio/mixer
mkdir /tmp/grpc-test-key-cert
cp -R $MIXER_REPO/pkg/protobuf/yaml/testdata/auth/. /tmp/grpc-test-key-cert
Then export environment variables that are used by GRPC adapter
export GRPC_ADAPTER_CREDENTIAL=/tmp/grpc-test-key-cert/adapter.crt
export GRPC_ADAPTER_PRIVATE_KEY=/tmp/grpc-test-key-cert/adapter.key
export GRPC_ADAPTER_CERTIFICATE=/tmp/grpc-test-key-cert/ca.pem
After exporting those environment variables, restart the adapter in the same terminal and adjust address in handler config accordingly
go run cmd/main.go
Mixer also needs to load key certs, which could be configured via adapter handler config, replace your test operator config $MIXER_REPO/adapter/mygrpcadapter/testdata/sample_operator_cfg.yaml
with the following
# handler for adapter mygrpcadapter
apiVersion: "config.istio.io/v1alpha2"
kind: handler
metadata:
name: h1
namespace: istio-system
spec:
adapter: mygrpcadapter
connection:
address: "{ADDRESS}" #replaces at runtime by the test
authentication:
mutual:
private_key: "/tmp/grpc-test-key-cert/mixer.key"
client_certificate: "/tmp/grpc-test-key-cert/mixer.crt"
ca_certificates: "/tmp/grpc-test-key-cert/ca.pem"
params:
file_path: "out.txt"
---
# instance for template metric
apiVersion: "config.istio.io/v1alpha2"
kind: instance
metadata:
name: i1metric
namespace: istio-system
spec:
template: metric
params:
value: request.size | 0
dimensions:
response_code: "200"
---
# rule to dispatch to handler h1
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: r1
namespace: istio-system
spec:
actions:
- handler: h1.istio-system
instances:
- i1metric
---
Then restart Mixer with the new config, and now communication between Mixer and adapter should be MTLS encrypted. Send sample report traffic with mixc as former step to verify that the connection works.
Note: within Istio installation, Mixer mounts a key/cert pair by default, which is generated by citadel and uses SAN/SPIFFE base on Mixer service account as identity. In release-1.1, Mixer enforces that certificate from the adapter must be of the same identity as Mixer, which could be achieved by running adapter with Mixer service account. You can load key/cert in adapter either via manual volume mount or sidecar injection. Besides MTLS, TLS is also supported if your GRPC adpater runs outside of mesh or if you are using adapter services provided by vendor. See here for more detail on TLS related config.
Delete the adapter/mygrpcadapter
directory
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