newrelic-lambda-cli 0.9.13__py2.py3-none-any.whl → 0.9.15__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -96,24 +96,23 @@ class NewRelicGQL(object):
96
96
  """
97
97
  res = self.query(
98
98
  """
99
- query ($accountId: Int!) {
100
- requestContext {
101
- apiKey
102
- }
103
- actor {
104
- account(id: $accountId) {
105
- licenseKey
106
- id
107
- name
99
+ query ($accountId: Int!) {
100
+ actor {
101
+ apiAccess {
102
+ keySearch(query: {types: INGEST, scope: {accountIds: [$accountId]}}) {
103
+ keys {
104
+ key
105
+ }
106
+ }
107
+ }
108
+ }
108
109
  }
109
- }
110
- }
111
110
  """,
112
111
  accountId=self.account_id,
113
112
  )
114
113
  try:
115
- return res["actor"]["account"]["licenseKey"]
116
- except KeyError:
114
+ return res["actor"]["apiAccess"]["keySearch"]["keys"][0]["key"]
115
+ except (KeyError, IndexError):
117
116
  return None
118
117
 
119
118
  def get_linked_account_by_id(self, id):
@@ -0,0 +1,346 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+
5
+ Example usage:
6
+
7
+ >>> from newrelic_lambda_cli.api import NewRelicGQL
8
+ >>> gql = NewRelicGQL("api key here", "account id here")
9
+ >>> gql.get_linked_accounts()
10
+
11
+ """
12
+
13
+ from gql import Client, gql
14
+ from gql.transport.requests import RequestsHTTPTransport
15
+
16
+ import click
17
+ import requests
18
+ import json
19
+
20
+ from newrelic_lambda_cli.cliutils import failure, success
21
+
22
+
23
+ class NRGQL_APM(object):
24
+ def __init__(self, account_id, api_key, region="us"):
25
+ try:
26
+ self.account_id = int(account_id)
27
+ except ValueError:
28
+ raise ValueError("Account ID must be an integer")
29
+
30
+ self.api_key = api_key
31
+
32
+ if region == "us":
33
+ self.url = "https://api.newrelic.com/graphql"
34
+ elif region == "eu":
35
+ self.url = "https://api.eu.newrelic.com/graphql"
36
+ elif region == "staging":
37
+ self.url = "https://staging-api.newrelic.com/graphql"
38
+ else:
39
+ raise ValueError("Region must be one of 'us' or 'eu'")
40
+
41
+ transport = RequestsHTTPTransport(url=self.url, use_json=True)
42
+ transport.headers = {"api-key": self.api_key}
43
+
44
+ try:
45
+ self.client = Client(transport=transport, fetch_schema_from_transport=True)
46
+ except Exception:
47
+ self.client = Client(transport=transport, fetch_schema_from_transport=False)
48
+
49
+ def query(self, query, timeout=None, **variable_values):
50
+ return self.client.execute(
51
+ gql(query), timeout=timeout, variable_values=variable_values or None
52
+ )
53
+
54
+ def get_entity_guids_from_entity_name(self, entity_name) -> dict[str, str]:
55
+ entity_dicts = {}
56
+ res = self.query(
57
+ f"""
58
+ query {{
59
+ actor {{
60
+ entitySearch(query: "name LIKE '{entity_name}' AND accountId = {self.account_id}") {{
61
+ results {{
62
+ entities {{
63
+ guid
64
+ name
65
+ type
66
+ }}
67
+ }}
68
+ }}
69
+ }}
70
+ }}
71
+ """
72
+ )
73
+
74
+ try:
75
+ data = res
76
+ entities = data["actor"]["entitySearch"]["results"]["entities"]
77
+ for entity in entities:
78
+ if entity["name"] == entity_name:
79
+ entity_dicts[entity["type"]] = entity["guid"]
80
+ return entity_dicts
81
+ except (KeyError, TypeError) as e:
82
+ print(f"An error occurred parsing the response: {e}")
83
+ return None
84
+ except Exception as e:
85
+ print(f"An error occurred: {e}")
86
+ return None
87
+
88
+ def get_entity_alert_details(self, entity_guid) -> dict:
89
+ res = self.query(
90
+ f"""
91
+ query {{
92
+ actor {{
93
+ entity(guid: "{entity_guid}") {{
94
+ name
95
+ guid
96
+ reporting
97
+ alertSeverity
98
+ }}
99
+ account(id: {self.account_id}) {{
100
+ alerts {{
101
+ nrqlConditionsSearch(searchCriteria: {{queryLike: "{entity_guid}"}}) {{
102
+ nrqlConditions {{
103
+ id
104
+ name
105
+ enabled
106
+ description
107
+ policyId
108
+ nrql {{
109
+ query
110
+ }}
111
+ terms {{
112
+ operator
113
+ priority
114
+ threshold
115
+ thresholdDuration
116
+ thresholdOccurrences
117
+ }}
118
+ }}
119
+ }}
120
+ }}
121
+ }}
122
+ }}
123
+ }}
124
+ """
125
+ )
126
+
127
+ print(f"Querying alert details for Lambda entity")
128
+
129
+ try:
130
+ data = res
131
+ # Check for GraphQL errors in the response
132
+ if "errors" in data:
133
+ print("Error in GraphQL response:")
134
+ for error in data["errors"]:
135
+ print(f"- {error.get('message')}")
136
+ return None
137
+
138
+ # Extract the entity data from the response
139
+ actor_data = data.get("actor", {})
140
+ entity_data = actor_data.get("entity")
141
+
142
+ if not entity_data:
143
+ print(f"Error: Could not find data for entity with GUID: {entity_guid}")
144
+ return None
145
+
146
+ # Extract alert conditions from the new path and add them to our entity_data dictionary
147
+ conditions_search_result = (
148
+ actor_data.get("account", {})
149
+ .get("alerts", {})
150
+ .get("nrqlConditionsSearch", {})
151
+ )
152
+ entity_data["alertConditions"] = (
153
+ conditions_search_result.get("nrqlConditions", [])
154
+ if conditions_search_result
155
+ else []
156
+ )
157
+
158
+ return entity_data
159
+
160
+ except (KeyError, TypeError) as e:
161
+ print(f"An error occurred parsing the response: {e}")
162
+ return None
163
+ except Exception as e:
164
+ print(f"An error occurred: {e}")
165
+ return None
166
+
167
+ def create_alert_for_new_entity(
168
+ self, lambda_entity_selected_alerts, lambda_entity_guid, apm_entity_guid
169
+ ):
170
+ # Create a list of new NRQL conditions with modified queries
171
+ new_nrql_conditions = []
172
+ for alert_condition in lambda_entity_selected_alerts:
173
+ original_description = alert_condition.get("description") or ""
174
+ new_description = (
175
+ f"{original_description} migrated from Lambda entity".strip()
176
+ )
177
+ alert_query = alert_condition["nrql"]["query"]
178
+ alert_query = create_apm_alert_query(
179
+ alert_query, lambda_entity_guid, apm_entity_guid
180
+ )
181
+ new_condition = {
182
+ "name": alert_condition["name"] + " - apm_migrated",
183
+ "description": new_description,
184
+ "enabled": True,
185
+ "nrql": {"query": alert_query},
186
+ "terms": alert_condition["terms"],
187
+ }
188
+ policy_id = alert_condition.get("policyId")
189
+ new_nrql_conditions.append((new_condition, policy_id))
190
+
191
+ # Process each condition individually
192
+ results = []
193
+ for new_condition, policy_id in new_nrql_conditions:
194
+ # Validate policy_id
195
+ try:
196
+ policy_id_int = int(policy_id)
197
+ except (ValueError, TypeError):
198
+ print(
199
+ f"Error: Invalid policy ID '{policy_id}' for condition '{new_condition['name']}'. Skipping."
200
+ )
201
+ continue
202
+
203
+ # Format terms as proper GraphQL objects
204
+ terms_list = []
205
+ for term in new_condition["terms"]:
206
+ term_str = "{"
207
+ term_str += f"operator: {term['operator']}, "
208
+ term_str += f"priority: {term['priority']}, "
209
+ term_str += f"threshold: {term['threshold']}, "
210
+ term_str += f"thresholdDuration: {term['thresholdDuration']}, "
211
+ term_str += f"thresholdOccurrences: {term['thresholdOccurrences']}"
212
+ term_str += "}"
213
+ terms_list.append(term_str)
214
+
215
+ terms_str = "[" + ", ".join(terms_list) + "]"
216
+
217
+ mutation = f"""
218
+ mutation {{
219
+ alertsNrqlConditionStaticCreate(
220
+ accountId: {self.account_id},
221
+ condition: {{
222
+ name: "{new_condition['name']}"
223
+ description: "{new_condition['description']}"
224
+ enabled: {str(new_condition['enabled']).lower()}
225
+ nrql: {{
226
+ query: "{new_condition['nrql']['query']}"
227
+ }}
228
+ terms: {terms_str}
229
+ }},
230
+ policyId: "{policy_id_int}"
231
+ ) {{
232
+ id
233
+ name
234
+ description
235
+ nrql {{
236
+ query
237
+ }}
238
+ terms {{
239
+ operator
240
+ priority
241
+ threshold
242
+ thresholdDuration
243
+ thresholdOccurrences
244
+ }}
245
+ }}
246
+ }}
247
+ """
248
+
249
+ try:
250
+ res = self.query(mutation)
251
+ # Check for GraphQL errors in the response
252
+ if "errors" in res:
253
+ print("Error in GraphQL response:")
254
+ for error in res["errors"]:
255
+ print(f"- {error.get('message')}")
256
+ continue
257
+
258
+ result = res["alertsNrqlConditionStaticCreate"]
259
+ results.append(result)
260
+ success(f"Successfully migrated to alert: {result['name']}")
261
+
262
+ except (KeyError, TypeError) as e:
263
+ print(f"An error occurred parsing the response: {e}")
264
+ continue
265
+ except Exception as e:
266
+ print(f"An error occurred: {e}")
267
+ continue
268
+
269
+ return results
270
+
271
+
272
+ lambda_entity_alert_metric = {
273
+ "cwBilledDuration": "apm.lambda.transaction.billed_duration",
274
+ "cwDuration": "apm.lambda.transaction.duration",
275
+ "cwInitDuration": "apm.lambda.transaction.init_duration",
276
+ "cwMaxMemoryUsed": "apm.lambda.transaction.max_memory_used",
277
+ "cwMemorySize": "apm.lambda.transaction.memory_size",
278
+ "cloudWatchBilledDuration": "apm.lambda.transaction.billed_duration",
279
+ "cloudWatchDuration": "apm.lambda.transaction.duration",
280
+ "cloudWatchInitDuration": "apm.lambda.transaction.init_duration",
281
+ }
282
+
283
+
284
+ def check_apm_migrated_alerts(lambda_entity_data, apm_entity_data):
285
+ """
286
+ Check which Lambda alerts have not been migrated to APM yet.
287
+ Returns a list of Lambda alert conditions that don't have corresponding APM migrated versions.
288
+ """
289
+ if not lambda_entity_data or "alertConditions" not in lambda_entity_data:
290
+ print("No alert conditions found for Lambda entity.")
291
+ return []
292
+
293
+ if not apm_entity_data or "alertConditions" not in apm_entity_data:
294
+ print("No alert conditions found for APM entity.")
295
+ return []
296
+
297
+ alerts_not_migrated = []
298
+ lambda_alert_conditions = lambda_entity_data["alertConditions"]
299
+ apm_alert_conditions = apm_entity_data["alertConditions"]
300
+ apm_alerts_name = [condition["name"] for condition in apm_alert_conditions]
301
+
302
+ for lambda_condition in lambda_alert_conditions:
303
+ migrated_lambda_alert_name = lambda_condition["name"] + " - apm_migrated"
304
+ if migrated_lambda_alert_name not in apm_alerts_name:
305
+ alerts_not_migrated.append(lambda_condition)
306
+ else:
307
+ print(f"Alert already migrated, skipping: {lambda_condition['name']}")
308
+
309
+ return alerts_not_migrated
310
+
311
+
312
+ def select_lambda_entity_impacted_alerts(entity_data, apm_entity_data=None):
313
+ if not entity_data or "alertConditions" not in entity_data:
314
+ print("No alert conditions found.")
315
+ return []
316
+
317
+ selected_alerts = []
318
+ alert_conditions = entity_data["alertConditions"]
319
+ for condition in alert_conditions:
320
+ alert_query = condition["nrql"]["query"]
321
+ has_lambda_invocation = "AwsLambdaInvocation" in alert_query
322
+ has_cloudwatch_metrics = any(
323
+ metric in alert_query for metric in lambda_entity_alert_metric.keys()
324
+ )
325
+ if has_lambda_invocation and has_cloudwatch_metrics:
326
+ print(f"Selected alert for migration: {condition['name']}")
327
+ selected_alerts.append(condition)
328
+ if apm_entity_data:
329
+ selected_alerts = check_apm_migrated_alerts(
330
+ {"alertConditions": selected_alerts}, apm_entity_data
331
+ )
332
+ if len(selected_alerts) == 0:
333
+ print(
334
+ "No alerts met the migration criteria (must contain 'AwsLambdaInvocation' or CloudWatch metrics)"
335
+ )
336
+ return selected_alerts
337
+
338
+
339
+ def create_apm_alert_query(alert_query, lambda_entity_guid, apm_entity_guid):
340
+ for key, value in lambda_entity_alert_metric.items():
341
+ if key in alert_query:
342
+ alert_query = alert_query.replace(key, value)
343
+ break
344
+ apm_alert_query = alert_query.replace("AwsLambdaInvocation", "Metric")
345
+ apm_alert_query = apm_alert_query.replace(lambda_entity_guid, apm_entity_guid)
346
+ return apm_alert_query
@@ -3,6 +3,7 @@
3
3
  import click
4
4
 
5
5
  from newrelic_lambda_cli.cli import (
6
+ apm,
6
7
  functions,
7
8
  integrations,
8
9
  layers,
@@ -21,6 +22,7 @@ def cli(ctx, verbose):
21
22
 
22
23
 
23
24
  def register_groups(group):
25
+ apm.register(group)
24
26
  functions.register(group)
25
27
  integrations.register(group)
26
28
  otel_ingestions.register(group)
@@ -0,0 +1,121 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import boto3
4
+ import click
5
+
6
+ from newrelic_lambda_cli import api, apm, otel_ingestions, permissions, integrations
7
+ from newrelic_lambda_cli.types import (
8
+ AlertsMigrate,
9
+ )
10
+ from newrelic_lambda_cli.cli.decorators import add_options, AWS_OPTIONS, NR_OPTIONS
11
+ from newrelic_lambda_cli.cliutils import done, failure
12
+
13
+
14
+ @click.group(name="apm")
15
+ def apm_mode_group():
16
+ """Manage New Relic APM Mode of AWS Lambda instrumentation"""
17
+ pass
18
+
19
+
20
+ def register(group):
21
+ group.add_command(apm_mode_group)
22
+ apm_mode_group.add_command(alerts_migrate)
23
+
24
+
25
+ @click.command(name="alerts-migrate")
26
+ @click.option(
27
+ "--nr-account-id",
28
+ "-a",
29
+ envvar="NEW_RELIC_ACCOUNT_ID",
30
+ help="New Relic Account ID",
31
+ metavar="<account_id>",
32
+ required=True,
33
+ type=click.INT,
34
+ )
35
+ @click.option(
36
+ "--nr-api-key",
37
+ "-k",
38
+ envvar="NEW_RELIC_API_KEY",
39
+ help="New Relic User API Key",
40
+ metavar="<key>",
41
+ required=True,
42
+ )
43
+ @click.option(
44
+ "--nr-region",
45
+ default="us",
46
+ envvar="NEW_RELIC_REGION",
47
+ help="New Relic Account Region",
48
+ metavar="<region>",
49
+ show_default=True,
50
+ type=click.Choice(["us", "eu", "staging"]),
51
+ )
52
+ @add_options(AWS_OPTIONS)
53
+ @click.option(
54
+ "function",
55
+ "--function",
56
+ "-f",
57
+ help="AWS Lambda function name or ARN",
58
+ metavar="<arn>",
59
+ multiple=False,
60
+ required=True,
61
+ )
62
+ @click.option(
63
+ "excludes",
64
+ "--exclude",
65
+ "-e",
66
+ help="Functions to exclude (if using 'all, 'installed', 'not-installed aliases)",
67
+ metavar="<name>",
68
+ multiple=True,
69
+ )
70
+ @click.pass_context
71
+ def alerts_migrate(ctx, **kwargs):
72
+ """Migrate New Relic AWS Lambda Alerts to APM mode"""
73
+ input = AlertsMigrate(session=None, verbose=ctx.obj["VERBOSE"], **kwargs)
74
+ input = input._replace(
75
+ session=boto3.Session(
76
+ profile_name=input.aws_profile, region_name=input.aws_region
77
+ )
78
+ )
79
+
80
+ # Validate required parameters
81
+ if not input.nr_api_key:
82
+ failure(
83
+ "New Relic API key is required. Provide it via --nr-api-key or NEW_RELIC_API_KEY environment variable.",
84
+ exit=True,
85
+ )
86
+
87
+ print("Started migration of alerts")
88
+ if ctx.obj["VERBOSE"]:
89
+ print("Will migrate alerts for {}".format(input.function))
90
+ print(f"Using account ID: {input.nr_account_id}")
91
+ print(f"Using region: {input.nr_region}")
92
+
93
+ # setup client
94
+ client = apm.NRGQL_APM(
95
+ account_id=input.nr_account_id,
96
+ api_key=input.nr_api_key,
97
+ region=input.nr_region,
98
+ )
99
+
100
+ print(f"Getting entity GUID for function: {input.function}")
101
+ result = client.get_entity_guids_from_entity_name(input.function)
102
+ lambda_entity_guid = ""
103
+ apm_entity_guid = ""
104
+ for entity_type, entity_guid in result.items():
105
+ if entity_type == "AWSLAMBDAFUNCTION":
106
+ lambda_entity_guid = entity_guid
107
+ if entity_type == "APPLICATION":
108
+ apm_entity_guid = entity_guid
109
+ lambda_entity_data = client.get_entity_alert_details(lambda_entity_guid)
110
+ apm_entity_data = client.get_entity_alert_details(apm_entity_guid)
111
+ lambda_entity_selected_alerts = apm.select_lambda_entity_impacted_alerts(
112
+ lambda_entity_data, apm_entity_data
113
+ )
114
+ if lambda_entity_selected_alerts:
115
+ client.create_alert_for_new_entity(
116
+ lambda_entity_selected_alerts, lambda_entity_guid, apm_entity_guid
117
+ )
118
+ else:
119
+ print(
120
+ "No alerts need to be migrated - all eligible alerts have already been migrated."
121
+ )
@@ -133,6 +133,12 @@ def register(group):
133
133
  is_flag=True,
134
134
  help="Enable sending Lambda function logs via the New Relic Lambda Extension",
135
135
  )
136
+ @click.option(
137
+ "--slim",
138
+ is_flag=True,
139
+ default=False,
140
+ help="New Relic slim Layer without opentelemetry for Node.js",
141
+ )
136
142
  @click.option(
137
143
  "--disable-function-logs",
138
144
  is_flag=True,
@@ -48,7 +48,12 @@ def index(region, runtime, architecture):
48
48
 
49
49
 
50
50
  def layer_selection(
51
- available_layers, runtime, architecture, upgrade=False, existing_layer_arn=None
51
+ available_layers,
52
+ runtime,
53
+ architecture,
54
+ upgrade=False,
55
+ existing_layer_arn=None,
56
+ slim=False,
52
57
  ):
53
58
  if upgrade and existing_layer_arn:
54
59
  base_arn = existing_layer_arn.rsplit(":", 1)[0]
@@ -65,7 +70,11 @@ def layer_selection(
65
70
  layer_options = [
66
71
  layer["LatestMatchingVersion"]["LayerVersionArn"] for layer in available_layers
67
72
  ]
68
-
73
+ if slim:
74
+ for arn in layer_options:
75
+ if "-slim:" in arn:
76
+ success("Layer %s selected (slim)" % arn)
77
+ return arn
69
78
  if sys.stdout.isatty():
70
79
  output = "\n".join(
71
80
  [
@@ -86,11 +95,14 @@ def layer_selection(
86
95
  except IndexError:
87
96
  failure("Invalid layer selection")
88
97
  else:
89
- raise click.UsageError(
98
+ click.echo(
90
99
  "Discovered multiple layers for runtime %s (%s):\n%s\n"
91
- "Pass --layer-arn to specify a layer ARN"
100
+ "To add a particular Layer ARN use --layer-arn"
92
101
  % (runtime, architecture, "\n".join(layer_options))
93
102
  )
103
+ selected = layer_options[0]
104
+ success("Layer %s selected (auto)" % selected)
105
+ return selected
94
106
 
95
107
 
96
108
  def _add_new_relic(input, config, nr_license_key):
@@ -167,6 +179,7 @@ def _add_new_relic(input, config, nr_license_key):
167
179
  architecture,
168
180
  upgrade=input.upgrade,
169
181
  existing_layer_arn=existing_layer_arn,
182
+ slim=input.slim,
170
183
  )
171
184
 
172
185
  update_kwargs = {
@@ -117,6 +117,7 @@ LAYER_INSTALL_KEYS = [
117
117
  "disable_extension_logs",
118
118
  "java_handler_method",
119
119
  "esm",
120
+ "slim",
120
121
  ]
121
122
 
122
123
  LAYER_UNINSTALL_KEYS = [
@@ -141,6 +142,19 @@ SUBSCRIPTION_INSTALL_KEYS = [
141
142
  "otel",
142
143
  ]
143
144
 
145
+ ALERTS_MIGRATE_KEYS = [
146
+ "session",
147
+ "aws_profile",
148
+ "aws_region",
149
+ "aws_permissions_check",
150
+ "nr_account_id",
151
+ "nr_api_key",
152
+ "nr_region",
153
+ "function",
154
+ "excludes",
155
+ "verbose",
156
+ ]
157
+
144
158
  SUBSCRIPTION_UNINSTALL_KEYS = [
145
159
  "session",
146
160
  "aws_profile",
@@ -166,5 +180,7 @@ OtelIngestionUpdate = namedtuple("OtelIngestionUpdate", OTEL_INGESTION_UPDATE_KE
166
180
  LayerInstall = namedtuple("LayerInstall", LAYER_INSTALL_KEYS)
167
181
  LayerUninstall = namedtuple("LayerUninstall", LAYER_UNINSTALL_KEYS)
168
182
 
183
+ AlertsMigrate = namedtuple("AlertsMigrate", ALERTS_MIGRATE_KEYS)
184
+
169
185
  SubscriptionInstall = namedtuple("SubscriptionInstall", SUBSCRIPTION_INSTALL_KEYS)
170
186
  SubscriptionUninstall = namedtuple("SubscriptionUninstall", SUBSCRIPTION_UNINSTALL_KEYS)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: newrelic-lambda-cli
3
- Version: 0.9.13
3
+ Version: 0.9.15
4
4
  Summary: A CLI to install the New Relic AWS Lambda integration and layers.
5
5
  Home-page: https://github.com/newrelic/newrelic-lambda-cli
6
6
  Author: New Relic
@@ -64,11 +64,11 @@ A CLI to install the New Relic AWS Lambda integration and layers.
64
64
 
65
65
  | Runtime | Versions |
66
66
  |-------------|------------------------|
67
- | Python | `python3.7`, `python3.8`, `python3.9`, `python3.10`, `python3.11`, `python3.12`, `python3.13` |
67
+ | Python | `python3.8`, `python3.9`, `python3.10`, `python3.11`, `python3.12`, `python3.13` |
68
68
  | Node.js | `nodejs16.x`, `nodejs18.x`, `nodejs20.x`, `nodejs22.x` |
69
- | .NET | `dotnet3.1`, `dotnet6`, `dotnet8` |
69
+ | .NET | `dotnet6`, `dotnet8` |
70
70
  | Java | `java8.al2`, `java11`, `java17`, `java21` |
71
- | Provided | `provided`, `provided.al2`, `provided.al2023` |
71
+ | Provided | `provided.al2`, `provided.al2023` |
72
72
  | Ruby | `ruby3.2`, `ruby3.3`, `ruby3.4` |
73
73
 
74
74
  **Note:** Automatic handler wrapping is only supported for Node.js, Python, Java, and Ruby. For other runtimes,
@@ -78,6 +78,7 @@ manual function wrapping is required using the runtime specific New Relic agent.
78
78
 
79
79
  * Python >= 3.7 <= 3.13
80
80
  * Retrieve your [New relic Account ID](https://docs.newrelic.com/docs/accounts/install-new-relic/account-setup/account-id) and [User API Key](https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys#user-api-key)
81
+ * Retrieve your [New Relic Ingest License Key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#personal-api-key)
81
82
 
82
83
  ## Recommendations
83
84
 
@@ -199,6 +200,7 @@ newrelic-lambda layers install \
199
200
  | `--function` or `-f` | Yes | The AWS Lambda function name or ARN in which to add a layer. Can provide multiple `--function` arguments. Will also accept `all`, `installed` and `not-installed` similar to `newrelic-lambda functions list`. |
200
201
  | `--nr-account-id` or `-a` | Yes | The [New Relic Account ID](https://docs.newrelic.com/docs/accounts/install-new-relic/account-setup/account-id) this function should use. Can also use the `NEW_RELIC_ACCOUNT_ID` environment variable. |
201
202
  | `--exclude` or `-e` | No | A function name to exclude while installing layers. Can provide multiple `--exclude` arguments. Only checked when `all`, `installed` and `not-installed` are used. See `newrelic-lambda functions list` for function names. |
203
+ | `--slim` | No | The flag `--slim` adds the Node.js layer without OpenTelemetry dependencies, resulting in a lighter size. |
202
204
  | `--layer-arn` or `-l` | No | Specify a specific layer version ARN to use. This is auto detected by default. |
203
205
  | `--upgrade` or `-u` | No | Permit upgrade to the latest layer version for this region and runtime. |
204
206
  | `--disable-extension` | No | Disable the [New Relic Lambda Extension](https://github.com/newrelic/newrelic-lambda-extension). |
@@ -209,6 +211,7 @@ newrelic-lambda layers install \
209
211
  | `--aws-profile` or `-p` | No | The AWS profile to use for this command. Can also use `AWS_PROFILE`. Will also check `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables if not using AWS CLI. |
210
212
  | `--aws-region` or `-r` | No | The AWS region this function is located. Can use `AWS_DEFAULT_REGION` environment variable. Defaults to AWS session region. |
211
213
  | `--nr-api-key` or `-k` | No | Your [New Relic User API Key](https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys#user-api-key). Can also use the `NEW_RELIC_API_KEY` environment variable. Only used if `--enable-extension` is set and there is no New Relic license key in AWS Secrets Manager. |
214
+ | `--nr-ingest-key`| No | Your [New Relic Ingest License Key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#personal-api-key). Can be used without `--enable-extension` configured or license key in AWS Secrets Manager. |
212
215
  | `--nr-region` | No | The New Relic region to use for the integration. Can use the `NEW_RELIC_REGION` environment variable. Can be either `eu` or `us`. Defaults to `us`. Only used if `--enable-extension` is set and there is no New Relic license key in AWS Secrets Manager. |
213
216
  | `--nr-env-delimite` | No | Set `NR_ENV_DELIMITER` environment variable for your Lambda Function |
214
217
  | `--nr-tags` | No | Set `NR_TAGS` environment variable for your Lambda Function |
@@ -281,6 +284,17 @@ newrelic-lambda subscriptions uninstall --function <name or arn>
281
284
  | `--aws-profile` or `-p` | No | The AWS profile to use for this command. Can also use `AWS_PROFILE`. Will also check `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables if not using AWS CLI. |
282
285
  | `--aws-region` or `-r` | No | The AWS region this function is located. Can use `AWS_DEFAULT_REGION` environment variable. Defaults to AWS session region. |
283
286
 
287
+ ### NewRelic APM + Serverless Convergence
288
+
289
+ #### Migrate Alerts from Lambda to APM
290
+
291
+ ```bash
292
+ newrelic-lambda apm alerts-migrate \
293
+ --nr-account-id <account id> \
294
+ --nr-api-key <api key>
295
+ --function <Lambda-function-name>
296
+ ```
297
+
284
298
  ### NewRelic Otel Ingestions Install
285
299
 
286
300
  #### Install Otel Log Ingestion
@@ -1,27 +1,29 @@
1
1
  newrelic_lambda_cli/__init__.py,sha256=u4cy7hwbrNjK3xtldGQD_51aoNgSqGQ3IzFYsuvaIM4,149
2
- newrelic_lambda_cli/api.py,sha256=-lRsWBNO0YWV2yHp-zUbS2vs4AYJCn9yJngOrKAvmuo,15133
2
+ newrelic_lambda_cli/api.py,sha256=eBGq-IV73urdqhAA2xsIdf772q0npMiuRCCiCJgS10Q,15287
3
+ newrelic_lambda_cli/apm.py,sha256=ZGEdmRW8jlJOl17TnTO716dwxpDSvEb5_28K6XadPDA,12475
3
4
  newrelic_lambda_cli/cliutils.py,sha256=XDtvTlgbf2uVg01yCJrfJmmgxbF0nIL9x58ETyvefk8,685
4
5
  newrelic_lambda_cli/functions.py,sha256=p57ZUw424BaSUA_Gw8DrYsJOYKROEHEaXgARadqwcP0,2800
5
6
  newrelic_lambda_cli/integrations.py,sha256=r0gxfEqVHTGu3xbr4dOCDwm0-Yhoz3KntfeQRWvoWrQ,31266
6
- newrelic_lambda_cli/layers.py,sha256=pyTbSCwJ2A3KP6YZiN-bkzt210UEj3j6dVnIFgijxWo,20339
7
+ newrelic_lambda_cli/layers.py,sha256=BL7XPMw8zal0UL5nrgDJMknfnhwSkp_LKVkBThWx_do,20673
7
8
  newrelic_lambda_cli/otel_ingestions.py,sha256=vi1Mlfc9nRvRWV7STwK7fDXZGozG8ufKohmpHcaWGic,9250
8
9
  newrelic_lambda_cli/permissions.py,sha256=H7v5IMpKaJIWC4Dff2YcTis4BKAAFIJr9IHWUj1LnF4,9093
9
10
  newrelic_lambda_cli/subscriptions.py,sha256=-BYkltqiDLdmhUB_Ot4w-5vvrKcQC6aHcTBLl1mlUlI,9564
10
- newrelic_lambda_cli/types.py,sha256=_nqOh4VU55CEu5EzUvPVtNeofIuOmF3_DIV1RcT6doA,3526
11
+ newrelic_lambda_cli/types.py,sha256=MC22Gw5u97qcpjLrmExST_2uXSH7qceDEmdMziOslh4,3815
11
12
  newrelic_lambda_cli/utils.py,sha256=_XU5tFx9SxEzvp_brFFQtspBz_qoUZ2H_HDxhB_Qr1U,5902
12
- newrelic_lambda_cli/cli/__init__.py,sha256=FciF2RVqQbpMKqEiN8qjO6qmdLB6Yv8LyyyPYcfJNrc,622
13
+ newrelic_lambda_cli/cli/__init__.py,sha256=vY8EdoHeI5r8DY_zELWMb2hCkm_p6oae6JYPweOq1d4,655
14
+ newrelic_lambda_cli/cli/apm.py,sha256=gISzrUQ7a_QqOmReRGaTdpW1INIpAjPauBbqNt1vuNM,3510
13
15
  newrelic_lambda_cli/cli/decorators.py,sha256=a3agkVfy8omkUSL4aKblwSX95xtxYOGASULDYcJDPHk,1786
14
16
  newrelic_lambda_cli/cli/functions.py,sha256=RSh2Cowe1_oQup8q5YRidp03z-BITo2uzvDh4zvLr4I,2601
15
17
  newrelic_lambda_cli/cli/integrations.py,sha256=aQAWcCCU2kBmbF8fLKwKB9bzSY0uipvnojajjTkhqEs,10461
16
- newrelic_lambda_cli/cli/layers.py,sha256=LM_vMHcfFAjSCtnaH34P0Jn1ixY07s93EzOBsEgRQK0,7584
18
+ newrelic_lambda_cli/cli/layers.py,sha256=iZ6FBvAXeo-WC48E-bgerMx3JAY_AUCI4BL5Raop6sU,7719
17
19
  newrelic_lambda_cli/cli/otel_ingestions.py,sha256=4rTm9iYUo2qdMeqxJSrYLCA6ZXHy5bJnjDn9x54pCYc,6096
18
20
  newrelic_lambda_cli/cli/subscriptions.py,sha256=bUupv5iv3mUkC8t31nnI3BahoKxDnUJ8Rgq4QHJcFNU,5890
19
21
  newrelic_lambda_cli/templates/import-template.yaml,sha256=0r1yeoqpnqtEMggWomALkPG10NiANPWWBqz03rChch8,3771
20
22
  newrelic_lambda_cli/templates/license-key-secret.yaml,sha256=ZldQaLXsyF1K2I4X_AsLdH7kRmLkPUYI3talmhqQyHg,1849
21
23
  newrelic_lambda_cli/templates/nr-lambda-integration-role.yaml,sha256=s7T73B_k-mAwgzJrD2xn8YGUNgn2E1V7Exifrl81ViU,2874
22
- newrelic_lambda_cli-0.9.13.dist-info/licenses/LICENSE,sha256=uuxDzQm0yfq_tNZX0tQYzsZUVRIF0jm3dBLZUojSYzI,11345
23
- newrelic_lambda_cli-0.9.13.dist-info/METADATA,sha256=FgXCoP0n0coh0xQMDFh8h-QsY4ps6ZpjjfwGHDUsWTs,28715
24
- newrelic_lambda_cli-0.9.13.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
25
- newrelic_lambda_cli-0.9.13.dist-info/entry_points.txt,sha256=iks2k9Y4WNgIecsDzreIvMV9pGCjwwKTf33LKKvl2A8,65
26
- newrelic_lambda_cli-0.9.13.dist-info/top_level.txt,sha256=dxX2w58VgSUFiPD8C_lFuY-T2C1kjfeY0xi8iTh0r44,20
27
- newrelic_lambda_cli-0.9.13.dist-info/RECORD,,
24
+ newrelic_lambda_cli-0.9.15.dist-info/licenses/LICENSE,sha256=uuxDzQm0yfq_tNZX0tQYzsZUVRIF0jm3dBLZUojSYzI,11345
25
+ newrelic_lambda_cli-0.9.15.dist-info/METADATA,sha256=9Vv9rKLxLmmkFA3e_NaJROzu1oJ9lf_fquYcNLMIpXg,29411
26
+ newrelic_lambda_cli-0.9.15.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
27
+ newrelic_lambda_cli-0.9.15.dist-info/entry_points.txt,sha256=iks2k9Y4WNgIecsDzreIvMV9pGCjwwKTf33LKKvl2A8,65
28
+ newrelic_lambda_cli-0.9.15.dist-info/top_level.txt,sha256=dxX2w58VgSUFiPD8C_lFuY-T2C1kjfeY0xi8iTh0r44,20
29
+ newrelic_lambda_cli-0.9.15.dist-info/RECORD,,