Ejemplo n.º 1
0
def alert_if_domain_expired(_: utils.LambdaEvent) -> List[dict]:
    """Check WHOIS data for each domain in WHOIS_DOMAINS env variable, send alert if errors."""
    domains = env["WHOIS_DOMAINS"].replace(" ", "").split(",")

    futures = helpers.exec_in_thread_and_wait(
        (_domain_is_expiring, (domain, )) for domain in domains)
    results = [future.result() for future in futures.done]

    output = [{
        "domain": result[0],
        "expiringSoon": result[1],
        "expiresDateUTC": result[2],
    } for result in results]

    if any(item["expiringSoon"] for item in output):
        title = "Domain(s) expiring soon"
        msg = ", ".join(f"{item['domain']}={item['expiresDateUTC']}"
                        for item in output if item["expiringSoon"])

        utils.Log.warning("%s: %s", title, msg)
        utils.Log.info("Sending alert notification via %s",
                       env["LAMBDA_NOTIFICATIONS"])

        aws.invoke_lambda(name=env["LAMBDA_NOTIFICATIONS"],
                          payload={
                              "title": title,
                              "payload": msg,
                          })

    return output
Ejemplo n.º 2
0
def trigger_lambdas(event: utils.LambdaEvent) -> Mapping:  # pylint: disable=unused-argument
    """Trigger EPUB creator lambdas."""
    response = {"new_articles": [], "count": 0}
    articles, since = retrieve()

    if not articles:
        utils.Log.info(
            "Found no new articles since %d, skipping 'since' storage", since)
        return response

    response["new_articles"] = [article["url"] for article in articles]
    response["count"] = len(articles)

    utils.Log.debug("Fan-out %d new article(s) to lambda %s",
                    response["count"], env["LAMBDA_PUBLISHER"])
    for article in articles:
        utils.Log.info("Triggering Lambda %s for %s", env["LAMBDA_PUBLISHER"],
                       article)
        aws.invoke_lambda(name=env["LAMBDA_PUBLISHER"],
                          payload=article,
                          invoke_type="Event")

    utils.Log.info("Store new since to LogStream: %d", since)
    aws.send_event_to_logstream(
        log_group=env["SINCE_LOG_GROUP"],
        log_stream=env["SINCE_LOG_GROUP"],
        message={
            "since": since,
            "items": sorted(int(article["item_id"]) for article in articles),
        })

    return response
Ejemplo n.º 3
0
def invoke_publish(_: utils.LambdaEvent):
    """Poll blog's RSS/Atom feed and trigger LAMBDA_PUBLISH for each article added yesterday."""
    posts = _poll_new_posts()

    for index, post in enumerate(posts, start=1):
        utils.Log.warning("Triggering lambda %s with payload URL %s",
                          env["LAMBDA_PUBLISH"], post)
        aws.invoke_lambda(env["LAMBDA_PUBLISH"], payload={"url": post})
        if index < len(posts):
            time.sleep(
                10)  # In case of multiple posts, delay 10 seconds to allow
Ejemplo n.º 4
0
def check_backup_buckets(_: utils.LambdaEvent) -> List[str]:
    """Scan for S3 backup bucket config in BUCKETS_TO_MONITOR env var and run checks on content.

    BUCKETS_TO_MONITOR is a ;-separated string in the format
        <bucket_name>,<retention_days>,<regexp>,<start_day_isoformat>,<tolerance>;<bucket_name>...

    where `bucket_name_x` is the S3 bucket name
    `retention_days` is an int with the max amount of days for any backup to be considered valid.
        Check will also fail if the bucket contains more than `retention_days + 1` keys. The + 1
        is because the way S3 lifecycle polices remove expired keys is non deterministic, so there
        might be moments during the day when the actual amount of keys eccedes of 1 the expected
        value.
    `regexp` is a regular expression to be used to match the bucket keys, use NoRegexp to disable
    `start_day_isoformat` is the date in iso format of the first backup ever (needed for new
        buckets only), use NoStartDay to disable
    `tolerance` is an int with the the maximum expected change in file size compared to the
        previous backup, i.e. the check will fail if the size of today's backup is
        <tolerance percent> bigger or smaller than yesterday backup, use NoTolerance to disable
    """
    configs = []
    output = []
    errors = False

    for config in env["BUCKETS_TO_MONITOR"].split(";"):
        bucket_name, retention_days, regexp, start_day_isoformat, tolerance = config.split(
            ",")
        configs.append([
            bucket_name,
            int(retention_days), None if regexp == "NoRegexp" else regexp, None
            if start_day_isoformat == "NoStartDay" else start_day_isoformat,
            None if tolerance == "NoTolerance" else int(tolerance)
        ])

    futures = utils.helpers.exec_in_thread_and_wait(
        ((_check_bucket_validity, config) for config in configs), False)

    for future in futures.done:
        try:
            output.append(future.result())

        except utils.HandledError as error:
            output.append(str(error))
            errors = True

    if errors:
        aws.invoke_lambda(name=env["LAMBDA_NOTIFICATIONS"],
                          payload={
                              "title": "backups_monitor: errors",
                              "payload": str(output),
                          })

    return output
Ejemplo n.º 5
0
def _send_attachment_to_kindle(key: str,
                               bucket: str,
                               item_id: int = None) -> utils.Response:
    utils.Log.info("Send attachment to %s via %s email notification service",
                   env["KINDLE_EMAIL"], env["LAMBDA_NOTIFICATIONS"])

    extension = None
    if key.endswith(".mobi"):
        extension = "mobi"
    if key.endswith(".html"):
        extension = "html"
    if extension not in ["mobi", "html"]:
        raise utils.HandledError(
            message=
            "Invalid document extension: must be either '.mobi' or '.html'",
            status_code=401)

    return aws.invoke_lambda(
        name=env["LAMBDA_NOTIFICATIONS"],
        payload={
            "mail_to": env["KINDLE_EMAIL"],
            "attachments": [{
                # https://www.iana.org/assignments/media-types/
                "ContentType": "application/vnd.amazon.mobi8-ebook" \
                               if extension == "mobi" else "text/html",
                "Key": key,
                "Bucket": bucket,
                "Filename": f"pocket-{item_id}.{extension}" \
                            if item_id else f"{uuid4()}.{extension}",
            }],
        },
        invoke_type="Event")
Ejemplo n.º 6
0
def _send_email(subject: str, content: str) -> utils.Response:
    utils.Log.info("Send report via %s email notification service",
                   env["LAMBDA_NOTIFICATIONS"])

    return aws.invoke_lambda(name=env["LAMBDA_NOTIFICATIONS"],
                             payload={
                                 "subject": subject,
                                 "text": content,
                             },
                             invoke_type="Event")
Ejemplo n.º 7
0
def contact(event: utils.LambdaEvent) -> str:
    """
    Send event payload to Notifications lambda for delivery.

    Expects these keys in event mapping:

    - source
    - name
    - email
    - description
    """
    lambda_notifications = env["LAMBDA_NOTIFICATIONS"]

    body = event["body"]

    utils.Log.debug("Processing body payload: %s", body)

    try:
        utils.Log.debug("Loading JSON content from body")
        utils.Log.info("json.loads should be safe to use: "
                       "https://stackoverflow.com/a/45483187/2274124")

        msg = """Source: {source}
Name: {name}
Mail: {email}
Desc: {description}
""".format(**json.loads(body))

    except (TypeError, json.JSONDecodeError) as error:
        raise utils.HandledError("JSON body is malformatted: %s" % error)

    except KeyError as error:
        raise utils.HandledError("Missing JSON key: %s" % error)

    utils.Log.debug("### Message content below ###")
    utils.Log.debug(msg)
    utils.Log.debug("#############################")

    return aws.invoke_lambda(
        name=lambda_notifications,
        payload={
            "title": "New /contact submission received",
            "payload": msg,
        }).text
Ejemplo n.º 8
0
def _send_attachment_to_kindle(
        key: str,
        bucket: str,
        item_id: Optional[int] = None) -> utils.Response:
    utils.Log.info("Send attachment to %s via %s email notification service",
                   env["KINDLE_EMAIL"], env["LAMBDA_NOTIFICATIONS"])
    return aws.invoke_lambda(
        name=env["LAMBDA_NOTIFICATIONS"],
        payload={
            "mail_to":
            env["KINDLE_EMAIL"],
            "attachments": [{
                # https://www.iana.org/assignments/media-types/application/vnd.amazon.mobi8-ebook
                "ContentType":
                "application/vnd.amazon.mobi8-ebook",
                "Key":
                key,
                "Bucket":
                bucket,
                "Filename":
                f"pocket-{item_id}.mobi" if item_id else f"{uuid4()}.mobi",
            }],
        },
        invoke_type="Event")