Skip to content

Commit 37b425f

Browse files
committed
Add B3 trace IDs to cf cli commands
1 parent dfe2201 commit 37b425f

File tree

10 files changed

+302
-0
lines changed

10 files changed

+302
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package wrapper
2+
3+
import (
4+
"code.cloudfoundry.org/cli/api/cloudcontroller"
5+
"code.cloudfoundry.org/cli/api/shared"
6+
)
7+
8+
// CCTraceHeaderRequest is a wrapper that adds b3 trace headers to requests.
9+
type CCTraceHeaderRequest struct {
10+
headers *shared.TraceHeaders
11+
connection cloudcontroller.Connection
12+
}
13+
14+
// NewCCTraceHeaderRequest returns a pointer to a CCTraceHeaderRequest wrapper.
15+
func NewCCTraceHeaderRequest(trace, span string) *CCTraceHeaderRequest {
16+
return &CCTraceHeaderRequest{
17+
headers: shared.NewTraceHeaders(trace, span),
18+
}
19+
}
20+
21+
// Add tracing headers
22+
func (t *CCTraceHeaderRequest) Make(request *cloudcontroller.Request, passedResponse *cloudcontroller.Response) error {
23+
t.headers.SetHeaders(request.Request, passedResponse.HTTPResponse)
24+
return t.connection.Make(request, passedResponse)
25+
}
26+
27+
// Wrap sets the connection in the CCTraceHeaderRequest and returns itself.
28+
func (t *CCTraceHeaderRequest) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection {
29+
t.connection = innerconnection
30+
return t
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package wrapper
2+
3+
import (
4+
"code.cloudfoundry.org/cli/api/router"
5+
"code.cloudfoundry.org/cli/api/shared"
6+
)
7+
8+
// RoutingTraceHeaderRequest is a wrapper that adds b3 trace headers to requests.
9+
type RoutingTraceHeaderRequest struct {
10+
headers *shared.TraceHeaders
11+
connection router.Connection
12+
}
13+
14+
// NewRoutingTraceHeaderRequest returns a pointer to a RoutingTraceHeaderRequest wrapper.
15+
func NewRoutingTraceHeaderRequest(trace, span string) *RoutingTraceHeaderRequest {
16+
return &RoutingTraceHeaderRequest{
17+
headers: shared.NewTraceHeaders(trace, span),
18+
}
19+
}
20+
21+
// Add tracing headers
22+
func (t *RoutingTraceHeaderRequest) Make(request *router.Request, passedResponse *router.Response) error {
23+
t.headers.SetHeaders(request.Request, passedResponse.HTTPResponse)
24+
return t.connection.Make(request, passedResponse)
25+
}
26+
27+
// Wrap sets the connection in the RoutingTraceHeaderRequest and returns itself.
28+
func (t *RoutingTraceHeaderRequest) Wrap(innerconnection router.Connection) router.Connection {
29+
t.connection = innerconnection
30+
return t
31+
}

api/shared/b3_trace_headers.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package shared
2+
3+
import (
4+
"net/http"
5+
)
6+
7+
const (
8+
B3TraceIDHeader = "X-B3-TraceId"
9+
B3SpanIDHeader = "X-B3-SpanId"
10+
)
11+
12+
// TODO
13+
// 1. tests
14+
15+
// TraceHeaders sets b3 trace headers to requests.
16+
type TraceHeaders struct {
17+
b3trace string
18+
b3span string
19+
}
20+
21+
// NewTraceHeaders returns a pointer to a TraceHeaderRequest.
22+
func NewTraceHeaders(trace, span string) *TraceHeaders {
23+
return &TraceHeaders{
24+
b3trace: trace,
25+
b3span: span,
26+
}
27+
}
28+
29+
// Add tracing headers if they are not already set.
30+
func (t *TraceHeaders) SetHeaders(request *http.Request, passedResponse *http.Response) {
31+
// only override the trace headers if they are not already set (e.g. already explicitly set by cf curl)
32+
if request.Header.Get(B3TraceIDHeader) == "" {
33+
request.Header.Add(B3TraceIDHeader, t.b3trace)
34+
}
35+
if request.Header.Get(B3SpanIDHeader) == "" {
36+
request.Header.Add(B3SpanIDHeader, t.b3span)
37+
}
38+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package wrapper
2+
3+
import (
4+
"net/http"
5+
6+
"code.cloudfoundry.org/cli/api/shared"
7+
"code.cloudfoundry.org/cli/api/uaa"
8+
)
9+
10+
// UAATraceHeaderRequest is a wrapper that adds b3 trace headers to requests.
11+
type UAATraceHeaderRequest struct {
12+
headers *shared.TraceHeaders
13+
connection uaa.Connection
14+
}
15+
16+
// NewUAATraceHeaderRequest returns a pointer to a UAATraceHeaderRequest wrapper.
17+
func NewUAATraceHeaderRequest(trace, span string) *UAATraceHeaderRequest {
18+
return &UAATraceHeaderRequest{
19+
headers: shared.NewTraceHeaders(trace, span),
20+
}
21+
}
22+
23+
// Add tracing headers
24+
func (t *UAATraceHeaderRequest) Make(request *http.Request, passedResponse *uaa.Response) error {
25+
t.headers.SetHeaders(request, passedResponse.HTTPResponse)
26+
return t.connection.Make(request, passedResponse)
27+
}
28+
29+
// Wrap sets the connection in the UAATraceHeaderRequest and returns itself.
30+
func (t *UAATraceHeaderRequest) Wrap(innerconnection uaa.Connection) uaa.Connection {
31+
t.connection = innerconnection
32+
return t
33+
}

command/commandfakes/fake_config.go

Lines changed: 130 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

command/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ type Config interface {
1515
AddPluginRepository(name string, url string)
1616
AuthorizationEndpoint() string
1717
APIVersion() string
18+
B3TraceID() string
19+
B3SpanID() string
1820
BinaryName() string
1921
BinaryVersion() string
2022
CFPassword() string

command/v7/shared/new_clients.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func NewWrappedCloudControllerClient(config command.Config, ui command.UI, extra
4747
}
4848

4949
ccWrappers = append(ccWrappers, extraWrappers...)
50+
ccWrappers = append(ccWrappers, ccWrapper.NewCCTraceHeaderRequest(config.B3TraceID(), config.B3SpanID()))
5051
ccWrappers = append(ccWrappers, ccWrapper.NewRetryRequest(config.RequestRetryCount()))
5152

5253
return ccv3.NewClient(ccv3.Config{
@@ -85,6 +86,7 @@ func newWrappedUAAClient(config command.Config, ui command.UI) (*uaa.Client, err
8586

8687
uaaAuthWrapper := uaaWrapper.NewUAAAuthentication(uaaClient, config)
8788
uaaClient.WrapConnection(uaaAuthWrapper)
89+
uaaClient.WrapConnection(uaaWrapper.NewUAATraceHeaderRequest(config.B3TraceID(), config.B3SpanID()))
8890
uaaClient.WrapConnection(uaaWrapper.NewRetryRequest(config.RequestRetryCount()))
8991

9092
err = uaaClient.SetupResources(config.UAAEndpoint(), config.AuthorizationEndpoint())

util/configv3/env.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"strconv"
66
"strings"
77
"time"
8+
9+
"code.cloudfoundry.org/cli/util/random"
810
)
911

1012
// EnvOverride represents all the environment variables read by the CF CLI
@@ -20,6 +22,8 @@ type EnvOverride struct {
2022
CFStartupTimeout string
2123
CFTrace string
2224
CFUsername string
25+
CFB3TraceID string
26+
CFB3SpanID string
2327
DockerPassword string
2428
CNBCredentials string
2529
Experimental string
@@ -160,3 +164,17 @@ func (config *Config) StartupTimeout() time.Duration {
160164

161165
return DefaultStartupTimeout
162166
}
167+
168+
func (config *Config) B3TraceID() string {
169+
if config.ENV.CFB3TraceID == "" {
170+
config.ENV.CFB3TraceID = random.GenerateHex(32)
171+
}
172+
return config.ENV.CFB3TraceID
173+
}
174+
175+
func (config *Config) B3SpanID() string {
176+
if config.ENV.CFB3SpanID == "" {
177+
config.ENV.CFB3SpanID = random.GenerateHex(16)
178+
}
179+
return config.ENV.CFB3SpanID
180+
}

util/configv3/load_config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ func LoadConfig(flags ...FlagOverride) (*Config, error) {
127127
CFStartupTimeout: os.Getenv("CF_STARTUP_TIMEOUT"),
128128
CFTrace: os.Getenv("CF_TRACE"),
129129
CFUsername: os.Getenv("CF_USERNAME"),
130+
CFB3TraceID: os.Getenv("CF_B3_TRACE_ID"),
131+
CFB3SpanID: os.Getenv("CF_B3_SPAN_ID"),
130132
DockerPassword: os.Getenv("CF_DOCKER_PASSWORD"),
131133
CNBCredentials: os.Getenv("CNB_REGISTRY_CREDS"),
132134
Experimental: os.Getenv("CF_CLI_EXPERIMENTAL"),

util/random/hex.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package random
2+
3+
import (
4+
"crypto/rand"
5+
"encoding/hex"
6+
)
7+
8+
func GenerateHex(length int) string {
9+
b := make([]byte, length/2)
10+
if _, err := rand.Read(b); err != nil {
11+
panic(err)
12+
}
13+
14+
return hex.EncodeToString(b)
15+
}

0 commit comments

Comments
 (0)