Skip to content

Commit c4dca01

Browse files
committed
Add user agent to telemetry
Update from code review
1 parent 47d9699 commit c4dca01

File tree

7 files changed

+122
-62
lines changed

7 files changed

+122
-62
lines changed

lib/cloud_controller/rack_app_builder.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
require 'syslog/logger'
2-
require 'vcap_request_id'
2+
require 'vcap_request_context_setter'
33
require 'cors'
44
require 'request_metrics'
55
require 'request_logs'
@@ -24,7 +24,7 @@ def build(config, request_metrics, request_logs)
2424
Rack::Builder.new do
2525
use CloudFoundry::Middleware::RequestMetrics, request_metrics
2626
use CloudFoundry::Middleware::Cors, config.get(:allowed_cors_domains)
27-
use CloudFoundry::Middleware::VcapRequestId
27+
use CloudFoundry::Middleware::VcapRequestContextSetter
2828
use CloudFoundry::Middleware::BelowMinCliWarning if config.get(:warn_if_below_min_cli_version)
2929
use CloudFoundry::Middleware::NewRelicCustomAttributes if config.get(:newrelic_enabled)
3030
use CloudFoundry::Middleware::SecurityContextSetter, configurer

lib/cloud_controller/telemetry_logger.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ def emit(event_name, entries, raw_entries={})
3131
INTEGER_FIELDS.include?(k)
3232
end.transform_values(&:to_i))
3333

34-
# Add user-agent if available
3534
user_agent = ::VCAP::Request.user_agent
3635
converted_entries['user-agent'] = user_agent if user_agent.present?
3736

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
module CloudFoundry
55
module Middleware
6-
class VcapRequestId
6+
class VcapRequestContextSetter
77
def initialize(app)
88
@app = app
99
end

spec/diego/client_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ module Diego
1919
end
2020

2121
before do
22-
# from middleware/vcap_request_id.rb
22+
# from middleware/vcap_request_context_setter.rb
2323
::VCAP::Request.current_id = "#{request_id}::b62be6c2-0f2c-4199-94d3-41a69e00f67d"
2424
allow(Steno).to receive(:logger).with('cc.diego.client').and_return(logger)
2525
allow(logger).to receive(:info)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
require 'spec_helper'
2+
3+
module VCAP::CloudController
4+
RSpec.describe 'VCAP Request ID Middleware - User Agent' do
5+
let(:captured_values) { {} }
6+
7+
before do
8+
allow_any_instance_of(CloudFoundry::Middleware::VcapRequestContextSetter).to receive(:call).and_wrap_original do |original_method, *args|
9+
env = args[0]
10+
result = original_method.call(*args)
11+
captured_values[:user_agent_during_request] = env['HTTP_USER_AGENT']
12+
captured_values[:request_id_during_request] = env['cf.request_id']
13+
14+
result
15+
end
16+
end
17+
18+
describe 'user agent and request id handling' do
19+
let(:space) { VCAP::CloudController::Space.make }
20+
let(:user) { make_developer_for_space(space) }
21+
let(:request_id) { 'test-request-123' }
22+
let(:user_agent) { 'cf/8.7.0 (go1.21.4; amd64 linux)' }
23+
let(:user_headers) do
24+
headers_for(user, user_name: 'roto').merge('HTTP_USER_AGENT' => user_agent)
25+
end
26+
27+
context 'when User-Agent header is provided' do
28+
it 'sets VCAP::Request.user_agent during the request' do
29+
get '/v3/spaces', nil, user_headers
30+
expect(last_response.status).to eq(200)
31+
expect(captured_values[:user_agent_during_request]).to eq(user_agent)
32+
expect(captured_values[:request_id_during_request]).to be_present
33+
# After the request completes, user_agent should be nil
34+
expect(::VCAP::Request.user_agent).to be_nil
35+
end
36+
end
37+
38+
context 'when User-Agent header and HTTP_X_VCAP_REQUEST_ID are provided' do
39+
it 'sets VCAP::Request.user_agent and HTTP_X_VCAP_REQUEST_ID during the request' do
40+
get '/v3/spaces', nil, user_headers.merge('HTTP_X_VCAP_REQUEST_ID' => request_id)
41+
expect(last_response.status).to eq(200)
42+
expect(captured_values[:user_agent_during_request]).to eq(user_agent)
43+
expect(captured_values[:request_id_during_request]).to include(request_id)
44+
expect(last_response.headers['X-VCAP-Request-ID']).to include(request_id)
45+
# After the request completes, user_agent and current_id should be nil
46+
expect(::VCAP::Request.user_agent).to be_nil
47+
expect(::VCAP::Request.current_id).to be_nil
48+
end
49+
end
50+
end
51+
52+
context 'telemetry' do
53+
let(:space) { VCAP::CloudController::Space.make }
54+
let(:org) { space.organization }
55+
let(:user) { make_developer_for_space(space) }
56+
let(:user_agent) { 'cf/8.7.0 (go1.21.4; amd64 linux)' }
57+
let(:user_header) { headers_for(user, user_name: 'roto').merge('HTTP_USER_AGENT' => user_agent) }
58+
let(:logger_spy) { spy('logger') }
59+
let(:stack) { VCAP::CloudController::Stack.make }
60+
let(:buildpack) { VCAP::CloudController::Buildpack.make(stack: stack.name) }
61+
let(:create_request) do
62+
{
63+
name: 'my_app',
64+
lifecycle: {
65+
type: 'buildpack',
66+
data: {
67+
stack: buildpack.stack,
68+
buildpacks: [buildpack.name]
69+
}
70+
},
71+
relationships: {
72+
space: {
73+
data: {
74+
guid: space.guid
75+
}
76+
}
77+
}
78+
}
79+
end
80+
81+
before do
82+
org.add_user(user)
83+
space.add_developer(user)
84+
allow(VCAP::CloudController::TelemetryLogger).to receive(:logger).and_return(logger_spy)
85+
end
86+
87+
it 'includes user-agent in telemetry logs when making a request' do
88+
Timecop.freeze do
89+
post '/v3/apps', create_request.to_json, user_header
90+
91+
parsed_response = Oj.load(last_response.body)
92+
app_guid = parsed_response['guid']
93+
94+
expected_json = {
95+
'telemetry-source' => 'cloud_controller_ng',
96+
'telemetry-time' => Time.now.to_datetime.rfc3339,
97+
'create-app' => {
98+
'api-version' => 'v3',
99+
'app-id' => OpenSSL::Digest::SHA256.hexdigest(app_guid),
100+
'user-id' => OpenSSL::Digest::SHA256.hexdigest(user.guid),
101+
'user-agent' => user_agent
102+
}
103+
}
104+
expect(logger_spy).to have_received(:info) do |actual_json|
105+
actual = Oj.load(actual_json)
106+
expect(actual).to eq(expected_json)
107+
end
108+
expect(last_response.status).to eq(201), last_response.body
109+
end
110+
end
111+
end
112+
end
113+
end

spec/request/vcap_request_id_spec.rb

Lines changed: 0 additions & 52 deletions
This file was deleted.

spec/unit/middleware/vcap_request_id_spec.rb renamed to spec/unit/middleware/vcap_request_context_setter_spec.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
require 'spec_helper'
2-
require 'vcap_request_id'
2+
require 'vcap_request_context_setter'
33

44
module CloudFoundry
55
module Middleware
6-
RSpec.describe VcapRequestId do
7-
let(:middleware) { VcapRequestId.new(app) }
8-
let(:app) { VcapRequestId::FakeApp.new }
6+
RSpec.describe VcapRequestContextSetter do
7+
let(:middleware) { VcapRequestContextSetter.new(app) }
8+
let(:app) { VcapRequestContextSetter::FakeApp.new }
99
let(:app_response) { [200, {}, 'a body'] }
1010
let(:uuid_regex) { '\w+-\w+-\w+-\w+-\w+' }
1111

12-
class VcapRequestId::FakeApp
12+
class VcapRequestContextSetter::FakeApp
1313
attr_accessor :last_request_id, :last_env_input, :last_user_agent
1414

1515
def call(env)

0 commit comments

Comments
 (0)