Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 9 additions & 1 deletion lib/datadog/core/environment/process.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ module Process
# @return [String] comma-separated normalized key:value pairs
def self.serialized
return @serialized if defined?(@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
return @tags if defined?(@tags)
tags = []

workdir = TagNormalizer.normalize_process_value(entrypoint_workdir.to_s)
Expand All @@ -27,7 +35,7 @@ def self.serialized

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

@serialized = tags.join(',').freeze
@tags = tags.freeze
end

# Returns the last segment of the working directory of the process
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
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[::String]

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

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

private

def self.entrypoint_workdir: () -> ::String
Expand Down
107 changes: 81 additions & 26 deletions spec/datadog/core/environment/process_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,6 @@
describe '::serialized' do
subject(:serialized) { described_class.serialized }

def reset_serialized!
described_class.remove_instance_variable(:@serialized) if described_class.instance_variable_defined?(:@serialized)
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_serialized!
end

after do
reset_serialized!
end
end

it { is_expected.to be_a_kind_of(String) }

it 'returns the same object when called multiple times' do
Expand Down Expand Up @@ -147,4 +121,85 @@ def reset_serialized!
end
end
end
describe '::tags' do
subject(:tags) { described_class.tags }

it { is_expected.to be_a_kind_of(Array) }

it 'is an array of strings' do
expect(tags).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
second_call = described_class.tags
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.length).to eq(4)
expect(described_class.tags).to include('entrypoint.workdir:app')
expect(described_class.tags).to include('entrypoint.name:executable')
expect(described_class.tags).to include('entrypoint.basedir:expectedbasedir')
expect(described_class.tags).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.length).to eq(4)
expect(described_class.tags).to include('entrypoint.workdir:app')
expect(described_class.tags).to include('entrypoint.name:irb')
expect(described_class.tags).to include('entrypoint.basedir:app')
expect(described_class.tags).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.length).to eq(4)
expect(described_class.tags).to include('entrypoint.workdir:app')
expect(described_class.tags).to include('entrypoint.name:rubyapp.rb')
expect(described_class.tags).to include('entrypoint.basedir:path')
expect(described_class.tags).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.length).to eq(4)
expect(described_class.tags).to include('entrypoint.workdir:app')
expect(described_class.tags).to include('entrypoint.name:foo_bar')
expect(described_class.tags).to include('entrypoint.basedir:path')
expect(described_class.tags).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.length).to eq(4)
expect(described_class.tags).to include('entrypoint.workdir:app')
expect(described_class.tags).to include('entrypoint.name:rails')
expect(described_class.tags).to include('entrypoint.basedir:bin')
expect(described_class.tags).to include('entrypoint.type:script')
end
end
end
end
27 changes: 27 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,33 @@
end
end

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

context 'when process tags propagation is enabled' do
include_context 'with mocked process environment'
before do
allow(Datadog.configuration).to receive(:experimental_propagate_process_tags_enabled).and_return(true)
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
# Currently false by default
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
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
require 'support/network_helpers'
require 'support/object_space_helper'
require 'support/platform_helpers'
require 'support/process_helpers'
require 'support/span_helpers'
require 'support/spy_transport'
require 'support/synchronization_helpers'
Expand Down
31 changes: 31 additions & 0 deletions spec/support/process_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

RSpec.shared_context 'with mocked process environment' do
def reset_memoized_variables!
[:@serialized, :@tags].each do |variable|
Datadog::Core::Environment::Process.remove_instance_variable(variable) if
Datadog::Core::Environment::Process.instance_variable_defined?(variable)
end
end

let(:program_name) { 'bin/rspec' }
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_memoized_variables!
end

after do
reset_memoized_variables!
end
end
Loading