-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gRPC Plugin framework #1461
gRPC Plugin framework #1461
Changes from 3 commits
2d32099
1a21e65
70f43dc
482edd6
be72ed7
9925b38
74aaf5e
a8a0114
ef0078f
931dfa4
d5fe102
f56cc80
7dc2b9a
f180890
419f4ce
9f482b4
3919ac6
5e87836
7e7b072
7ffb8b6
c136b37
978eb07
0abb726
dd199ad
79e0849
b747fb9
03db22b
ba8b35f
616f1b9
bf0e140
deba568
763ec4c
3b5e9ea
6041195
86d8a1f
84a7a67
4a43047
83f7edc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/jaegertracing/jaeger/model" | ||
"github.com/jaegertracing/jaeger/plugin/storage/grpc" | ||
"github.com/spf13/viper" | ||
"github.com/uber/jaeger-lib/metrics/prometheus" | ||
"go.uber.org/zap" | ||
"testing" | ||
"time" | ||
) | ||
|
||
var logger = zap.NewNop() | ||
|
||
func BenchmarkNoopSpanWriter(b *testing.B) { | ||
s := &noopStore{} | ||
|
||
for n := 0; n < b.N; n++ { | ||
s.WriteSpan(&model.Span{ | ||
TraceID:model.NewTraceID(1, 2), | ||
SpanID:model.NewSpanID(1), | ||
OperationName: "test", | ||
StartTime: time.Now(), | ||
Duration: 1 * time.Second, | ||
Process: model.NewProcess("process", []model.KeyValue{ | ||
|
||
}), | ||
ProcessID: "process_id", | ||
Tags: []model.KeyValue{ | ||
{ | ||
Key:"test", | ||
VStr: "", | ||
}, | ||
}, | ||
}) | ||
} | ||
} | ||
|
||
func BenchmarkGRPCNoopSpanWriter(b *testing.B) { | ||
v := viper.New() | ||
|
||
v.Set("grpc-storage-plugin.binary", "noop-grpc-plugin") | ||
|
||
f := grpc.NewFactory() | ||
f.InitFromViper(v) | ||
|
||
metricsFactory := prometheus.New() | ||
|
||
f.Initialize( metricsFactory, logger) | ||
|
||
sw, err := f.CreateSpanWriter() | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
|
||
for n := 0; n < b.N; n++ { | ||
sw.WriteSpan(&model.Span{ | ||
TraceID:model.NewTraceID(1, 2), | ||
SpanID:model.NewSpanID(1), | ||
OperationName: "test", | ||
StartTime: time.Now(), | ||
Duration: 1 * time.Second, | ||
Process: model.NewProcess("process", []model.KeyValue{ | ||
|
||
}), | ||
ProcessID: "process_id", | ||
Tags: []model.KeyValue{ | ||
{ | ||
Key:"test", | ||
VStr: "", | ||
}, | ||
}, | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Copyright (c) 2018 The Jaeger 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 ( | ||
"context" | ||
"flag" | ||
"github.com/hashicorp/go-plugin" | ||
"github.com/jaegertracing/jaeger/model" | ||
"github.com/jaegertracing/jaeger/plugin/storage/grpc/shared" | ||
"github.com/jaegertracing/jaeger/plugin/storage/memory" | ||
"github.com/jaegertracing/jaeger/storage/spanstore" | ||
"github.com/spf13/viper" | ||
"path" | ||
"strings" | ||
"time" | ||
) | ||
|
||
var configPath string | ||
|
||
func main() { | ||
flag.StringVar(&configPath, "config", "", "A path to the plugin's configuration file") | ||
flag.Parse() | ||
|
||
if configPath != "" { | ||
viper.SetConfigFile(path.Base(configPath)) | ||
viper.AddConfigPath(path.Dir(configPath)) | ||
} | ||
|
||
v := viper.New() | ||
v.AutomaticEnv() | ||
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_")) | ||
|
||
opts := memory.Options{} | ||
opts.InitFromViper(v) | ||
|
||
plugin.Serve(&plugin.ServeConfig{ | ||
HandshakeConfig: shared.Handshake, | ||
VersionedPlugins: map[int]plugin.PluginSet{ | ||
1: map[string]plugin.Plugin{ | ||
shared.StoragePluginIdentifier: &shared.StorageGRPCPlugin{ | ||
Impl: &noopStore{}, | ||
}, | ||
}, | ||
}, | ||
GRPCServer: plugin.DefaultGRPCServer, | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we expect plugin authors to do something different in this ServeConfig aside from providing the storage impl? I am thinking we could move the construction of this config into a function in plugin/storage/grpc package that only takes the storage impl as argument. This way we wouldn't need to expose all the constants. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm unsure on that one. If we hide all the details inside a function then we can expose the ability to provide a custom GRPCServer factory method anyway. |
||
} | ||
|
||
type noopStore struct { | ||
|
||
} | ||
|
||
func (*noopStore) GetDependencies(endTs time.Time, lookback time.Duration) ([]model.DependencyLink, error) { | ||
return nil, nil | ||
} | ||
|
||
func (*noopStore) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { | ||
return nil, nil | ||
} | ||
|
||
func (*noopStore) GetServices(ctx context.Context) ([]string, error) { | ||
return nil, nil | ||
} | ||
|
||
func (*noopStore) GetOperations(ctx context.Context, service string) ([]string, error) { | ||
return nil, nil | ||
} | ||
|
||
func (*noopStore) FindTraces(ctx context.Context, query *spanstore.TraceQueryParameters) ([]*model.Trace, error) { | ||
return nil,nil | ||
} | ||
|
||
func (*noopStore) FindTraceIDs(ctx context.Context, query *spanstore.TraceQueryParameters) ([]model.TraceID, error) { | ||
return nil, nil | ||
} | ||
|
||
func (*noopStore) WriteSpan(span *model.Span) error { | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright (c) 2018 The Jaeger 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 config | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this import would read There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had just left it as it was when Olivier wrote it, now moved. |
||
|
||
import ( | ||
"fmt" | ||
"github.com/hashicorp/go-hclog" | ||
"github.com/hashicorp/go-plugin" | ||
"github.com/jaegertracing/jaeger/plugin/storage/grpc/shared" | ||
"os/exec" | ||
"runtime" | ||
) | ||
|
||
// Configuration describes the options to customize the storage behavior | ||
type Configuration struct { | ||
PluginBinary string `yaml:"binary"` | ||
PluginConfigurationFile string `yaml:"configuration-file"` | ||
} | ||
|
||
func (c *Configuration) Build() (shared.StoragePlugin, error) { | ||
client := plugin.NewClient(&plugin.ClientConfig{ | ||
HandshakeConfig: shared.Handshake, | ||
VersionedPlugins: map[int]plugin.PluginSet{ | ||
1: shared.PluginMap, | ||
}, | ||
Cmd: exec.Command(c.PluginBinary, "--config", c.PluginConfigurationFile), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pull up to a variable and disable gosec rule: // #nosec G204
cmd := exec.Command(c.PluginBinary, "--config", c.PluginConfigurationFile) |
||
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, | ||
Logger: hclog.New(&hclog.LoggerOptions{ | ||
Level: hclog.Warn, | ||
}), | ||
}) | ||
|
||
runtime.SetFinalizer(client, func(c *plugin.Client) { | ||
c.Kill() | ||
}) | ||
|
||
rpcClient, err := client.Client() | ||
if err != nil { | ||
return nil, fmt.Errorf("error attempting to connect to plugin rpc client: %s", err) | ||
} | ||
|
||
raw, err := rpcClient.Dispense(shared.StoragePluginIdentifier) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to retrieve storage plugin instance: %s", err) | ||
} | ||
|
||
storagePlugin, ok := raw.(shared.StoragePlugin) | ||
if !ok { | ||
return nil, fmt.Errorf("unexpected type for plugin \"%s\"", shared.StoragePluginIdentifier) | ||
} | ||
|
||
return storagePlugin, nil | ||
} | ||
|
||
type PluginBuilder interface { | ||
Build() (shared.StoragePlugin, error) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps it's better to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that it was using noop for benchmarking, happy to move it over to memstore for integration testing though.