Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ $ curl https://api.uc.gateway.dev/home
* cloudrun jobs
* bq remote functions
* cloudtasktarget
* uptime checks

## Data Typing Frameworks Supported

Expand Down Expand Up @@ -336,7 +337,8 @@ Please file any issues, bugs or feature requests as an issue on our [GitHub](htt
☑ Deploy arbitrary Dockerfile to Cloudrun \
☑ [Multi Container Deployments](https://cloud.google.com/blog/products/serverless/cloud-run-now-supports-multi-container-deployments) \
☑ Create Deployment Service Accounts \
☑ Automatically add IAM invoker bindings on the backend based on deployed handlers
☑ Automatically add IAM invoker bindings on the backend based on deployed handlers \
☑ [Uptime Checks](https://cloud.google.com/monitoring/uptime-checks)


## Want to Contribute
Expand Down
18 changes: 18 additions & 0 deletions docs/source/handlers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,21 @@ Another example using ``app.cloudtaskqueue`` to queue and handle tasks in the sa
payload = {"message": {"title": "enqueue"}}
client.enqueue(target="target", payload=payload)
return {}


Uptime Check
^^^^^^^^^^^^

You can create uptime checks for backends that are public using the `@app.uptime` decorator.
You can customize the check using all available fields found in the `uptime documentation <https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.uptimeCheckConfigs#UptimeCheckConfig>`_

.. code:: python

from goblet import Goblet, goblet_entrypoint

app = Goblet(function_name="uptime_example")
goblet_entrypoint(app)

@app.uptime(name="target")
def example_uptime():
return "success"
7 changes: 6 additions & 1 deletion examples/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,4 +402,9 @@ def handle_missing_route(error):
# Example of handling ValueError.
@app.errorhandler("ValueError")
def return_error_string(error):
return Response(str(error), status_code=200)
return Response(str(error), status_code=200)

# Example uptime check
@app.uptime(timeout="30s")
def uptime_check():
return "success"
2 changes: 1 addition & 1 deletion goblet/__version__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = (0, 12, 4)
VERSION = (0, 12, 5)


__version__ = ".".join(map(str, VERSION))
1 change: 1 addition & 0 deletions goblet/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"pubsub",
"storage",
"schedule",
"uptime",
]
SUPPORTED_INFRASTRUCTURES = [
"alerts",
Expand Down
10 changes: 10 additions & 0 deletions goblet/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"iam": "v1",
"cloudresourcemanager": "v3",
"artifactregistry": "v1",
"monitoring": "v3",
"storage": "v1",
}

Expand Down Expand Up @@ -223,6 +224,15 @@ def monitoring_alert(self):
parent_schema="projects/{project_id}",
)

@property
def monitoring_uptime(self):
return Client(
"monitoring",
self.client_versions.get("monitoring", "v3"),
calls="projects.uptimeCheckConfigs",
parent_schema="projects/{project_id}",
)

@property
def logging_metric(self):
return Client(
Expand Down
9 changes: 9 additions & 0 deletions goblet/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,15 @@ def eventarc(self, topic=None, event_filters=[], **kwargs):
},
)

def uptime(self, **kwargs):
"""Uptime trigger"""
return self._create_registration_function(
handler_type="uptime",
registration_kwargs={
"kwargs": kwargs,
},
)

def http(self, headers={}):
"""Base http trigger"""
return self._create_registration_function(
Expand Down
145 changes: 145 additions & 0 deletions goblet/handlers/uptime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import logging
import os

from goblet.handlers.handler import Handler
from goblet.permissions import gcp_generic_resource_permissions
from goblet.utils import nested_update
from goblet_gcp_client.client import get_default_project, get_default_location
from googleapiclient.errors import HttpError

log = logging.getLogger("goblet.deployer")
log.setLevel(logging.getLevelName(os.getenv("GOBLET_LOG_LEVEL", "INFO")))


class Uptime(Handler):
"""Uptime Trigger
Uptime checks only support public https urls and cloud run revisions currently https://cloud.google.com/monitoring/uptime-checks
"""

resource_type = "uptime"
valid_backends = ["cloudfunction", "cloudfunctionv2", "cloudrun"]
required_apis = ["monitoring"]
permissions = [
*gcp_generic_resource_permissions("monitoring", "uptimeCheckConfigs")
]

def __init__(
self, name, backend, versioned_clients=None, resources=None, routes_type=None
):
super(Uptime, self).__init__(
name=name,
versioned_clients=versioned_clients,
resources=resources,
backend=backend,
)
self.resources = resources or {}
self.routes_type = routes_type

def register(self, name, func, kwargs):
self.resources[name] = {"func": func, "name": name, "kwargs": kwargs["kwargs"]}

def __call__(self, request, context=None):
headers = request.headers or {}
uptime = self.resources[headers["X-Goblet-Uptime-Name"]]
return uptime["func"]()

def _deploy(self, source=None, entrypoint=None):
# TODO: support api gateway

if self.resources:
checks = self.list_uptime_checks()
for name, uptime in self.resources.items():
uptime_config = {"displayName": f"{self.name}-{name}"}
if (
self.backend.resource_type == "cloudrun"
and not self.routes_type == "apigateway"
):
uptime_config["monitoredResource"] = {
"type": "cloud_run_revision",
"labels": {
"location": "us-central1",
"service_name": self.name,
"revision_name": "",
"configuration_name": "",
"project_id": get_default_project(),
},
}
uptime_config["httpCheck"] = {
"useSsl": True,
"headers": {"X-Goblet-Uptime-Name": uptime["name"]},
}
if self.backend.resource_type.startswith("cloudfunction"):
uptime_config["monitoredResource"] = {
"type": "uptime_url",
"labels": {
"host": f"{get_default_location()}-{get_default_project()}.cloudfunctions.net",
"project_id": get_default_project(),
},
}
uptime_config["httpCheck"] = {
"useSsl": True,
"path": f"/{self.name}",
"headers": {"X-Goblet-Uptime-Name": uptime["name"]},
}

# update config based on user arguments
uptime_config = nested_update(uptime_config, uptime["kwargs"])

# Uptime exists
check = [
check
for check in checks
if check["displayName"] == uptime_config["displayName"]
]
if len(check) == 1:
# Setup update mask
keys = list(uptime_config.keys())

# Remove keys that cannot be updated
keys.remove("monitoredResource")
updateMask = ",".join(keys)

self.versioned_clients.monitoring_uptime.execute(
"patch",
parent_key="name",
parent_schema=check[0]["name"],
params={"body": uptime_config, "updateMask": updateMask},
)
log.info(f"updated uptime check: {name} for {self.name}")

else:
self.versioned_clients.monitoring_uptime.execute(
"create", params={"body": uptime_config}
)
log.info(f"created uptime check: {name} for {self.name}")

return

def destroy(self):
if not self.resources:
return
for check in self.list_uptime_checks():
self._destroy_uptime_check(check)

def _destroy_uptime_check(self, check):
try:
self.versioned_clients.monitoring_uptime.execute(
"delete", parent_key="name", parent_schema=check["name"]
)
log.info(f"Destroying uptime check {check['displayName']}......")
except HttpError as e:
if e.resp.status == 404:
log.info("Uptime check already destroyed")
else:
raise e

def list_uptime_checks(self):
resp = self.versioned_clients.monitoring_uptime.execute(
"list",
parent_key="parent",
params={"filter": f"displayName=starts_with('{self.name}-')"},
)
return resp.get("uptimeCheckConfigs", [])

def set_invoker_permissions(self):
return
24 changes: 24 additions & 0 deletions goblet/infrastructures/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,27 @@ def __init__(self, name, subscription_id, value=0, **kwargs) -> None:
),
**kwargs,
)


class UptimeCondition(MetricCondition):
"""
Creates and deploys an alert for failed uptime checks.
Supports `uptime_url` or `cloud_run_revision`
"""

def __init__(self, name, check_id, resource_type, value=1, **kwargs) -> None:
super().__init__(
name=name,
metric="monitoring.googleapis.com/uptime_check/check_passed",
value=value,
filter='metric.labels.check_id = "{check_id}" AND resource.type = "{resource_type}" AND metric.type = "monitoring.googleapis.com/uptime_check/check_passed"'.format(
check_id=check_id, resource_type=resource_type
),
aggregations=[
{
"alignmentPeriod": "1200s",
"crossSeriesReducer": "REDUCE_COUNT_FALSE",
}
],
**kwargs,
)
8 changes: 8 additions & 0 deletions goblet/resource_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from goblet.handlers.storage import Storage
from goblet.handlers.http import HTTP
from goblet.handlers.jobs import Jobs
from goblet.handlers.uptime import Uptime

from goblet.infrastructures.redis import Redis
from goblet.infrastructures.vpcconnector import VPCConnector
Expand Down Expand Up @@ -51,6 +52,7 @@
"job",
"bqremotefunction",
"cloudtasktarget",
"uptime",
]

SUPPORTED_BACKENDS = {
Expand Down Expand Up @@ -95,6 +97,7 @@ def __init__(
"jobs": Jobs(function_name, backend=backend),
"schedule": Scheduler(function_name, backend=backend),
"bqremotefunction": BigQueryRemoteFunction(function_name, backend=backend),
"uptime": Uptime(function_name, backend=backend, routes_type=routes_type),
}

self.infrastructure = {
Expand Down Expand Up @@ -170,6 +173,8 @@ def __call__(self, request, context=None):
response = self.handlers["bqremotefunction"](request)
if event_type == "cloudtasktarget":
response = self.handlers["cloudtasktarget"](request)
if event_type == "uptime":
response = self.handlers["uptime"](request)

# call after request middleware
response = self._call_middleware(
Expand Down Expand Up @@ -200,6 +205,8 @@ def get_event_type(self, request, context=None):
return context.event_type.split(".")[1].split("/")[0]
if request.headers.get("X-Goblet-Type") == "schedule":
return "schedule"
if request.headers.get("X-Goblet-Uptime-Name"):
return "uptime"
if request.headers.get("User-Agent") == "Google-Cloud-Tasks":
return "cloudtasktarget"
if request.headers.get("Ce-Type") and request.headers.get("Ce-Source"):
Expand Down Expand Up @@ -356,6 +363,7 @@ def is_http(self):
or self.handlers["pubsub"].is_http()
or len(self.handlers["bqremotefunction"].resources) > 0
or len(self.handlers["cloudtasktarget"].resources) > 0
or len(self.handlers["uptime"].resources) > 0
):
return True
return False
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"headers": {},
"body": {
"totalSize": 3
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"headers": {},
"body": {
"name": "projects/goblet/uptimeCheckConfigs/test-uptime-cloudrun-eR4tvksJ3ss",
"displayName": "test-uptime-cloudrun",
"monitoredResource": {
"type": "cloud_run_revision",
"labels": {
"project_id": "goblet",
"revision_name": "",
"location": "us-central1",
"service_name": "test-uptime",
"configuration_name": ""
}
},
"httpCheck": {
"useSsl": true,
"path": "/",
"port": 443,
"headers": {
"X-Goblet-Uptime-Name": "cloudrun"
},
"requestMethod": "GET"
},
"period": "60s",
"timeout": "10s",
"checkerType": "STATIC_IP_CHECKERS"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"headers": {},
"body": {
"totalSize": 3
}
}
Loading