Beispiel #1
0
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
Beispiel #2
0
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)
    )
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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)
Beispiel #6
0
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))
Beispiel #7
0
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)
Beispiel #8
0
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")
Beispiel #9
0
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
Beispiel #11
0
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)))
Beispiel #12
0
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")
Beispiel #15
0
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")
Beispiel #17
0
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
Beispiel #18
0
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
Beispiel #19
0
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))
Beispiel #20
0
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")
Beispiel #21
0
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))
Beispiel #22
0
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")
Beispiel #23
0
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")
Beispiel #24
0
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")
Beispiel #25
0
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")
Beispiel #26
0
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",
    )
Beispiel #27
0
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
Beispiel #28
0
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
Beispiel #30
0
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