Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions lib/datadog/core/environment/process.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ def self.serialized
@serialized = tags.join(',').freeze
end

# This method returns an array in the format ["k1:v1","k2:v2","k3:v3"]
# @return [Array<String>] array of normalized key:value pairs
def self.tags_array
return @tags_array if defined?(@tags_array)
tags = []

workdir = TagNormalizer.normalize_process_value(entrypoint_workdir.to_s)
tags << "#{Environment::Ext::TAG_ENTRYPOINT_WORKDIR}:#{workdir}" unless workdir.empty?

entry_name = TagNormalizer.normalize_process_value(entrypoint_name.to_s)
tags << "#{Environment::Ext::TAG_ENTRYPOINT_NAME}:#{entry_name}" unless entry_name.empty?

basedir = TagNormalizer.normalize_process_value(entrypoint_basedir.to_s)
tags << "#{Environment::Ext::TAG_ENTRYPOINT_BASEDIR}:#{basedir}" unless basedir.empty?

tags << "#{Environment::Ext::TAG_ENTRYPOINT_TYPE}:#{TagNormalizer.normalize(entrypoint_type, remove_digit_start_char: false)}"

@tags_array = tags.freeze
end

# Returns the last segment of the working directory of the process
# Example: /app/myapp -> myapp
# @return [String] the last segment of the working directory
Expand Down
5 changes: 5 additions & 0 deletions lib/datadog/core/remote/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ def payload # standard:disable Metrics/MethodLength

client_tracer[:app_version] = app_version if app_version

if Datadog.configuration.experimental_propagate_process_tags_enabled
process_tags = Core::Environment::Process.tags_array
client_tracer[:process_tags] = process_tags if process_tags.any?
end

{
client: {
state: {
Expand Down
3 changes: 3 additions & 0 deletions sig/datadog/core/environment/process.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ module Datadog
module Environment
module Process
@serialized: ::String
@tags_array: Array[::String]

def self.serialized: () -> ::String

def self.tags_array: () -> ::Array[::String]

private

def self.entrypoint_workdir: () -> ::String
Expand Down
107 changes: 107 additions & 0 deletions spec/datadog/core/environment/process_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,111 @@ def reset_serialized!
end
end
end
describe '::tags_array' do
subject(:tags_array) { described_class.tags_array }

def reset_tags_array!
described_class.remove_instance_variable(:@tags_array) if described_class.instance_variable_defined?(:@tags_array)
end

shared_context 'with mocked process environment' do
let(:pwd) { '/app' }

around do |example|
@original_0 = $0
$0 = program_name
example.run
$0 = @original_0
end

before do
allow(Dir).to receive(:pwd).and_return(pwd)
allow(File).to receive(:expand_path).and_call_original
allow(File).to receive(:expand_path).with('.').and_return('/app')
reset_tags_array!
end

after do
reset_tags_array!
end
end

it { is_expected.to be_a_kind_of(Array) }

it 'is an array of strings' do
expect(tags_array).to all(be_a(String))
end

it 'returns the same object when called multiple times' do
# Processes are fixed so no need to recompute this on each call
first_call = described_class.tags_array
second_call = described_class.tags_array
expect(first_call).to equal(second_call)
end

context 'with /expectedbasedir/executable' do
include_context 'with mocked process environment'
let(:program_name) { '/expectedbasedir/executable' }

it 'extracts out the tag array correctly' do
expect(tags_array.length).to eq(4)
expect(described_class.tags_array).to include('entrypoint.workdir:app')
expect(described_class.tags_array).to include('entrypoint.name:executable')
expect(described_class.tags_array).to include('entrypoint.basedir:expectedbasedir')
expect(described_class.tags_array).to include('entrypoint.type:script')
end
end

context 'with irb' do
include_context 'with mocked process environment'
let(:program_name) { 'irb' }

it 'extracts out the tag array correctly' do
expect(tags_array.length).to eq(4)
expect(described_class.tags_array).to include('entrypoint.workdir:app')
expect(described_class.tags_array).to include('entrypoint.name:irb')
expect(described_class.tags_array).to include('entrypoint.basedir:app')
expect(described_class.tags_array).to include('entrypoint.type:script')
end
end

context 'with my/path/rubyapp.rb' do
include_context 'with mocked process environment'
let(:program_name) { 'my/path/rubyapp.rb' }

it 'extracts out the tag array correctly' do
expect(tags_array.length).to eq(4)
expect(described_class.tags_array).to include('entrypoint.workdir:app')
expect(described_class.tags_array).to include('entrypoint.name:rubyapp.rb')
expect(described_class.tags_array).to include('entrypoint.basedir:path')
expect(described_class.tags_array).to include('entrypoint.type:script')
end
end

context 'with my/path/foo:,bar' do
include_context 'with mocked process environment'
let(:program_name) { 'my/path/foo:,bar' }

it 'extracts out the tag array correctly' do
expect(tags_array.length).to eq(4)
expect(described_class.tags_array).to include('entrypoint.workdir:app')
expect(described_class.tags_array).to include('entrypoint.name:foo_bar')
expect(described_class.tags_array).to include('entrypoint.basedir:path')
expect(described_class.tags_array).to include('entrypoint.type:script')
end
end

context 'with bin/rails' do
include_context 'with mocked process environment'
let(:program_name) { 'bin/rails' }

it 'extracts out the tags array correctly' do
expect(tags_array.length).to eq(4)
expect(described_class.tags_array).to include('entrypoint.workdir:app')
expect(described_class.tags_array).to include('entrypoint.name:rails')
expect(described_class.tags_array).to include('entrypoint.basedir:bin')
expect(described_class.tags_array).to include('entrypoint.type:script')
end
end
end
end
35 changes: 35 additions & 0 deletions spec/datadog/core/remote/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,41 @@
end
end

context 'process_tags' do
let(:client_payload) { client.send(:payload)[:client] }

context 'when process tags propagation is enabled' do
before do
allow(Datadog.configuration).to receive(:experimental_propagate_process_tags_enabled).and_return(true)
if Datadog::Core::Environment::Process.instance_variable_defined?(:@tags_array)
Datadog::Core::Environment::Process.remove_instance_variable(:@tags_array)
end
end

it 'has process tags in the payload' do
process_tags = client_payload[:client_tracer][:process_tags]
expect(process_tags).to be_a(Array)
expect(process_tags).to include('entrypoint.workdir:app')
expect(process_tags).to include('entrypoint.name:rspec')
expect(process_tags).to include('entrypoint.basedir:bin')
expect(process_tags).to include('entrypoint.type:script')
end
end

context 'when process tags propagation is not enabled' do
# Current false by default
before do
if Datadog::Core::Environment::Process.instance_variable_defined?(:@tags_array)
Datadog::Core::Environment::Process.remove_instance_variable(:@tags_array)
end
end

it 'does not have process tags in the payload' do
expect(client_payload[:client_tracer]).not_to have_key(:process_tags)
end
end
end

context 'cached_target_files' do
it 'returns cached_target_files' do
state = repository.state
Expand Down
Loading