forked from open-telemetry/opentelemetry-collector
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
183 lines (166 loc) · 6.35 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Copyright The OpenTelemetry 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 client contains generic representations of clients connecting to
// different receivers. Components, such as processors or exporters, can make
// use of this information to make decisions related to grouping of batches,
// tenancy, load balancing, tagging, among others.
//
// The structs defined here are typically used within the context that is
// propagated down the pipeline, with the values being produced by
// authenticators and/or receivers, and consumed by processors and exporters.
//
// Producers
//
// Receivers are responsible for obtaining a client.Info from the current
// context and enhancing the client.Info with the net.Addr from the peer,
// storing a new client.Info into the context that it passes down. For HTTP
// requests, the net.Addr is typically the IP address of the client.
//
// Typically, however, receivers would delegate this processing to helpers such
// as the confighttp or configgrpc packages: both contain interceptors that will
// enhance the context with the client.Info, such that no actions are needed by
// receivers that are built using confighttp.HTTPServerSettings or
// configgrpc.GRPCServerSettings.
//
// Authenticators are responsible for obtaining a client.Info from the current
// context, enhancing the client.Info with an implementation of client.AuthData,
// and storing a new client.Info into the context that it passes down. The
// attribute names should be documented with their return types and considered
// part of the public API for the authenticator.
//
// Consumers
//
// Provided that the pipeline does not contain processors that would discard or
// rewrite the context, such as the batch processor, processors and exporters
// have access to the client.Info via client.FromContext. Among other usages,
// this data can be used to:
//
// - annotate data points with authentication data (username, tenant, ...)
//
// - route data points based on authentication data
//
// - rate limit client calls based on IP addresses
//
// Processors and exporters relying on the existence of data from the
// client.Info, especially client.AuthData, should clearly document this as part
// of the component's README file. The expected pattern for consuming data is to
// allow users to specify the attribute name to use in the component. The
// expected data type should also be communicated to users, who should then
// compare this with the authenticators that are part of the pipeline. For
// example, assuming that the OIDC authenticator pushes a "subject" string
// attribute and that we have a hypothetical "authprinter" processor that prints
// the "username" to the console, this is how an OpenTelemetry Collector
// configuration would look like:
//
// extensions:
// oidc:
// issuer_url: http://localhost:8080/auth/realms/opentelemetry
// audience: collector
// receivers:
// otlp:
// protocols:
// grpc:
// auth:
// authenticator: oidc
// processors:
// authprinter:
// attribute: subject
// exporters:
// logging:
// service:
// extensions: [oidc]
// pipelines:
// traces:
// receivers: [otlp]
// processors: [authprinter]
// exporters: [logging]
package client // import "go.opentelemetry.io/collector/client"
import (
"context"
"net"
"strings"
)
type ctxKey struct{}
// Info contains data related to the clients connecting to receivers.
type Info struct {
// Addr for the client connecting to this collector. Available in a
// best-effort basis, and generally reliable for receivers making use of
// confighttp.ToServer and configgrpc.ToServerOption.
Addr net.Addr
// Auth information from the incoming request as provided by
// configauth.ServerAuthenticator implementations tied to the receiver for
// this connection.
Auth AuthData
// Metadata is the request metadata from the client connecting to this connector.
// Experimental: *NOTE* this structure is subject to change or removal in the future.
Metadata Metadata
}
// Metadata is an immutable map, meant to contain request metadata.
type Metadata struct {
data map[string][]string
}
// AuthData represents the authentication data as seen by authenticators tied to
// the receivers.
type AuthData interface {
// GetAttribute returns the value for the given attribute. Authenticator
// implementations might define different data types for different
// attributes. While "string" is used most of the time, a key named
// "membership" might return a list of strings.
GetAttribute(string) interface{}
// GetAttributes returns the names of all attributes in this authentication
// data.
GetAttributeNames() []string
}
const MetadataHostName = "Host"
// NewContext takes an existing context and derives a new context with the
// client.Info value stored on it.
func NewContext(ctx context.Context, c Info) context.Context {
return context.WithValue(ctx, ctxKey{}, c)
}
// FromContext takes a context and returns a ClientInfo from it.
// When a ClientInfo isn't present, a new empty one is returned.
func FromContext(ctx context.Context) Info {
c, ok := ctx.Value(ctxKey{}).(Info)
if !ok {
c = Info{}
}
return c
}
// NewMetadata creates a new Metadata object to use in Info. md is used as-is.
func NewMetadata(md map[string][]string) Metadata {
return Metadata{
data: md,
}
}
// Get gets the value of the key from metadata, returning a copy.
func (m Metadata) Get(key string) []string {
vals := m.data[key]
if len(vals) == 0 {
// we didn't find the key, but perhaps it just has different cases?
for k, v := range m.data {
if strings.EqualFold(key, k) {
vals = v
// we optimize for the next lookup
m.data[key] = v
}
}
// if it's still not found, it's really not here
if len(vals) == 0 {
return nil
}
}
ret := make([]string, len(vals))
copy(ret, vals)
return ret
}