1// Code created by gotmpl. DO NOT MODIFY.
2// source: internal/shared/otlp/envconfig/envconfig.go.tmpl
3
4// Copyright The OpenTelemetry Authors
5//
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18package envconfig
19
20import (
21 "crypto/tls"
22 "crypto/x509"
23 "errors"
24 "fmt"
25 "net/url"
26 "strconv"
27 "strings"
28 "time"
29
30 "go.opentelemetry.io/otel/internal/global"
31)
32
33// ConfigFn is the generic function used to set a config.
34type ConfigFn func(*EnvOptionsReader)
35
36// EnvOptionsReader reads the required environment variables.
37type EnvOptionsReader struct {
38 GetEnv func(string) string
39 ReadFile func(string) ([]byte, error)
40 Namespace string
41}
42
43// Apply runs every ConfigFn.
44func (e *EnvOptionsReader) Apply(opts ...ConfigFn) {
45 for _, o := range opts {
46 o(e)
47 }
48}
49
50// GetEnvValue gets an OTLP environment variable value of the specified key
51// using the GetEnv function.
52// This function prepends the OTLP specified namespace to all key lookups.
53func (e *EnvOptionsReader) GetEnvValue(key string) (string, bool) {
54 v := strings.TrimSpace(e.GetEnv(keyWithNamespace(e.Namespace, key)))
55 return v, v != ""
56}
57
58// WithString retrieves the specified config and passes it to ConfigFn as a string.
59func WithString(n string, fn func(string)) func(e *EnvOptionsReader) {
60 return func(e *EnvOptionsReader) {
61 if v, ok := e.GetEnvValue(n); ok {
62 fn(v)
63 }
64 }
65}
66
67// WithBool returns a ConfigFn that reads the environment variable n and if it exists passes its parsed bool value to fn.
68func WithBool(n string, fn func(bool)) ConfigFn {
69 return func(e *EnvOptionsReader) {
70 if v, ok := e.GetEnvValue(n); ok {
71 b := strings.ToLower(v) == "true"
72 fn(b)
73 }
74 }
75}
76
77// WithDuration retrieves the specified config and passes it to ConfigFn as a duration.
78func WithDuration(n string, fn func(time.Duration)) func(e *EnvOptionsReader) {
79 return func(e *EnvOptionsReader) {
80 if v, ok := e.GetEnvValue(n); ok {
81 d, err := strconv.Atoi(v)
82 if err != nil {
83 global.Error(err, "parse duration", "input", v)
84 return
85 }
86 fn(time.Duration(d) * time.Millisecond)
87 }
88 }
89}
90
91// WithHeaders retrieves the specified config and passes it to ConfigFn as a map of HTTP headers.
92func WithHeaders(n string, fn func(map[string]string)) func(e *EnvOptionsReader) {
93 return func(e *EnvOptionsReader) {
94 if v, ok := e.GetEnvValue(n); ok {
95 fn(stringToHeader(v))
96 }
97 }
98}
99
100// WithURL retrieves the specified config and passes it to ConfigFn as a net/url.URL.
101func WithURL(n string, fn func(*url.URL)) func(e *EnvOptionsReader) {
102 return func(e *EnvOptionsReader) {
103 if v, ok := e.GetEnvValue(n); ok {
104 u, err := url.Parse(v)
105 if err != nil {
106 global.Error(err, "parse url", "input", v)
107 return
108 }
109 fn(u)
110 }
111 }
112}
113
114// WithCertPool returns a ConfigFn that reads the environment variable n as a filepath to a TLS certificate pool. If it exists, it is parsed as a crypto/x509.CertPool and it is passed to fn.
115func WithCertPool(n string, fn func(*x509.CertPool)) ConfigFn {
116 return func(e *EnvOptionsReader) {
117 if v, ok := e.GetEnvValue(n); ok {
118 b, err := e.ReadFile(v)
119 if err != nil {
120 global.Error(err, "read tls ca cert file", "file", v)
121 return
122 }
123 c, err := createCertPool(b)
124 if err != nil {
125 global.Error(err, "create tls cert pool")
126 return
127 }
128 fn(c)
129 }
130 }
131}
132
133// WithClientCert returns a ConfigFn that reads the environment variable nc and nk as filepaths to a client certificate and key pair. If they exists, they are parsed as a crypto/tls.Certificate and it is passed to fn.
134func WithClientCert(nc, nk string, fn func(tls.Certificate)) ConfigFn {
135 return func(e *EnvOptionsReader) {
136 vc, okc := e.GetEnvValue(nc)
137 vk, okk := e.GetEnvValue(nk)
138 if !okc || !okk {
139 return
140 }
141 cert, err := e.ReadFile(vc)
142 if err != nil {
143 global.Error(err, "read tls client cert", "file", vc)
144 return
145 }
146 key, err := e.ReadFile(vk)
147 if err != nil {
148 global.Error(err, "read tls client key", "file", vk)
149 return
150 }
151 crt, err := tls.X509KeyPair(cert, key)
152 if err != nil {
153 global.Error(err, "create tls client key pair")
154 return
155 }
156 fn(crt)
157 }
158}
159
160func keyWithNamespace(ns, key string) string {
161 if ns == "" {
162 return key
163 }
164 return fmt.Sprintf("%s_%s", ns, key)
165}
166
167func stringToHeader(value string) map[string]string {
168 headersPairs := strings.Split(value, ",")
169 headers := make(map[string]string)
170
171 for _, header := range headersPairs {
172 n, v, found := strings.Cut(header, "=")
173 if !found {
174 global.Error(errors.New("missing '="), "parse headers", "input", header)
175 continue
176 }
177 name, err := url.PathUnescape(n)
178 if err != nil {
179 global.Error(err, "escape header key", "key", n)
180 continue
181 }
182 trimmedName := strings.TrimSpace(name)
183 value, err := url.PathUnescape(v)
184 if err != nil {
185 global.Error(err, "escape header value", "value", v)
186 continue
187 }
188 trimmedValue := strings.TrimSpace(value)
189
190 headers[trimmedName] = trimmedValue
191 }
192
193 return headers
194}
195
196func createCertPool(certBytes []byte) (*x509.CertPool, error) {
197 cp := x509.NewCertPool()
198 if ok := cp.AppendCertsFromPEM(certBytes); !ok {
199 return nil, errors.New("failed to append certificate to the cert pool")
200 }
201 return cp, nil
202}
View as plain text