def install(session, function_arn, layer_arn, account_id, allow_upgrade, verbose): client = session.client("lambda") config = get_function(session, function_arn) if not config: failure("Could not find function: %s" % function_arn) return False region = session.region_name update_kwargs = _add_new_relic(config, region, layer_arn, account_id, allow_upgrade) if not update_kwargs: return False try: res = client.update_function_configuration(**update_kwargs) except botocore.exceptions.ClientError as e: failure("Failed to update configuration for '%s': %s" % (config["Configuration"]["FunctionArn"], e)) return False else: if verbose: click.echo(json.dumps(res, indent=2)) success("Successfully installed layer on %s" % function_arn) return True
def create_integration_account(gql, nr_account_id, linked_account_name, role): """ Creates a New Relic Cloud integration account for the specified AWS IAM role. """ role_arn = role["Role"]["Arn"] account = gql.get_linked_account_by_name(linked_account_name) if account: success( "Cloud integrations account [%s] already exists " "in New Relic account [%d]." % (account["name"], nr_account_id) ) return account account = account = gql.link_account(role_arn, linked_account_name) if account: success( "Cloud integrations account [%s] was created in New Relic account [%s] " "with role [%s]." % (linked_account_name, nr_account_id, role_arn) ) return account failure( "Could not create Cloud integrations account [%s] in New Relic account [%s] " "with role [%s]. This may be due to a previously installed integration. " "Please contact New Relic support for assistance." % (linked_account_name, nr_account_id, role_arn) )
def install_log_ingestion(session, nr_license_key, enable_logs=False): """ Installs the New Relic AWS Lambda log ingestion function and role. Returns True for success and False for failure. """ function = get_function(session, "newrelic-log-ingestion") if function is None: stack_status = check_for_ingest_stack(session) if stack_status is None: click.echo( "Setting up 'newrelic-log-ingestion' function in region: %s" % session.region_name) try: create_log_ingestion_function(session, nr_license_key, enable_logs) except Exception as e: failure( "Failed to create 'newrelic-log-ingestion' function: %s" % e) return False else: failure( "CloudFormation Stack NewRelicLogIngestion exists (status: %s), but " "newrelic-log-ingestion Lambda function does not.\n" "Please manually delete the stack and re-run this command." % stack_status) return False else: success( "The 'newrelic-log-ingestion' function already exists in region %s, " "skipping" % session.region_name) return True
def enable_lambda_integration(gql, nr_account_id, linked_account_name): """ Enables AWS Lambda for the specified New Relic Cloud integrations account. Returns True for success and False for failure. """ account = gql.get_linked_account_by_name(linked_account_name) if account is None: failure("Could not find Cloud integrations account " "[%s] in New Relic account [%d]." % (linked_account_name, nr_account_id)) return False is_lambda_enabled = gql.is_integration_enabled(account["id"], "lambda") if is_lambda_enabled: success("The AWS Lambda integration is already enabled in " "Cloud integrations account [%s] of New Relic account [%d]." % (linked_account_name, nr_account_id)) return True try: integration = gql.enable_integration(account["id"], "aws", "lambda") except Exception: failure( "Could not enable New Relic AWS Lambda integration. Make sure your New " "Relic account is a Pro plan and try this command again.") return False else: success("Integration [id=%s, name=%s] has been enabled in Cloud " "integrations account [%s] of New Relic account [%d]." % ( integration["id"], integration["name"], linked_account_name, nr_account_id, )) return True
def install( ctx, nr_account_id, aws_profile, aws_region, aws_permissions_check, functions, excludes, layer_arn, upgrade, ): """Install New Relic AWS Lambda Layers""" session = boto3.Session(profile_name=aws_profile, region_name=aws_region) if aws_permissions_check: permissions.ensure_lambda_install_permissions(session) functions = get_aliased_functions(session, functions, excludes) install_success = True for function in functions: res = layers.install(session, function, layer_arn, nr_account_id, upgrade) install_success = res and install_success if res: success("Successfully installed layer on %s" % function) if ctx.obj["VERBOSE"]: click.echo(json.dumps(res, indent=2)) if install_success: done("Install Complete") else: failure("Install Incomplete. See messages above for details.", exit=True)
def create_integration_account(gql, input, role): """ Creates a New Relic Cloud integration account for the specified AWS IAM role. """ assert isinstance(gql, NewRelicGQL) assert isinstance(input, IntegrationInstall) role_arn = role["Role"]["Arn"] external_id = parse_arn(role_arn)["account"] account = gql.get_linked_account_by_external_id(external_id) if account: success("Cloud integrations account [%s] already exists " "in New Relic account [%d] with IAM role [%s]." % (account["name"], input.nr_account_id, account["authLabel"])) return account account = account = gql.link_account(role_arn, input.linked_account_name) if account: success( "Cloud integrations account [%s] was created in New Relic account [%s] " "with IAM role [%s]." % (input.linked_account_name, input.nr_account_id, role_arn)) return account failure( "Could not create Cloud integrations account [%s] in New Relic account [%s] " "with role [%s]. This may be due to a previously installed integration. " "Please contact New Relic support for assistance." % (input.linked_account_name, input.nr_account_id, role_arn))
def install( aws_profile, aws_region, aws_permissions_check, functions, excludes, filter_pattern ): """Install New Relic AWS Lambda Log Subscriptions""" session = boto3.Session(profile_name=aws_profile, region_name=aws_region) if aws_permissions_check: permissions.ensure_subscription_install_permissions(session) functions = get_aliased_functions(session, functions, excludes) install_success = True for function in functions: result = subscriptions.create_log_subscription( session, function, filter_pattern ) install_success = result and install_success if result: success("Successfully installed log subscription on %s" % function) if install_success: done("Install Complete") else: failure("Install Incomplete. See messages above for details.", exit=True)
def _exec_change_set(client, change_set, mode, stack_name): click.echo( "Waiting for change set creation to complete, this may take a minute... ", nl=False, ) try: client.get_waiter("change_set_create_complete").wait( ChangeSetName=change_set["Id"], WaiterConfig={"Delay": 10}) except botocore.exceptions.WaiterError as e: response = e.last_response status = response["Status"] reason = response["StatusReason"] if (status == "FAILED" and "The submitted information didn't contain changes." in reason or "No updates are to be performed" in reason): success("No Changes Detected") return raise e client.execute_change_set(ChangeSetName=change_set["Id"]) click.echo( "Waiting for change set to finish execution. This may take a minute... ", nl=False, ) exec_waiter_type = "stack_%s_complete" % mode.lower() client.get_waiter(exec_waiter_type).wait(StackName=stack_name, WaiterConfig={"Delay": 15}) success("Done")
def create_log_ingestion_function(session, nr_license_key, enable_logs=False): client = session.client("cloudformation") stack_name = "NewRelicLogIngestion" template_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "templates", "newrelic-log-ingestion.yaml", ) with open(template_path) as template: client.create_stack( StackName=stack_name, TemplateBody=template.read(), Parameters=[ { "ParameterKey": "NewRelicLicenseKey", "ParameterValue": nr_license_key, }, { "ParameterKey": "NewRelicLoggingEnabled", "ParameterValue": "True" if enable_logs else "False", }, ], Capabilities=["CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"], ) click.echo( "Waiting for stack creation to complete, this may take a minute... ", nl=False, ) client.get_waiter("stack_create_complete").wait(StackName=stack_name) success("Done")
def uninstall(input, function_arn): assert isinstance(input, LayerUninstall) client = input.session.client("lambda") config = get_function(input.session, function_arn) if not config: failure("Could not find function: %s" % function_arn) return False update_kwargs = _remove_new_relic(input, config) if not update_kwargs: return False try: res = client.update_function_configuration(**update_kwargs) except botocore.exceptions.ClientError as e: failure("Failed to update configuration for '%s': %s" % (config["Configuration"]["FunctionArn"], e)) return False else: policy_arn = _get_license_key_policy_arn(input.session) if policy_arn: _detach_license_key_policy(input.session, config["Configuration"]["Role"], policy_arn) if input.verbose: click.echo(json.dumps(res, indent=2)) success("Successfully uninstalled layer on %s" % function_arn) return True
def layer_selection(available_layers, runtime, architecture): if len(available_layers) == 1: return available_layers[0]["LatestMatchingVersion"]["LayerVersionArn"] layer_options = [ layer["LatestMatchingVersion"]["LayerVersionArn"] for layer in available_layers ] if sys.stdout.isatty(): output = "\n".join([ "Discovered multiple layers for runtime %s (%s):" % (runtime, architecture), "", ] + ["%d: %s" % (i, layer) for i, layer in enumerate(layer_options)] + ["", "Select a layer"]) while True: value = click.prompt(output, default=0, type=int) try: selected_layer = layer_options[value] success("Layer %s selected" % selected_layer) return selected_layer except IndexError: failure("Invalid layer selection") else: raise click.UsageError( "Discovered multiple layers for runtime %s (%s):\n%s\n" "Pass --layer-arn to specify a layer ARN" % (runtime, architecture, "\n".join(layer_options)))
def remove_subscription_filter(session, function_name, filter_name): try: session.client("logs").delete_subscription_filter( logGroupName=get_log_group_name(function_name), filterName=filter_name) except botocore.exceptions.ClientError as e: failure("Error removing log subscription filter for '%s': %s" % (function_name, e)) return False else: success("Successfully uninstalled log subscription on %s" % function_name) return True
def install(input, function_arn): assert isinstance(input, LayerInstall) client = input.session.client("lambda") config = get_function(input.session, function_arn) if not config: failure("Could not find function: %s" % function_arn) return False policy_arn = _get_license_key_policy_arn(input.session) if input.enable_extension and not policy_arn and not input.nr_api_key: raise click.UsageError( "In order to use `--enable-extension`, you must first run " "`newrelic-lambda integrations install` with the " "`--enable-license-key-secret` flag. This uses AWS Secrets Manager " "to securely store your New Relic license key in your AWS account. " "If you are unable to use AWS Secrets Manager, re-run this command with " "`--nr-api-key` argument with your New Relic API key to set your license " "key in a NEW_RELIC_LICENSE_KEY environment variable instead.") nr_license_key = None if not policy_arn and input.nr_api_key and input.nr_region: gql = api.validate_gql_credentials(input) nr_license_key = api.retrieve_license_key(gql) update_kwargs = _add_new_relic(input, config, nr_license_key) if not update_kwargs or not isinstance(update_kwargs, dict): return False try: res = client.update_function_configuration(**update_kwargs) except botocore.exceptions.ClientError as e: failure("Failed to update configuration for '%s': %s" % (config["Configuration"]["FunctionArn"], e)) return False else: if input.enable_extension and policy_arn: _attach_license_key_policy(input.session, config["Configuration"]["Role"], policy_arn) if input.enable_extension_function_logs: subscriptions.remove_log_subscription(input, function_arn) if input.verbose: click.echo(json.dumps(res, indent=2)) success("Successfully installed layer on %s" % function_arn) return True
def remove_log_ingestion_function(session): client = session.client("cloudformation") stack_status = check_for_ingest_stack(session) if stack_status is None: click.echo( "No New Relic AWS Lambda log ingestion found in region %s, skipping" % session.region_name ) return click.echo("Deleting New Relic log ingestion stack '%s'" % INGEST_STACK_NAME) client.delete_stack(StackName=INGEST_STACK_NAME) click.echo( "Waiting for stack deletion to complete, this may take a minute... ", nl=False ) client.get_waiter("stack_delete_complete").wait(StackName=INGEST_STACK_NAME) success("Done")
def create_integration_account(gql, nr_account_id, linked_account_name, role): """ Creates a New Relic Cloud integration account for the specified AWS IAM role. """ role_arn = role["Role"]["Arn"] account = gql.get_linked_account_by_name(linked_account_name) if account is None: account = gql.link_account(role_arn, linked_account_name) success( "Cloud integrations account [%s] was created in New Relic account [%s]" "with role [%s]." % (linked_account_name, nr_account_id, role_arn)) else: success("Cloud integrations account [%s] already exists " "in New Relic account [%d]." % (account["name"], nr_account_id)) return account
def remove_integration_role(session, nr_account_id): """ Removes the AWS CloudFormation stack that includes the New Relic AWS Integration IAM role. """ client = session.client("cloudformation") stack_name = "NewRelicLambdaIntegrationRole-%s" % nr_account_id stack_status = get_cf_stack_status(session, stack_name) if stack_status is None: click.echo("No New Relic AWS Lambda Integration found, skipping") return click.echo("Deleting New Relic AWS Lambda Integration stack '%s'" % stack_name) client.delete_stack(StackName=stack_name) click.echo( "Waiting for stack deletion to complete, this may take a minute... ", nl=False ) client.get_waiter("stack_delete_complete").wait(StackName=stack_name) success("Done")
def _create_subscription_filter( session, function_name, destination_arn, filter_pattern ): try: session.client("logs").put_subscription_filter( logGroupName=_get_log_group_name(function_name), filterName="NewRelicLogStreaming", filterPattern=filter_pattern, destinationArn=destination_arn, ) except botocore.exceptions.ClientError as e: failure( "Error creating log subscription filter for '%s': %s" % (function_name, e) ) return False else: success("Successfully installed log subscription on %s" % function_name) return True
def enable_lambda_integration(gql, input, linked_account_id): """ Enables AWS Lambda for the specified New Relic Cloud integrations account. Returns True for success and False for failure. """ assert isinstance(gql, NewRelicGQL) assert isinstance(input, IntegrationInstall) account = gql.get_linked_account_by_id(linked_account_id) if account is None: failure("Could not find Cloud integrations account " "[%d] in New Relic account [%d]." % (linked_account_id, input.nr_account_id)) return False is_lambda_enabled = gql.is_integration_enabled(linked_account_id, "lambda") if is_lambda_enabled: success("The AWS Lambda integration is already enabled in " "Cloud integrations account [%s] of New Relic account [%d]." % (account["name"], input.nr_account_id)) return True try: integration = gql.enable_integration(linked_account_id, "aws", "lambda") except Exception: failure( "Could not enable New Relic AWS Lambda integration for Cloud integrations " "account [%s] in New Relic account [%d]. Please contact New Relic Support " "for assistance." % (account["name"], input.nr_account_id)) return False if integration: success("Integration [id=%s, name=%s] has been enabled in Cloud " "integrations account [%s] of New Relic account [%d]." % ( integration["id"], integration["name"], account["name"], input.nr_account_id, )) return True failure( "Something went wrong while enabling the New Relic AWS Lambda integration. " "Please contact New Relic support for further assistance.") return False
def create_integration_role(input): """ Creates a AWS CloudFormation stack that adds the New Relic AWSLambda Integration IAM role. This can be overridden with the `role_arn` parameter, which just checks that the role exists. """ assert isinstance(input, IntegrationInstall) if input.integration_arn is not None: role = _get_role(input.session, input.integration_arn) if role: success( "Found existing AWS IAM role '%s', using it with the New Relic Lambda " "integration" % input.integration_arn) return role failure( "Could not find AWS IAM role '%s', please verify it exists and run this " "command again" % input.integration_arn) return role_name = "NewRelicLambdaIntegrationRole_%s" % input.nr_account_id stack_name = "NewRelicLambdaIntegrationRole-%s" % input.nr_account_id role = _get_role(input.session, role_name) if role: success("New Relic AWS Lambda integration role '%s' already exists" % role_name) return role stack_status = _get_cf_stack_status(input.session, stack_name) if stack_status is None: _create_role(input) role = _get_role(input.session, role_name) success("Created role [%s] in AWS account." % role_name) return role failure( "Cannot create CloudFormation stack %s because it exists in state %s" % (stack_name, stack_status))
def _create_role(input): assert isinstance(input, IntegrationInstall) client = input.session.client("cloudformation") role_policy_name = input.role_name or "" stack_name = "NewRelicLambdaIntegrationRole-%d" % input.nr_account_id template_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "templates", "nr-lambda-integration-role.yaml", ) with open(template_path) as template: client.create_stack( StackName=stack_name, TemplateBody=template.read(), Parameters=[ { "ParameterKey": "NewRelicAccountNumber", "ParameterValue": str(input.nr_account_id), }, { "ParameterKey": "PolicyName", "ParameterValue": role_policy_name }, ], Capabilities=["CAPABILITY_NAMED_IAM"], Tags=[{ "Key": key, "Value": value } for key, value in input.tags] if input.tags else [], ) click.echo("Waiting for stack creation to complete... ", nl=False) try: client.get_waiter("stack_create_complete").wait( StackName=stack_name) except botocore.exceptions.WaiterError as e: failure(e.last_response["Status"]["StatusReason"]) else: success("Done")
def create_integration_role(session, role_policy, nr_account_id): """ Creates a AWS CloudFormation stack that adds the New Relic AWSLambda Integration IAM role. """ role_name = "NewRelicLambdaIntegrationRole_%s" % nr_account_id stack_name = "NewRelicLambdaIntegrationRole-%s" % nr_account_id role = get_role(session, role_name) if role: success("New Relic AWS Lambda integration role '%s' already exists" % role_name) return role stack_status = get_cf_stack_status(session, stack_name) if stack_status is None: create_role(session, role_policy, nr_account_id) role = get_role(session, role_name) success("Created role [%s] with policy [%s] in AWS account." % (role_name, role_policy)) return role failure( "Cannot create CloudFormation stack %s because it exists in state %s" % (stack_name, stack_status))
def remove_log_ingestion_function(input): assert isinstance(input, IntegrationUninstall) client = input.session.client("cloudformation") stack_status = _check_for_ingest_stack(input.session) if stack_status is None: click.echo( "No New Relic AWS Lambda log ingestion found in region %s, skipping" % input.session.region_name) return click.echo("Deleting New Relic log ingestion stack '%s'" % get_resource_name(INGEST_STACK_NAME, resource_prefix=input.resource_prefix)) client.delete_stack(StackName=get_resource_name( INGEST_STACK_NAME, resource_prefix=input.resource_prefix)) click.echo( "Waiting for stack deletion to complete, this may take a minute... ", nl=False) client.get_waiter("stack_delete_complete").wait( StackName=get_resource_name(INGEST_STACK_NAME, resource_prefix=input.resource_prefix)) success("Done")
def remove_license_key(input): assert isinstance(input, (IntegrationUninstall, IntegrationUpdate)) client = input.session.client("cloudformation") stack_status = _get_cf_stack_status(input.session, LICENSE_KEY_STACK_NAME) if stack_status is None: click.echo( "No New Relic license key secret found in region %s, skipping" % input.session.region_name) return click.echo("Deleting stack '%s'" % LICENSE_KEY_STACK_NAME) client.delete_stack(StackName=LICENSE_KEY_STACK_NAME) click.echo( "Waiting for stack deletion to complete, this may take a minute... ", nl=False) try: client.get_waiter("stack_delete_complete").wait( StackName=LICENSE_KEY_STACK_NAME) except botocore.exceptions.WaiterError as e: failure(e.last_response["Status"]["StatusReason"]) else: success("Done")
def remove_license_key(input): assert isinstance(input, (IntegrationUninstall, IntegrationUpdate)) client = input.session.client("cloudformation") stack_status = _get_cf_stack_status( input.session, get_resource_name(LICENSE_KEY_STACK_NAME, resource_prefix=input.resource_prefix)) if stack_status is None: click.echo( "No New Relic license key secret found in region %s, skipping" % input.session.region_name) return click.echo("Deleting stack '%s'" % get_resource_name( LICENSE_KEY_STACK_NAME, resource_prefix=input.resource_prefix)) client.delete_stack(StackName=get_resource_name( LICENSE_KEY_STACK_NAME, resource_prefix=input.resource_prefix)) click.echo( "Waiting for stack deletion to complete, this may take a minute... ", nl=False) client.get_waiter("stack_delete_complete").wait( StackName=get_resource_name(LICENSE_KEY_STACK_NAME, resource_prefix=input.resource_prefix)) success("Done")
def remove_integration_role(input): """ Removes the AWS CloudFormation stack that includes the New Relic AWS Integration IAM role. """ assert isinstance(input, IntegrationUninstall) client = input.session.client("cloudformation") stack_name = "NewRelicLambdaIntegrationRole-%s" % input.nr_account_id stack_status = _get_cf_stack_status(input.session, stack_name) if stack_status is None: click.echo("No New Relic AWS Lambda Integration found, skipping") return click.echo("Deleting New Relic AWS Lambda Integration stack '%s'" % stack_name) client.delete_stack(StackName=stack_name) click.echo( "Waiting for stack deletion to complete, this may take a minute... ", nl=False) try: client.get_waiter("stack_delete_complete").wait(StackName=stack_name) except botocore.exceptions.WaiterError as e: failure(e.last_response["Status"]["StatusReason"]) else: success("Done")
def update_log_ingestion_function(input): assert isinstance(input, IntegrationUpdate) # Detect an old-style nested install and unwrap it client = input.session.client("cloudformation") resources = client.describe_stack_resources( StackName=INGEST_STACK_NAME, LogicalResourceId="NewRelicLogIngestion") stack_resources = resources["StackResources"] # The nested installs had a single Application resource if (len(stack_resources) > 0 and stack_resources[0]["ResourceType"] == "AWS::CloudFormation::Stack"): click.echo("Unwrapping nested stack... ", nl=False) # Set the ingest function itself to disallow deletes nested_stack = stack_resources[0] template_response = client.get_template( StackName=nested_stack["PhysicalResourceId"], TemplateStage="Processed") template_body = template_response["TemplateBody"] template_body["Resources"]["NewRelicLogIngestionFunction"][ "DeletionPolicy"] = "Retain" template_body["Resources"]["NewRelicLogIngestionFunctionRole"][ "DeletionPolicy"] = "Retain" # We can't change props during import, so let's set them to their current values lambda_client = input.session.client("lambda") old_props = lambda_client.get_function_configuration( FunctionName="newrelic-log-ingestion") old_role_name = old_props["Role"].split("/")[-1] old_nr_license_key = old_props["Environment"]["Variables"][ "LICENSE_KEY"] old_enable_logs = False if ("LOGGING_ENABLED" in old_props["Environment"]["Variables"] and old_props["Environment"]["Variables"] ["LOGGING_ENABLED"].lower() == "true"): old_enable_logs = True old_memory_size = old_props["MemorySize"] old_timeout = old_props["Timeout"] # Prepare to orphan the ingest function params = [{ "ParameterKey": name, "UsePreviousValue": True } for name in template_body["Parameters"]] client.update_stack( StackName=nested_stack["PhysicalResourceId"], TemplateBody=json.dumps(template_body), Parameters=params, Capabilities=["CAPABILITY_IAM"], Tags=[{ "Key": key, "Value": value } for key, value in input.tags] if input.tags else [], ) try: client.get_waiter("stack_update_complete").wait( StackName=nested_stack["PhysicalResourceId"]) except botocore.exceptions.WaiterError as e: failure(e.last_response["Status"]["StatusReason"]) raise e else: success("Done") click.echo("Removing outer stack... ", nl=False) # Delete the parent stack, which will delete its child and orphan the # ingest function client.delete_stack(StackName=INGEST_STACK_NAME) try: client.get_waiter("stack_delete_complete").wait( StackName=INGEST_STACK_NAME) except botocore.exceptions.WaiterError as e: failure(e.last_response["Status"]["StatusReason"]) raise e else: success("Done") click.echo("Starting import") _import_log_ingestion_function( input._replace( enable_logs=old_enable_logs, memory_size=old_memory_size, timeout=old_timeout, role_name=old_role_name, ), nr_license_key=old_nr_license_key, ) # Now that we've unnested, do the actual update # Not a nested install; just update _create_log_ingestion_function( input, nr_license_key=None, mode="UPDATE", )
def install_license_key(input, nr_license_key, policy_name=None, mode="CREATE"): assert isinstance(input, (IntegrationInstall, IntegrationUpdate)) lk_stack_status = _get_cf_stack_status(input.session, LICENSE_KEY_STACK_NAME, input.nr_account_id) if lk_stack_status is not None: success("Managed secret already exists") return True click.echo("Setting up %s stack in region: %s" % (LICENSE_KEY_STACK_NAME, input.session.region_name)) try: client = input.session.client("cloudformation") update_mode = mode == "UPDATE" parameters = [] if policy_name is not None: parameters.append({ "ParameterKey": "PolicyName", "ParameterValue": policy_name }) elif update_mode: parameters.append({ "ParameterKey": "PolicyName", "UsePreviousValue": True }) parameters.extend(( { "ParameterKey": "LicenseKey", "ParameterValue": nr_license_key }, { "ParameterKey": "NrAccountId", "ParameterValue": str(input.nr_account_id), }, )) change_set_name = "%s-%s-%d" % ( LICENSE_KEY_STACK_NAME, mode, int(time.time()), ) click.echo("Creating change set: %s" % change_set_name) template_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "templates", "license-key-secret.yaml", ) with open(template_path) as template: change_set = client.create_change_set( StackName=LICENSE_KEY_STACK_NAME, TemplateBody=template.read(), Parameters=parameters, Capabilities=["CAPABILITY_NAMED_IAM"], Tags=[{ "Key": key, "Value": value } for key, value in input.tags] if input.tags else [], ChangeSetType=mode, ChangeSetName=change_set_name, ) _exec_change_set(client, change_set, mode, stack_name=LICENSE_KEY_STACK_NAME) except Exception as e: failure("Failed to create %s stack: %s" % (LICENSE_KEY_STACK_NAME, e)) return False else: return True
def _add_new_relic(config, region, layer_arn, account_id, allow_upgrade): runtime = config["Configuration"]["Runtime"] if runtime not in utils.RUNTIME_CONFIG: failure("Unsupported Lambda runtime for '%s': %s" % (config["Configuration"]["FunctionArn"], runtime)) return handler = config["Configuration"]["Handler"] runtime_handler = utils.RUNTIME_CONFIG.get(runtime, {}).get("Handler") if not allow_upgrade and handler == runtime_handler: success( "Already installed on function '%s'. Pass --upgrade (or -u) to allow " "upgrade or reinstall to latest layer version." % config["Configuration"]["FunctionArn"]) return existing_layers = [ layer["Arn"] for layer in config["Configuration"].get("Layers", []) if not layer["Arn"].startswith(utils.get_arn_prefix(region)) ] new_relic_layers = [] if layer_arn: new_relic_layers = [layer_arn] else: # discover compatible layers... available_layers = index(region, runtime) if not available_layers: failure("No Lambda layers published for '%s' runtime: %s" % (config["Configuration"]["FunctionArn"], runtime)) return # TODO: MAke this a layer selection screen if len(available_layers) > 1: message = ["Discovered layers for runtime (%s)" % runtime] for layer in available_layers: message.append("%s\t%s" % ( layer["LatestMatchingVersion"]["LayerVersionArn"], layer.get("Description", ""), )) message.append( "\nMultiple layers found. Pass --layer-arn to specify layer ARN" ) raise click.UsageError("\n".join(message)) new_relic_layers = [ available_layers[0]["LatestMatchingVersion"]["LayerVersionArn"] ] update_kwargs = { "FunctionName": config["Configuration"]["FunctionArn"], "Handler": runtime_handler, "Environment": { "Variables": config["Configuration"].get("Environment", {}).get("Variables", {}) }, "Layers": new_relic_layers + existing_layers, } # Update the account id update_kwargs["Environment"]["Variables"]["NEW_RELIC_ACCOUNT_ID"] = str( account_id) # Update the NEW_RELIC_LAMBDA_HANDLER envvars only when it's a new install. if handler != runtime_handler: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LAMBDA_HANDLER"] = handler return update_kwargs
def _add_new_relic(input, config, nr_license_key): assert isinstance(input, LayerInstall) aws_region = input.session.region_name runtime = config["Configuration"]["Runtime"] if runtime not in utils.RUNTIME_CONFIG: failure("Unsupported Lambda runtime for '%s': %s" % (config["Configuration"]["FunctionArn"], runtime)) return True handler = config["Configuration"]["Handler"] runtime_handler = utils.RUNTIME_CONFIG.get(runtime, {}).get("Handler") existing_newrelic_layer = [ layer["Arn"] for layer in config["Configuration"].get("Layers", []) if layer["Arn"].startswith(utils.get_arn_prefix(aws_region)) ] if not input.upgrade and existing_newrelic_layer: success( "Already installed on function '%s'. Pass --upgrade (or -u) to allow " "upgrade or reinstall to latest layer version." % config["Configuration"]["FunctionArn"]) return True existing_layers = [ layer["Arn"] for layer in config["Configuration"].get("Layers", []) if not layer["Arn"].startswith(utils.get_arn_prefix(aws_region)) ] new_relic_layers = [] if input.layer_arn: new_relic_layers = [input.layer_arn] else: # discover compatible layers... available_layers = index(aws_region, runtime) if not available_layers: failure("No Lambda layers published for '%s' runtime: %s" % (config["Configuration"]["FunctionArn"], runtime)) return False # TODO: MAke this a layer selection screen if len(available_layers) > 1: message = ["Discovered layers for runtime (%s)" % runtime] for layer in available_layers: message.append("%s\t%s" % ( layer["LatestMatchingVersion"]["LayerVersionArn"], layer.get("Description", ""), )) message.append( "\nMultiple layers found. Pass --layer-arn to specify layer ARN" ) raise click.UsageError("\n".join(message)) new_relic_layers = [ available_layers[0]["LatestMatchingVersion"]["LayerVersionArn"] ] update_kwargs = { "FunctionName": config["Configuration"]["FunctionArn"], "Environment": { "Variables": config["Configuration"].get("Environment", {}).get("Variables", {}) }, "Layers": new_relic_layers + existing_layers, } # Only used by Python and Node.js runtimes if runtime_handler: update_kwargs["Handler"] = runtime_handler # Update the account id update_kwargs["Environment"]["Variables"]["NEW_RELIC_ACCOUNT_ID"] = str( input.nr_account_id) # Update the NEW_RELIC_LAMBDA_HANDLER envvars only when it's a new install. if runtime_handler and handler != runtime_handler: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LAMBDA_HANDLER"] = handler if input.enable_extension: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LAMBDA_EXTENSION_ENABLED"] = "true" if input.enable_extension_function_logs: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_EXTENSION_SEND_FUNCTION_LOGS"] = "true" else: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_EXTENSION_SEND_FUNCTION_LOGS"] = "false" if input.nr_region == "staging": update_kwargs["Environment"]["Variables"][ "NEW_RELIC_TELEMETRY_ENDPOINT"] = "https://staging-cloud-collector.newrelic.com/aws/lambda/v1" update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LOG_ENDPOINT"] = "https://staging-log-api.newrelic.com/log/v1" if nr_license_key: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LICENSE_KEY"] = nr_license_key else: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LAMBDA_EXTENSION_ENABLED"] = "false" return update_kwargs
def _add_new_relic(input, config, nr_license_key): assert isinstance(input, LayerInstall) aws_region = input.session.region_name runtime = config["Configuration"]["Runtime"] if runtime not in utils.RUNTIME_CONFIG: failure("Unsupported Lambda runtime for '%s': %s" % (config["Configuration"]["FunctionArn"], runtime)) return True architectures = config["Configuration"].get("Architectures", ["x86_64"]) architecture = architectures[0] handler = config["Configuration"]["Handler"] runtime_handler = utils.RUNTIME_CONFIG.get(runtime, {}).get("Handler") if "java" in runtime: postfix = input.java_handler_method or "handleRequest" runtime_handler = runtime_handler + postfix existing_newrelic_layer = [ layer["Arn"] for layer in config["Configuration"].get("Layers", []) if layer["Arn"].startswith(utils.get_arn_prefix(aws_region)) ] if not input.upgrade and existing_newrelic_layer: success( "Already installed on function '%s'. Pass --upgrade (or -u) to allow " "upgrade or reinstall to latest layer version." % config["Configuration"]["FunctionArn"]) return True existing_layers = [ layer["Arn"] for layer in config["Configuration"].get("Layers", []) if not layer["Arn"].startswith(utils.get_arn_prefix(aws_region)) ] new_relic_layer = [] if input.layer_arn: new_relic_layer = input.layer_arn else: # discover compatible layers... available_layers = index(aws_region, runtime, architecture) if not available_layers: failure("No Lambda layers published for %s (%s) runtime: %s" % (config["Configuration"]["FunctionArn"], runtime, architecture)) return False new_relic_layer = layer_selection(available_layers, runtime, architecture) update_kwargs = { "FunctionName": config["Configuration"]["FunctionArn"], "Environment": { "Variables": config["Configuration"].get("Environment", {}).get("Variables", {}) }, "Layers": [new_relic_layer] + existing_layers, } # We don't want to modify the handler if the NewRelicLambdaExtension layer # has been selected if any("NewRelicLambdaExtension" in s for s in new_relic_layer): runtime_handler = None # Only used by Python, Node.js and Java runtimes not using the # NewRelicLambdaExtension layer if runtime_handler: update_kwargs["Handler"] = runtime_handler # Update the account id update_kwargs["Environment"]["Variables"]["NEW_RELIC_ACCOUNT_ID"] = str( input.nr_account_id) # Update the NEW_RELIC_LAMBDA_HANDLER envvars only when it's a new install. if runtime_handler and handler != runtime_handler: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LAMBDA_HANDLER"] = handler if input.enable_extension and not utils.supports_lambda_extension(runtime): warning( "The %s runtime for %s does not support Lambda Extensions, reverting to a " "CloudWatch Logs based ingestion. Make sure you run `newrelic-lambda " "integrations install` command to install the New Relic log ingestion " "function and `newrelic-lambda subscriptions install` to create the log " "subscription filter." % (runtime, config["Configuration"]["FunctionName"])) if input.enable_extension and utils.supports_lambda_extension(runtime): update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LAMBDA_EXTENSION_ENABLED"] = "true" update_kwargs["Environment"]["Variables"][ "NEW_RELIC_EXTENSION_SEND_FUNCTION_LOGS"] = ( "true" if input.enable_extension_function_logs else "false") if input.nr_region == "staging": update_kwargs["Environment"]["Variables"][ "NEW_RELIC_TELEMETRY_ENDPOINT"] = "https://staging-cloud-collector.newrelic.com/aws/lambda/v1" update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LOG_ENDPOINT"] = "https://staging-log-api.newrelic.com/log/v1" if nr_license_key: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LICENSE_KEY"] = nr_license_key else: update_kwargs["Environment"]["Variables"][ "NEW_RELIC_LAMBDA_EXTENSION_ENABLED"] = "false" return update_kwargs