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..8a697f8 100644 --- a/newrelic_lambda_cli/cli/integrations.py +++ b/newrelic_lambda_cli/cli/integrations.py @@ -64,6 +64,12 @@ def register(group): required=False, ) @add_options(NR_OPTIONS) +@click.option( + "--nr-ingest-key", + help="New Relic Ingest Key", + metavar="", + required=False, +) @click.option( "--timeout", "-t", @@ -113,6 +119,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 +142,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 +169,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 +311,12 @@ def uninstall(**kwargs): type=click.INT, ) @add_options(NR_OPTIONS) +@click.option( + "--nr-ingest-key", + help="New Relic Ingest Key", + metavar="", + required=False, +) @click.option( "--timeout", "-t", @@ -323,6 +358,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 +373,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", diff --git a/tests/cli/test_integrations.py b/tests/cli/test_integrations.py index ab3974a..46301ad 100644 --- a/tests/cli/test_integrations.py +++ b/tests/cli/test_integrations.py @@ -177,3 +177,268 @@ 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