From d0b7c564da96220719201daf824fafd42e25ecc2 Mon Sep 17 00:00:00 2001 From: Sashwatdas123 Date: Thu, 6 Nov 2025 15:02:05 +0530 Subject: [PATCH 1/4] feat: Add support for New Relic ingest key in integration commands --- newrelic_lambda_cli/cli/decorators.py | 2 +- newrelic_lambda_cli/cli/integrations.py | 67 +++++++++++++++++++++---- newrelic_lambda_cli/types.py | 2 + 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/newrelic_lambda_cli/cli/decorators.py b/newrelic_lambda_cli/cli/decorators.py index fc98496..8f13873 100644 --- a/newrelic_lambda_cli/cli/decorators.py +++ b/newrelic_lambda_cli/cli/decorators.py @@ -46,7 +46,7 @@ envvar="NEW_RELIC_API_KEY", help="New Relic User API Key", metavar="", - required=True, + required=False, ), click.option( "--nr-region", diff --git a/newrelic_lambda_cli/cli/integrations.py b/newrelic_lambda_cli/cli/integrations.py index d7d1d13..9fa8c20 100644 --- a/newrelic_lambda_cli/cli/integrations.py +++ b/newrelic_lambda_cli/cli/integrations.py @@ -64,6 +64,13 @@ def register(group): required=False, ) @add_options(NR_OPTIONS) +@click.option( + "--nr-ingest-key", + envvar="NEW_RELIC_INGEST_KEY", + help="New Relic License/Ingest Key. When used alone (without --nr-api-key), only AWS resources will be created; cloud integration linking will be skipped.", + metavar="", + required=False, +) @click.option( "--timeout", "-t", @@ -113,6 +120,12 @@ def install(ctx, **kwargs): """Install New Relic AWS Lambda Integration""" input = IntegrationInstall(session=None, verbose=ctx.obj["VERBOSE"], **kwargs) + if input.nr_api_key and input.nr_ingest_key: + raise click.UsageError( + "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both." + ) + if not input.nr_api_key and not input.nr_ingest_key: + raise click.UsageError("Please provide either --nr-api-key or --nr-ingest-key.") input = input._replace( session=boto3.Session( profile_name=input.aws_profile, region_name=input.aws_region @@ -130,11 +143,26 @@ def install(ctx, **kwargs): if input.aws_permissions_check: permissions.ensure_integration_install_permissions(input) - click.echo("Validating New Relic credentials") - gql_client = api.validate_gql_credentials(input) + nr_license_key = None + gql_client = None - click.echo("Retrieving integration license key") - nr_license_key = api.retrieve_license_key(gql_client) + if input.nr_ingest_key: + click.echo("Using provided New Relic ingest key") + nr_license_key = input.nr_ingest_key + if input.nr_api_key: + click.echo("Validating New Relic credentials for account linking") + gql_client = api.validate_gql_credentials(input) + else: + click.echo( + "Note: Skipping cloud integration account linking " + "(requires --nr-api-key). Only AWS resources will be created." + ) + else: + click.echo("Validating New Relic credentials") + gql_client = api.validate_gql_credentials(input) + + click.echo("Retrieving integration license key") + nr_license_key = api.retrieve_license_key(gql_client) install_success = True @@ -142,18 +170,20 @@ def install(ctx, **kwargs): role = integrations.create_integration_role(input) install_success = install_success and role - if role: + if role and gql_client: click.echo("Linking New Relic account to AWS account") res = api.create_integration_account(gql_client, input, role) install_success = res and install_success - linked_account_id = res.get("id") + linked_account_id = res.get("id") if res else None if linked_account_id: click.echo( "Enabling Lambda integration on the link between New Relic and AWS" ) res = api.enable_lambda_integration(gql_client, input, linked_account_id) install_success = res and install_success + elif role and not gql_client: + click.echo("Skipping cloud integration (no API key provided)") if input.enable_license_key_secret: click.echo("Creating the managed secret for the New Relic License Key") @@ -282,6 +312,13 @@ def uninstall(**kwargs): type=click.INT, ) @add_options(NR_OPTIONS) +@click.option( + "--nr-ingest-key", + envvar="NEW_RELIC_INGEST_KEY", + help="New Relic License/Ingest Key (alternative to --nr-api-key for update operations)", + metavar="", + required=False, +) @click.option( "--timeout", "-t", @@ -323,6 +360,12 @@ def update(**kwargs): """UpdateNew Relic AWS Lambda Integration""" input = IntegrationUpdate(session=None, **kwargs) + if input.nr_api_key and input.nr_ingest_key: + raise click.UsageError( + "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both." + ) + if not input.nr_api_key and not input.nr_ingest_key: + raise click.UsageError("Please provide either --nr-api-key or --nr-ingest-key.") input = input._replace( session=boto3.Session( profile_name=input.aws_profile, region_name=input.aws_region @@ -332,11 +375,15 @@ def update(**kwargs): if input.aws_permissions_check: permissions.ensure_integration_install_permissions(input) - click.echo("Validating New Relic credentials") - gql_client = api.validate_gql_credentials(input) + if input.nr_ingest_key: + click.echo("Using provided New Relic ingest key") + nr_license_key = input.nr_ingest_key + else: + click.echo("Validating New Relic credentials") + gql_client = api.validate_gql_credentials(input) - click.echo("Retrieving integration license key") - nr_license_key = api.retrieve_license_key(gql_client) + click.echo("Retrieving integration license key") + nr_license_key = api.retrieve_license_key(gql_client) update_success = True diff --git a/newrelic_lambda_cli/types.py b/newrelic_lambda_cli/types.py index 934c43b..76c7d51 100644 --- a/newrelic_lambda_cli/types.py +++ b/newrelic_lambda_cli/types.py @@ -13,6 +13,7 @@ "linked_account_name", "nr_account_id", "nr_api_key", + "nr_ingest_key", "nr_region", "timeout", "role_name", @@ -42,6 +43,7 @@ "memory_size", "nr_account_id", "nr_api_key", + "nr_ingest_key", "nr_region", "timeout", "role_name", From d9979e2728d4f0e0a436f19bd61eebe16053c42d Mon Sep 17 00:00:00 2001 From: Sashwatdas123 Date: Thu, 6 Nov 2025 16:01:27 +0530 Subject: [PATCH 2/4] feat: Add tests for integration install and update --- tests/cli/test_integrations.py | 256 +++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/tests/cli/test_integrations.py b/tests/cli/test_integrations.py index ab3974a..f585c24 100644 --- a/tests/cli/test_integrations.py +++ b/tests/cli/test_integrations.py @@ -177,3 +177,259 @@ def test_integrations_update( call.install_license_key().__bool__(), ] ) + + +@patch("newrelic_lambda_cli.cli.integrations.boto3") +@patch("newrelic_lambda_cli.cli.integrations.integrations") +@patch("newrelic_lambda_cli.cli.integrations.permissions") +def test_integrations_install_with_ingest_key_only( + permissions_mock, integrations_mock, boto3_mock, cli_runner +): + """ + Test install with only --nr-ingest-key (no API key) + Should create AWS resources but skip cloud integration linking + """ + register_groups(cli) + result = cli_runner.invoke( + cli, + [ + "integrations", + "install", + "--nr-account-id", + "12345678", + "--nr-ingest-key", + "test_ingest_key", + "--linked-account-name", + "test_linked_account", + "--aws-permissions-check", + ], + env={"AWS_DEFAULT_REGION": "us-east-1"}, + ) + + assert result.exit_code == 0, result.stderr + assert "Using provided New Relic ingest key" in result.output + assert "Skipping cloud integration account linking" in result.output + + boto3_mock.assert_has_calls( + [call.Session(profile_name=None, region_name="us-east-1")] + ) + + permissions_mock.assert_has_calls( + [call.ensure_integration_install_permissions(ANY)] + ) + + integrations_mock.assert_has_calls( + [ + call.create_integration_role(ANY), + call.install_log_ingestion(ANY, "test_ingest_key"), + ], + any_order=True, + ) + + +@patch("newrelic_lambda_cli.cli.integrations.boto3") +@patch("newrelic_lambda_cli.cli.integrations.integrations") +@patch("newrelic_lambda_cli.cli.integrations.permissions") +@patch("newrelic_lambda_cli.cli.integrations.api") +def test_integrations_install_with_both_keys( + api_mock, permissions_mock, integrations_mock, boto3_mock, cli_runner +): + """ + Test install with both --nr-ingest-key and --nr-api-key + Should fail with usage error + """ + register_groups(cli) + result = cli_runner.invoke( + cli, + [ + "integrations", + "install", + "--nr-account-id", + "12345678", + "--nr-api-key", + "test_api_key", + "--nr-ingest-key", + "test_ingest_key", + "--linked-account-name", + "test_linked_account", + ], + env={"AWS_DEFAULT_REGION": "us-east-1"}, + ) + + assert result.exit_code != 0 + error_message = result.output + result.stderr + assert "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" in error_message + + +@patch("newrelic_lambda_cli.cli.integrations.boto3") +@patch("newrelic_lambda_cli.cli.integrations.integrations") +@patch("newrelic_lambda_cli.cli.integrations.permissions") +@patch("newrelic_lambda_cli.cli.integrations.api") +def test_integrations_install_with_no_keys( + api_mock, permissions_mock, integrations_mock, boto3_mock, cli_runner +): + """ + Test install with neither --nr-api-key nor --nr-ingest-key + Should fail with usage error + """ + register_groups(cli) + result = cli_runner.invoke( + cli, + [ + "integrations", + "install", + "--nr-account-id", + "12345678", + "--linked-account-name", + "test_linked_account", + ], + env={"AWS_DEFAULT_REGION": "us-east-1"}, + ) + + assert result.exit_code != 0 + error_message = result.output + result.stderr + assert "Please provide either --nr-api-key or --nr-ingest-key" in error_message + + +@patch("newrelic_lambda_cli.cli.integrations.boto3") +@patch("newrelic_lambda_cli.cli.integrations.integrations") +@patch("newrelic_lambda_cli.cli.integrations.permissions") +@patch("newrelic_lambda_cli.cli.integrations.api") +def test_integrations_install_ingest_key_with_api_key( + api_mock, permissions_mock, integrations_mock, boto3_mock, cli_runner +): + """ + Test install with --nr-ingest-key alongside --nr-api-key (both provided) + This should fail as they are mutually exclusive + """ + api_mock.validate_gql_credentials.return_value = MagicMock() + api_mock.create_integration_account.return_value = {"id": "test_id"} + integrations_mock.create_integration_role.return_value = True + + register_groups(cli) + result = cli_runner.invoke( + cli, + [ + "integrations", + "install", + "--nr-account-id", + "12345678", + "--nr-api-key", + "test_api_key", + "--nr-ingest-key", + "test_ingest_key", + "--linked-account-name", + "test_linked_account", + ], + env={"AWS_DEFAULT_REGION": "us-east-1"}, + ) + + assert result.exit_code != 0 + error_message = result.output + result.stderr + assert "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" in error_message + + +@patch("newrelic_lambda_cli.cli.integrations.boto3") +@patch("newrelic_lambda_cli.cli.integrations.integrations") +@patch("newrelic_lambda_cli.cli.integrations.permissions") +def test_integrations_update_with_ingest_key( + permissions_mock, integrations_mock, boto3_mock, cli_runner +): + """ + Test update with --nr-ingest-key instead of --nr-api-key + """ + integrations_mock.update_log_ingestion.return_value = True + integrations_mock.install_license_key.return_value = True + + register_groups(cli) + result = cli_runner.invoke( + cli, + [ + "integrations", + "update", + "--nr-account-id", + "123456789", + "--nr-ingest-key", + "test_ingest_key", + "--aws-permissions-check", + ], + env={"AWS_DEFAULT_REGION": "us-east-1"}, + ) + + assert result.exit_code == 0, result.stderr + assert "Using provided New Relic ingest key" in result.output + + boto3_mock.assert_has_calls( + [call.Session(profile_name=None, region_name="us-east-1")] + ) + permissions_mock.assert_has_calls( + [call.ensure_integration_install_permissions(ANY)] + ) + + integrations_mock.assert_has_calls( + [ + call.update_log_ingestion(ANY), + call.install_license_key(ANY, "test_ingest_key"), + ], + any_order=True, + ) + + +@patch("newrelic_lambda_cli.cli.integrations.boto3") +@patch("newrelic_lambda_cli.cli.integrations.integrations") +@patch("newrelic_lambda_cli.cli.integrations.permissions") +@patch("newrelic_lambda_cli.cli.integrations.api") +def test_integrations_update_with_both_keys( + api_mock, permissions_mock, integrations_mock, boto3_mock, cli_runner +): + """ + Test update with both --nr-api-key and --nr-ingest-key + Should fail with usage error + """ + register_groups(cli) + result = cli_runner.invoke( + cli, + [ + "integrations", + "update", + "--nr-account-id", + "123456789", + "--nr-api-key", + "test_api_key", + "--nr-ingest-key", + "test_ingest_key", + ], + env={"AWS_DEFAULT_REGION": "us-east-1"}, + ) + + assert result.exit_code != 0 + error_message = result.output + result.stderr + assert "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" in error_message + + +@patch("newrelic_lambda_cli.cli.integrations.boto3") +@patch("newrelic_lambda_cli.cli.integrations.integrations") +@patch("newrelic_lambda_cli.cli.integrations.permissions") +@patch("newrelic_lambda_cli.cli.integrations.api") +def test_integrations_update_with_no_keys( + api_mock, permissions_mock, integrations_mock, boto3_mock, cli_runner +): + """ + Test update with neither --nr-api-key nor --nr-ingest-key + Should fail with usage error + """ + register_groups(cli) + result = cli_runner.invoke( + cli, + [ + "integrations", + "update", + "--nr-account-id", + "123456789", + ], + env={"AWS_DEFAULT_REGION": "us-east-1"}, + ) + + assert result.exit_code != 0 + error_message = result.output + result.stderr + assert "Please provide either --nr-api-key or --nr-ingest-key" in error_message From 0c0a160b259bef50f9a7d14bceece7db445a2930 Mon Sep 17 00:00:00 2001 From: Sashwatdas123 Date: Thu, 6 Nov 2025 16:04:12 +0530 Subject: [PATCH 3/4] fix: Formatted using black --- tests/cli/test_integrations.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/cli/test_integrations.py b/tests/cli/test_integrations.py index f585c24..46301ad 100644 --- a/tests/cli/test_integrations.py +++ b/tests/cli/test_integrations.py @@ -258,7 +258,10 @@ def test_integrations_install_with_both_keys( assert result.exit_code != 0 error_message = result.output + result.stderr - assert "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" in error_message + assert ( + "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" + in error_message + ) @patch("newrelic_lambda_cli.cli.integrations.boto3") @@ -305,7 +308,7 @@ def test_integrations_install_ingest_key_with_api_key( api_mock.validate_gql_credentials.return_value = MagicMock() api_mock.create_integration_account.return_value = {"id": "test_id"} integrations_mock.create_integration_role.return_value = True - + register_groups(cli) result = cli_runner.invoke( cli, @@ -326,7 +329,10 @@ def test_integrations_install_ingest_key_with_api_key( assert result.exit_code != 0 error_message = result.output + result.stderr - assert "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" in error_message + assert ( + "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" + in error_message + ) @patch("newrelic_lambda_cli.cli.integrations.boto3") @@ -340,7 +346,7 @@ def test_integrations_update_with_ingest_key( """ integrations_mock.update_log_ingestion.return_value = True integrations_mock.install_license_key.return_value = True - + register_groups(cli) result = cli_runner.invoke( cli, @@ -404,7 +410,10 @@ def test_integrations_update_with_both_keys( assert result.exit_code != 0 error_message = result.output + result.stderr - assert "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" in error_message + assert ( + "Please provide either the --nr-api-key or the --nr-ingest-key flag, but not both" + in error_message + ) @patch("newrelic_lambda_cli.cli.integrations.boto3") From f38076af8c14ca3ff9ad3ae73adc65e1eaa665ec Mon Sep 17 00:00:00 2001 From: Sashwatdas123 Date: Thu, 6 Nov 2025 17:39:54 +0530 Subject: [PATCH 4/4] refactor: updated --nr-ingest-key description --- newrelic_lambda_cli/cli/integrations.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/newrelic_lambda_cli/cli/integrations.py b/newrelic_lambda_cli/cli/integrations.py index 9fa8c20..8a697f8 100644 --- a/newrelic_lambda_cli/cli/integrations.py +++ b/newrelic_lambda_cli/cli/integrations.py @@ -66,8 +66,7 @@ def register(group): @add_options(NR_OPTIONS) @click.option( "--nr-ingest-key", - envvar="NEW_RELIC_INGEST_KEY", - help="New Relic License/Ingest Key. When used alone (without --nr-api-key), only AWS resources will be created; cloud integration linking will be skipped.", + help="New Relic Ingest Key", metavar="", required=False, ) @@ -314,8 +313,7 @@ def uninstall(**kwargs): @add_options(NR_OPTIONS) @click.option( "--nr-ingest-key", - envvar="NEW_RELIC_INGEST_KEY", - help="New Relic License/Ingest Key (alternative to --nr-api-key for update operations)", + help="New Relic Ingest Key", metavar="", required=False, )