|
1 |
| -if ENV['DD_TRACE_SKIP_LIB_INJECTION'] == 'true' |
2 |
| - # Skip |
3 |
| -else |
4 |
| - utils = Module.new do |
5 |
| - module_function |
| 1 | +# This file's intent is to be parseable and executable by all ruby versions |
| 2 | +# to call into the main one only for versions for which it is known-compatible |
| 3 | +# with at the language level. |
6 | 4 |
|
7 |
| - def debug(msg) |
8 |
| - $stdout.puts "[datadog][#{pid}][#{$0}] #{msg}" if ENV['DD_TRACE_DEBUG'] == 'true' |
9 |
| - end |
10 |
| - |
11 |
| - def error(msg) |
12 |
| - warn "[datadog][#{pid}][#{$0}] #{msg}" |
13 |
| - end |
14 |
| - |
15 |
| - def pid |
16 |
| - Process.respond_to?(:pid) ? Process.pid : 0 |
17 |
| - end |
18 |
| - |
19 |
| - def root |
20 |
| - File.expand_path(File.join(File.dirname(__FILE__), '.')) |
21 |
| - end |
22 |
| - |
23 |
| - def path |
24 |
| - major, minor, = RUBY_VERSION.split('.') |
25 |
| - ruby_api_version = "#{major}.#{minor}.0" |
26 |
| - |
27 |
| - "#{root}/#{ruby_api_version}" |
28 |
| - end |
29 |
| - |
30 |
| - def version |
31 |
| - File.exist?("#{root}/version") ? File.read("#{root}/version").chomp : 'unknown' |
32 |
| - end |
33 |
| - end |
34 |
| - |
35 |
| - telemetry = Module.new do |
36 |
| - module_function |
37 |
| - |
38 |
| - def emit(pid, version, events) |
39 |
| - payload = { |
40 |
| - metadata: { |
41 |
| - language_name: 'ruby', |
42 |
| - language_version: RUBY_VERSION, |
43 |
| - runtime_name: RUBY_ENGINE, |
44 |
| - runtime_version: RUBY_VERSION, |
45 |
| - tracer_version: version, |
46 |
| - pid: pid |
47 |
| - }, |
48 |
| - points: events |
49 |
| - } |
50 |
| - |
51 |
| - fowarder = ENV['DD_TELEMETRY_FORWARDER_PATH'] |
52 |
| - |
53 |
| - if fowarder && !fowarder.empty? |
54 |
| - require 'open3' |
55 |
| - require 'json' |
56 |
| - |
57 |
| - Open3.capture2e(fowarder, 'library_entrypoint', stdin_data: payload.to_json) |
58 |
| - end |
59 |
| - end |
60 |
| - end |
61 |
| - |
62 |
| - pid = utils.pid |
63 |
| - |
64 |
| - if Process.respond_to?(:fork) |
65 |
| - utils.debug 'Starts injection' |
66 |
| - |
67 |
| - require 'rubygems' |
68 |
| - |
69 |
| - read, write = IO.pipe |
70 |
| - |
71 |
| - fork do |
72 |
| - read.close |
73 |
| - |
74 |
| - require 'open3' |
75 |
| - require 'bundler' |
76 |
| - require 'bundler/cli' |
77 |
| - require 'fileutils' |
78 |
| - |
79 |
| - precheck = Module.new do |
80 |
| - module_function |
81 |
| - |
82 |
| - def in_bundle? |
83 |
| - Bundler::SharedHelpers.in_bundle? |
84 |
| - end |
85 |
| - |
86 |
| - def runtime_supported? |
87 |
| - major, minor, = RUBY_VERSION.split('.') |
88 |
| - ruby_api_version = "#{major}.#{minor}.0" |
89 |
| - |
90 |
| - supported_ruby_api_versions = ['2.7.0', '3.0.0', '3.1.0', '3.2.0', '3.3.0'].freeze |
91 |
| - |
92 |
| - RUBY_ENGINE == 'ruby' && supported_ruby_api_versions.any? { |v| ruby_api_version == v } |
93 |
| - end |
94 |
| - |
95 |
| - def platform_supported? |
96 |
| - platform_support_matrix = { |
97 |
| - cpu: ['x86_64', 'aarch64'].freeze, |
98 |
| - os: ['linux'].freeze, |
99 |
| - version: ['gnu', nil].freeze # nil is equivalent to `gnu` for local platform |
100 |
| - } |
101 |
| - local_platform = Gem::Platform.local |
102 |
| - |
103 |
| - platform_support_matrix.fetch(:cpu).any? { |v| local_platform.cpu == v } && |
104 |
| - platform_support_matrix.fetch(:os).any? { |v| local_platform.os == v } && |
105 |
| - platform_support_matrix.fetch(:version).any? { |v| local_platform.version == v } |
106 |
| - end |
107 |
| - |
108 |
| - def already_installed? |
109 |
| - ['ddtrace', 'datadog'].any? do |gem| |
110 |
| - fork do |
111 |
| - $stdout = File.new('/dev/null', 'w') |
112 |
| - $stderr = File.new('/dev/null', 'w') |
113 |
| - Bundler::CLI::Common.select_spec(gem) |
114 |
| - end |
115 |
| - _, status = Process.wait2 |
116 |
| - status.success? |
117 |
| - end |
118 |
| - end |
119 |
| - |
120 |
| - def frozen_bundle? |
121 |
| - Bundler.frozen_bundle? |
122 |
| - end |
123 |
| - |
124 |
| - def bundler_supported? |
125 |
| - Bundler::CLI.commands['add'] && Bundler::CLI.commands['add'].options.key?('require') |
126 |
| - end |
127 |
| - end |
128 |
| - |
129 |
| - if !precheck.in_bundle? |
130 |
| - utils.debug 'Not in bundle... skipping injection' |
131 |
| - exit!(1) |
132 |
| - elsif !precheck.runtime_supported? |
133 |
| - utils.debug "Runtime not supported: #{RUBY_DESCRIPTION}" |
134 |
| - telemetry.emit( |
135 |
| - pid, |
136 |
| - utils.version, |
137 |
| - [{ name: 'library_entrypoint.abort', tags: ['reason:incompatible_runtime'] }, |
138 |
| - { name: 'library_entrypoint.abort.runtime' }] |
139 |
| - ) |
140 |
| - exit!(1) |
141 |
| - elsif !precheck.platform_supported? |
142 |
| - utils.debug "Platform not supported: #{local_platform}" |
143 |
| - telemetry.emit(pid, utils.version, [{ name: 'library_entrypoint.abort', tags: ['reason:incompatible_platform'] }]) |
144 |
| - exit!(1) |
145 |
| - elsif precheck.already_installed? |
146 |
| - utils.debug 'Skip injection: already installed' |
147 |
| - elsif precheck.frozen_bundle? |
148 |
| - utils.error "Skip injection: bundler is configured with 'deployment' or 'frozen'" |
149 |
| - telemetry.emit(pid, utils.version, [{ name: 'library_entrypoint.abort', tags: ['reason:bundler'] }]) |
150 |
| - exit!(1) |
151 |
| - elsif !precheck.bundler_supported? |
152 |
| - utils.error "Skip injection: bundler version #{Bundler::VERSION} is not supported, please upgrade to >= 2.3." |
153 |
| - telemetry.emit(pid, utils.version, [{ name: 'library_entrypoint.abort', tags: ['reason:bundler_version'] }]) |
154 |
| - exit!(1) |
155 |
| - else |
156 |
| - # Injection |
157 |
| - path = utils.path |
158 |
| - utils.debug "Loading from #{path}" |
159 |
| - lock_file_parser = Bundler::LockfileParser.new(Bundler.read_file("#{path}/Gemfile.lock")) |
160 |
| - gem_version_mapping = lock_file_parser.specs.each_with_object({}) do |spec, hash| |
161 |
| - hash[spec.name] = spec.version.to_s |
162 |
| - hash |
163 |
| - end |
164 |
| - |
165 |
| - gemfile = Bundler::SharedHelpers.default_gemfile |
166 |
| - lockfile = Bundler::SharedHelpers.default_lockfile |
167 |
| - |
168 |
| - datadog_gemfile = gemfile.dirname + '.datadog-Gemfile' |
169 |
| - datadog_lockfile = lockfile.dirname + '.datadog-Gemfile.lock' |
170 |
| - |
171 |
| - # Copies for trial |
172 |
| - ::FileUtils.cp gemfile, datadog_gemfile |
173 |
| - ::FileUtils.cp lockfile, datadog_lockfile |
174 |
| - |
175 |
| - injection_failure = false |
176 |
| - |
177 |
| - # This is order dependent |
178 |
| - [ |
179 |
| - 'msgpack', |
180 |
| - 'ffi', |
181 |
| - 'datadog-ruby_core_source', |
182 |
| - 'libdatadog', |
183 |
| - 'libddwaf', |
184 |
| - 'datadog' |
185 |
| - ].each do |gem| |
186 |
| - fork do |
187 |
| - $stdout = File.new('/dev/null', 'w') |
188 |
| - $stderr = File.new('/dev/null', 'w') |
189 |
| - Bundler::CLI::Common.select_spec(gem) |
190 |
| - end |
191 |
| - |
192 |
| - _, status = Process.wait2 |
193 |
| - if status.success? |
194 |
| - utils.debug "#{gem} already installed... skipping..." |
195 |
| - next |
196 |
| - end |
197 |
| - |
198 |
| - bundle_add_cmd = "bundle add #{gem} --skip-install --version #{gem_version_mapping[gem]} " |
199 |
| - bundle_add_cmd << ' --verbose ' if ENV['DD_TRACE_DEBUG'] == 'true' |
200 |
| - bundle_add_cmd << '--require datadog/single_step_instrument' if gem == 'datadog' |
201 |
| - |
202 |
| - utils.debug "Injection with `#{bundle_add_cmd}`" |
203 |
| - |
204 |
| - env = { 'BUNDLE_GEMFILE' => datadog_gemfile.to_s, |
205 |
| - 'DD_TRACE_SKIP_LIB_INJECTION' => 'true', |
206 |
| - 'GEM_PATH' => utils.path } |
207 |
| - add_output, add_status = Open3.capture2e(env, bundle_add_cmd) |
208 |
| - |
209 |
| - if add_status.success? |
210 |
| - utils.debug "Successfully injected #{gem} into the application." |
211 |
| - else |
212 |
| - injection_failure = true |
213 |
| - utils.error "Injection failed: Unable to add datadog. Error output: #{add_output}" |
214 |
| - end |
215 |
| - end |
216 |
| - |
217 |
| - if injection_failure |
218 |
| - ::FileUtils.rm datadog_gemfile |
219 |
| - ::FileUtils.rm datadog_lockfile |
220 |
| - telemetry.emit(pid, utils.version, [{ name: 'library_entrypoint.error', tags: ['error_type:injection_failure'] }]) |
221 |
| - exit!(1) |
222 |
| - else |
223 |
| - write.puts datadog_gemfile.to_s |
224 |
| - telemetry.emit(pid, utils.version, [{ name: 'library_entrypoint.complete', tags: ['injection_forced:false'] }]) |
225 |
| - end |
226 |
| - end |
227 |
| - end |
228 |
| - |
229 |
| - write.close |
230 |
| - gemfile = read.read.to_s.chomp |
231 |
| - |
232 |
| - _, status = Process.wait2 |
233 |
| - ENV['DD_TRACE_SKIP_LIB_INJECTION'] = 'true' |
234 |
| - |
235 |
| - if status.success? |
236 |
| - dd_lib_injection_path = utils.path |
237 |
| - |
238 |
| - Gem.paths = { 'GEM_PATH' => "#{dd_lib_injection_path}:#{ENV['GEM_PATH']}" } |
239 |
| - ENV['GEM_PATH'] = Gem.path.join(':') |
240 |
| - ENV['BUNDLE_GEMFILE'] = gemfile |
241 |
| - utils.debug "Fork success: Using Gemfile `#{gemfile}`" |
242 |
| - else |
243 |
| - utils.debug 'Fork abort' |
244 |
| - end |
245 |
| - else |
246 |
| - utils.debug 'Fork not supported... skipping injection' |
247 |
| - telemetry.emit(pid, utils.version, [{ name: 'library_entrypoint.abort', tags: ['reason:fork_not_supported'] }]) |
248 |
| - end |
| 5 | +if RUBY_VERSION >= '2.3.' |
| 6 | + require File.expand_path(File.join(File.dirname(__FILE__), 'host_inject_main.rb')) |
249 | 7 | end
|
0 commit comments