Skip to content

Commit 1d22aaf

Browse files
authored
Implement anonymizer's main program (jaegertracing#2621)
1 parent dcf56e8 commit 1d22aaf

File tree

10 files changed

+363
-35
lines changed

10 files changed

+363
-35
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ examples/memstore-plugin/memstore-plugin
1818
cmd/all-in-one/all-in-one-*
1919
cmd/agent/agent
2020
cmd/agent/agent-*
21+
cmd/anonymizer/anonymizer
22+
cmd/anonymizer/anonymizer-*
2123
cmd/collector/collector
2224
cmd/collector/collector-*
2325
cmd/ingester/ingester

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ build-all-in-one build-all-in-one-debug: build-ui elasticsearch-mappings
250250
build-agent build-agent-debug:
251251
$(GOBUILD) $(DISABLE_OPTIMIZATIONS) -o ./cmd/agent/agent$(SUFFIX)-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/agent/main.go
252252

253+
.PHONY: build-anonymizer
254+
build-anonymizer:
255+
$(GOBUILD) $(DISABLE_OPTIMIZATIONS) -o ./cmd/anonymizer/anonymizer$(SUFFIX)-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/anonymizer/main.go
256+
253257
.PHONY: build-query build-query-debug
254258
build-query build-query-debug: build-ui
255259
$(GOBUILD) $(DISABLE_OPTIMIZATIONS) -tags ui -o ./cmd/query/query$(SUFFIX)-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/query/main.go

cmd/anonymizer/app/anonymizer/anonymizer.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,26 +53,32 @@ type mapping struct {
5353
//
5454
// The mapping from original to obfuscated strings is stored in a file and can be reused between runs.
5555
type Anonymizer struct {
56-
mappingFile string
57-
logger *zap.Logger
58-
lock sync.Mutex
59-
mapping mapping
60-
hashStandardTags bool
61-
hashCustomTags bool
62-
hashLogs bool
63-
hashProcess bool
56+
mappingFile string
57+
logger *zap.Logger
58+
lock sync.Mutex
59+
mapping mapping
60+
options Options
61+
}
62+
63+
// Options represents the various options with which the anonymizer can be configured.
64+
type Options struct {
65+
HashStandardTags bool `yaml:"hash_standard_tags" name:"hash_standard_tags"`
66+
HashCustomTags bool `yaml:"hash_custom_tags" name:"hash_custom_tags"`
67+
HashLogs bool `yaml:"hash_logs" name:"hash_logs"`
68+
HashProcess bool `yaml:"hash_process" name:"hash_process"`
6469
}
6570

6671
// New creates new Anonymizer. The mappingFile stores the mapping from original to
6772
// obfuscated strings, in case later investigations require looking at the original traces.
68-
func New(mappingFile string, logger *zap.Logger) *Anonymizer {
73+
func New(mappingFile string, options Options, logger *zap.Logger) *Anonymizer {
6974
a := &Anonymizer{
7075
mappingFile: mappingFile,
7176
logger: logger,
7277
mapping: mapping{
7378
Services: make(map[string]string),
7479
Operations: make(map[string]string),
7580
},
81+
options: options,
7682
}
7783
if _, err := os.Stat(filepath.Clean(mappingFile)); err == nil {
7884
dat, err := ioutil.ReadFile(filepath.Clean(mappingFile))
@@ -142,18 +148,18 @@ func (a *Anonymizer) AnonymizeSpan(span *model.Span) *uimodel.Span {
142148

143149
outputTags := filterStandardTags(span.Tags)
144150
// when true, the allowedTags are hashed and when false they are preserved as it is
145-
if a.hashStandardTags {
151+
if a.options.HashStandardTags {
146152
outputTags = hashTags(outputTags)
147153
}
148154
// when true, all tags other than allowedTags are hashed, when false they are dropped
149-
if a.hashCustomTags {
155+
if a.options.HashCustomTags {
150156
customTags := hashTags(filterCustomTags(span.Tags))
151157
outputTags = append(outputTags, customTags...)
152158
}
153159
span.Tags = outputTags
154160

155161
// when true, logs are hashed, when false, they are dropped
156-
if a.hashLogs {
162+
if a.options.HashLogs {
157163
for _, log := range span.Logs {
158164
log.Fields = hashTags(log.Fields)
159165
}
@@ -164,7 +170,7 @@ func (a *Anonymizer) AnonymizeSpan(span *model.Span) *uimodel.Span {
164170
span.Process.ServiceName = a.mapServiceName(service)
165171

166172
// when true, process tags are hashed, when false they are dropped
167-
if a.hashProcess {
173+
if a.options.HashProcess {
168174
span.Process.Tags = hashTags(span.Process.Tags)
169175
} else {
170176
span.Process.Tags = nil

cmd/anonymizer/app/anonymizer/anonymizer_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,12 @@ func TestAnonymizer_AnonymizeSpan_AllTrue(t *testing.T) {
103103
Services: make(map[string]string),
104104
Operations: make(map[string]string),
105105
},
106-
hashStandardTags: true,
107-
hashCustomTags: true,
108-
hashProcess: true,
109-
hashLogs: true,
106+
options: Options{
107+
HashStandardTags: true,
108+
HashCustomTags: true,
109+
HashProcess: true,
110+
HashLogs: true,
111+
},
110112
}
111113
_ = anonymizer.AnonymizeSpan(span1)
112114
assert.Equal(t, 3, len(span1.Tags))
@@ -120,10 +122,12 @@ func TestAnonymizer_AnonymizeSpan_AllFalse(t *testing.T) {
120122
Services: make(map[string]string),
121123
Operations: make(map[string]string),
122124
},
123-
hashStandardTags: false,
124-
hashCustomTags: false,
125-
hashProcess: false,
126-
hashLogs: false,
125+
options: Options{
126+
HashStandardTags: false,
127+
HashCustomTags: false,
128+
HashProcess: false,
129+
HashLogs: false,
130+
},
127131
}
128132
_ = anonymizer.AnonymizeSpan(span2)
129133
assert.Equal(t, 2, len(span2.Tags))

cmd/anonymizer/app/flags.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) 2020 The Jaeger Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package app
16+
17+
import (
18+
"github.com/spf13/cobra"
19+
)
20+
21+
// Options represent configurable parameters for jaeger-anonymizer
22+
type Options struct {
23+
QueryGRPCHostPort string
24+
MaxSpansCount int
25+
TraceID string
26+
OutputDir string
27+
HashStandardTags bool
28+
HashCustomTags bool
29+
HashLogs bool
30+
HashProcess bool
31+
}
32+
33+
const (
34+
queryGRPCHostPortFlag = "query-host-port"
35+
outputDirFlag = "output-dir"
36+
traceIDFlag = "trace-id"
37+
hashStandardTagsFlag = "hash-standard-tags"
38+
hashCustomTagsFlag = "hash-custom-tags"
39+
hashLogsFlag = "hash-logs"
40+
hashProcessFlag = "hash-process"
41+
maxSpansCount = "max-spans-count"
42+
)
43+
44+
// AddFlags adds flags for anonymizer main program
45+
func (o *Options) AddFlags(command *cobra.Command) {
46+
command.Flags().StringVar(
47+
&o.QueryGRPCHostPort,
48+
queryGRPCHostPortFlag,
49+
"localhost:16686",
50+
"The host:port of the jaeger-query endpoint")
51+
command.Flags().StringVar(
52+
&o.OutputDir,
53+
outputDirFlag,
54+
"/tmp",
55+
"The directory to store the anonymized trace")
56+
command.Flags().StringVar(
57+
&o.TraceID,
58+
traceIDFlag,
59+
"",
60+
"The trace-id of trace to anonymize")
61+
command.Flags().BoolVar(
62+
&o.HashStandardTags,
63+
hashStandardTagsFlag,
64+
false,
65+
"Whether to hash standard tags")
66+
command.Flags().BoolVar(
67+
&o.HashCustomTags,
68+
hashCustomTagsFlag,
69+
false,
70+
"Whether to hash custom tags")
71+
command.Flags().BoolVar(
72+
&o.HashLogs,
73+
hashLogsFlag,
74+
false,
75+
"Whether to hash logs")
76+
command.Flags().BoolVar(
77+
&o.HashProcess,
78+
hashProcessFlag,
79+
false,
80+
"Whether to hash process")
81+
command.Flags().IntVar(
82+
&o.MaxSpansCount,
83+
maxSpansCount,
84+
-1,
85+
"The maximum number of spans to anonymize")
86+
87+
// mark traceid flag as mandatory
88+
command.MarkFlagRequired(traceIDFlag)
89+
}

cmd/anonymizer/app/flags_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2020 The Jaeger Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package app
16+
17+
import (
18+
"testing"
19+
20+
"github.com/spf13/cobra"
21+
"github.com/stretchr/testify/assert"
22+
)
23+
24+
func TestOptionsWithDefaultFlags(t *testing.T) {
25+
o := Options{}
26+
c := cobra.Command{}
27+
o.AddFlags(&c)
28+
29+
assert.Equal(t, "localhost:16686", o.QueryGRPCHostPort)
30+
assert.Equal(t, "/tmp", o.OutputDir)
31+
assert.Equal(t, false, o.HashStandardTags)
32+
assert.Equal(t, false, o.HashCustomTags)
33+
assert.Equal(t, false, o.HashLogs)
34+
assert.Equal(t, false, o.HashProcess)
35+
assert.Equal(t, -1, o.MaxSpansCount)
36+
}
37+
38+
func TestOptionsWithFlags(t *testing.T) {
39+
o := Options{}
40+
c := cobra.Command{}
41+
42+
o.AddFlags(&c)
43+
c.ParseFlags([]string{
44+
"--query-host-port=192.168.1.10:16686",
45+
"--output-dir=/data",
46+
"--trace-id=6ef2debb698f2f7c",
47+
"--hash-standard-tags",
48+
"--hash-custom-tags",
49+
"--hash-logs",
50+
"--hash-process",
51+
"--max-spans-count=100",
52+
})
53+
54+
assert.Equal(t, "192.168.1.10:16686", o.QueryGRPCHostPort)
55+
assert.Equal(t, "/data", o.OutputDir)
56+
assert.Equal(t, "6ef2debb698f2f7c", o.TraceID)
57+
assert.Equal(t, true, o.HashStandardTags)
58+
assert.Equal(t, true, o.HashCustomTags)
59+
assert.Equal(t, true, o.HashLogs)
60+
assert.Equal(t, true, o.HashProcess)
61+
assert.Equal(t, 100, o.MaxSpansCount)
62+
}

cmd/anonymizer/app/query/.nocover

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
non-critical test utility

cmd/anonymizer/app/query/query.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (c) 2020 The Jaeger Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package query
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"io"
21+
"time"
22+
23+
"google.golang.org/grpc"
24+
"google.golang.org/grpc/status"
25+
26+
"github.com/jaegertracing/jaeger/model"
27+
"github.com/jaegertracing/jaeger/proto-gen/api_v2"
28+
"github.com/jaegertracing/jaeger/storage/spanstore"
29+
)
30+
31+
// Query represents a jaeger-query's query for trace-id
32+
type Query struct {
33+
client api_v2.QueryServiceClient
34+
conn *grpc.ClientConn
35+
}
36+
37+
// New creates a Query object
38+
func New(addr string) (*Query, error) {
39+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
40+
defer cancel()
41+
42+
conn, err := grpc.DialContext(ctx, addr, grpc.WithInsecure())
43+
if err != nil {
44+
return nil, fmt.Errorf("failed to connect with the jaeger-query service: %w", err)
45+
}
46+
47+
return &Query{
48+
client: api_v2.NewQueryServiceClient(conn),
49+
conn: conn,
50+
}, nil
51+
}
52+
53+
// unwrapNotFoundErr is a conversion function
54+
func unwrapNotFoundErr(err error) error {
55+
if s, _ := status.FromError(err); s != nil {
56+
if s.Message() == spanstore.ErrTraceNotFound.Error() {
57+
return spanstore.ErrTraceNotFound
58+
}
59+
}
60+
return err
61+
}
62+
63+
// QueryTrace queries for a trace and returns all spans inside it
64+
func (q *Query) QueryTrace(traceID string) ([]model.Span, error) {
65+
mTraceID, err := model.TraceIDFromString(traceID)
66+
if err != nil {
67+
return nil, fmt.Errorf("failed to convert the provided trace id: %w", err)
68+
}
69+
70+
stream, err := q.client.GetTrace(context.Background(), &api_v2.GetTraceRequest{
71+
TraceID: mTraceID,
72+
})
73+
if err != nil {
74+
return nil, unwrapNotFoundErr(err)
75+
}
76+
77+
var spans []model.Span
78+
for received, err := stream.Recv(); err != io.EOF; received, err = stream.Recv() {
79+
if err != nil {
80+
return nil, unwrapNotFoundErr(err)
81+
}
82+
for i := range received.Spans {
83+
spans = append(spans, received.Spans[i])
84+
}
85+
}
86+
87+
return spans, nil
88+
}

0 commit comments

Comments
 (0)