Skip to content

Commit 7c839f0

Browse files
authored
Merge pull request #1162 from newrelic/develop-k8s-agent-operator
K8s Agents Operator Changes
2 parents 4ddc5b1 + d969238 commit 7c839f0

3 files changed

Lines changed: 83 additions & 36 deletions

File tree

newrelic/bootstrap/sitecustomize.py

Lines changed: 76 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
import sys
1717
import time
1818

19-
# Define some debug logging routines to help sort out things when this
20-
# all doesn't work as expected.
21-
22-
2319
# Avoiding additional imports by defining PY2 manually
2420
PY2 = sys.version_info[0] == 2
2521

22+
# Define some debug logging routines to help sort out things when this
23+
# all doesn't work as expected.
24+
2625
startup_debug = os.environ.get("NEW_RELIC_STARTUP_DEBUG", "off").lower() in ("on", "true", "1")
2726

2827

@@ -35,6 +34,14 @@ def log_message(text, *args, **kwargs):
3534
sys.stdout.flush()
3635

3736

37+
def del_sys_path_entry(path):
38+
if path and path in sys.path:
39+
try:
40+
del sys.path[sys.path.index(path)]
41+
except Exception:
42+
pass
43+
44+
3845
log_message("New Relic Bootstrap (%s)", __file__)
3946

4047
log_message("working_directory = %r", os.getcwd())
@@ -70,25 +77,19 @@ def log_message(text, *args, **kwargs):
7077
# the search, and then load what was found.
7178

7279
boot_directory = os.path.dirname(__file__)
73-
root_directory = os.path.dirname(os.path.dirname(boot_directory))
74-
75-
log_message("root_directory = %r", root_directory)
7680
log_message("boot_directory = %r", boot_directory)
7781

78-
path = list(sys.path)
79-
80-
if boot_directory in path:
81-
del path[path.index(boot_directory)]
82+
del_sys_path_entry(boot_directory)
8283

8384
try:
8485
if PY2:
8586
import imp
8687

87-
module_spec = imp.find_module("sitecustomize", path)
88+
module_spec = imp.find_module("sitecustomize", sys.path)
8889
else:
8990
from importlib.machinery import PathFinder
9091

91-
module_spec = PathFinder.find_spec("sitecustomize", path=path)
92+
module_spec = PathFinder.find_spec("sitecustomize", path=sys.path)
9293

9394
except ImportError:
9495
pass
@@ -118,52 +119,91 @@ def log_message(text, *args, **kwargs):
118119

119120
python_prefix_matches = expected_python_prefix == actual_python_prefix
120121
python_version_matches = expected_python_version == actual_python_version
122+
k8s_operator_enabled = os.environ.get("NEW_RELIC_K8S_OPERATOR_ENABLED", "off").lower() in ("on", "true", "1")
121123

122124
log_message("python_prefix_matches = %r", python_prefix_matches)
123125
log_message("python_version_matches = %r", python_version_matches)
126+
log_message("k8s_operator_enabled = %r", k8s_operator_enabled)
124127

125-
if python_prefix_matches and python_version_matches:
128+
if k8s_operator_enabled or (python_prefix_matches and python_version_matches):
126129
# We also need to skip agent initialisation if neither the license
127130
# key or config file environment variables are set. We do this as
128131
# some people like to use a common startup script which always uses
129132
# the wrapper script, and which controls whether the agent is
130133
# actually run based on the presence of the environment variables.
131134

132135
license_key = os.environ.get("NEW_RELIC_LICENSE_KEY", None)
133-
136+
developer_mode = os.environ.get("NEW_RELIC_DEVELOPER_MODE", "off").lower() in ("on", "true", "1")
134137
config_file = os.environ.get("NEW_RELIC_CONFIG_FILE", None)
135138
environment = os.environ.get("NEW_RELIC_ENVIRONMENT", None)
139+
initialize_agent = bool(license_key or config_file or developer_mode)
140+
141+
log_message("initialize_agent = %r", initialize_agent)
142+
143+
if initialize_agent:
144+
if not k8s_operator_enabled:
145+
# When installed as an egg with buildout, the root directory for
146+
# packages is not listed in sys.path and scripts instead set it
147+
# after Python has started up. This will cause importing of
148+
# 'newrelic' module to fail. What we do is see if the root
149+
# directory where the package is held is in sys.path and if not
150+
# insert it. For good measure we remove it after having imported
151+
# 'newrelic' module to reduce chance that will cause any issues.
152+
# If it is a buildout created script, it will replace the whole
153+
# sys.path again later anyway.
154+
root_directory = os.path.dirname(os.path.dirname(boot_directory))
155+
log_message("root_directory = %r", root_directory)
156+
157+
new_relic_path = root_directory
158+
do_insert_path = root_directory not in sys.path
159+
else:
160+
# When installed with the kubernetes operator, we need to attempt
161+
# to find a distribution from our initcontainer that matches the
162+
# current environment. For wheels, this is platform dependent and we
163+
# rely on pip to identify the correct wheel to use. If no suitable
164+
# wheel can be found, we will fall back to the sdist and disable
165+
# extensions. Once the appropriate distribution is found, we import
166+
# it and leave the entry in sys.path. This allows users to import
167+
# the 'newrelic' module later and use our APIs in their code.
168+
try:
169+
sys.path.insert(0, boot_directory)
170+
from newrelic_k8s_operator import find_supported_newrelic_distribution
171+
finally:
172+
del_sys_path_entry(boot_directory)
136173

137-
log_message("initialize_agent = %r", bool(license_key or config_file))
174+
new_relic_path = find_supported_newrelic_distribution()
175+
do_insert_path = True
138176

139-
if license_key or config_file:
140-
# When installed as an egg with buildout, the root directory for
141-
# packages is not listed in sys.path and scripts instead set it
142-
# after Python has started up. This will cause importing of
143-
# 'newrelic' module to fail. What we do is see if the root
144-
# directory where the package is held is in sys.path and if not
145-
# insert it. For good measure we remove it after having imported
146-
# 'newrelic' module to reduce chance that will cause any issues.
147-
# If it is a buildout created script, it will replace the whole
148-
# sys.path again later anyway.
177+
# Now that the appropriate location of the module has been identified,
178+
# either by the kubernetes operator or this script, we are ready to import
179+
# the 'newrelic' module to make it available in sys.modules. If the location
180+
# containing it was not found on sys.path, do_insert_path will be set and
181+
# the location will be inserted into sys.path. The module is then imported,
182+
# and the sys.path entry is removed afterwards to reduce chance that will
183+
# cause any issues.
149184

150-
do_insert_path = root_directory not in sys.path
151-
if do_insert_path:
152-
sys.path.insert(0, root_directory)
185+
log_message("new_relic_path = %r" % new_relic_path)
186+
log_message("do_insert_path = %r" % do_insert_path)
153187

154-
import newrelic.config
188+
try:
189+
if do_insert_path:
190+
sys.path.insert(0, new_relic_path)
155191

156-
log_message("agent_version = %r", newrelic.version)
192+
import newrelic
157193

158-
if do_insert_path:
159-
try:
160-
del sys.path[sys.path.index(root_directory)]
161-
except Exception:
162-
pass
194+
log_message("agent_version = %r", newrelic.version)
195+
finally:
196+
if do_insert_path:
197+
del_sys_path_entry(new_relic_path)
163198

164199
# Finally initialize the agent.
200+
import newrelic.config
165201

166202
newrelic.config.initialize(config_file, environment)
203+
else:
204+
log_message(
205+
"New Relic could not start due to missing configuration. Either NEW_RELIC_LICENSE_KEY or NEW_RELIC_CONFIG_FILE are required."
206+
)
167207
else:
168208
log_message(
169209
"""New Relic could not start because the newrelic-admin script was called from a Python installation that is different from the Python installation that is currently running. To fix this problem, call the newrelic-admin script from the Python installation that is currently running (details below).

newrelic/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ def _process_configuration(section):
564564
_process_setting(section, "ai_monitoring.enabled", "getboolean", None)
565565
_process_setting(section, "ai_monitoring.record_content.enabled", "getboolean", None)
566566
_process_setting(section, "ai_monitoring.streaming.enabled", "getboolean", None)
567+
_process_setting(section, "k8s_operator.enabled", "getboolean", None)
567568
_process_setting(section, "package_reporting.enabled", "getboolean", None)
568569

569570

newrelic/core/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ class AIMonitoringRecordContentSettings(Settings):
162162
pass
163163

164164

165+
class K8sOperatorSettings(Settings):
166+
pass
167+
168+
165169
class PackageReportingSettings(Settings):
166170
pass
167171

@@ -430,6 +434,7 @@ class EventHarvestConfigHarvestLimitSettings(Settings):
430434
_settings.ai_monitoring = AIMonitoringSettings()
431435
_settings.ai_monitoring.streaming = AIMonitoringStreamingSettings()
432436
_settings.ai_monitoring.record_content = AIMonitoringRecordContentSettings()
437+
_settings.k8s_operator = K8sOperatorSettings()
433438
_settings.package_reporting = PackageReportingSettings()
434439
_settings.attributes = AttributesSettings()
435440
_settings.browser_monitoring = BrowserMonitorSettings()
@@ -955,6 +960,7 @@ def default_otlp_host(host):
955960
"NEW_RELIC_AI_MONITORING_RECORD_CONTENT_ENABLED", default=True
956961
)
957962
_settings.ai_monitoring._llm_token_count_callback = None
963+
_settings.k8s_operator.enabled = _environ_as_bool("NEW_RELIC_K8S_OPERATOR_ENABLED", default=False)
958964
_settings.package_reporting.enabled = _environ_as_bool("NEW_RELIC_PACKAGE_REPORTING_ENABLED", default=True)
959965
_settings.ml_insights_events.enabled = _environ_as_bool("NEW_RELIC_ML_INSIGHTS_EVENTS_ENABLED", default=False)
960966

0 commit comments

Comments
 (0)