PK!I^^%snakeless_provider_gcloud/__init__.pyfrom .provider import GCloudProvider from .schema import GCLOUD_SCHEMA __version__ = "0.2.0" PK!o(&snakeless_provider_gcloud/constants.pyEVENTS_MAPPING = { "http": { "trigger_id": "httpsTrigger", "fields": { "path": "url" }, "additional_fields": {} }, "pubsub": { "trigger_id": "eventTrigger", "fields": { "topic": "resource" }, "additional_fields": { "eventType": "providers/cloud.pubsub/eventTypes/topic.publish", } } }PK!Os%snakeless_provider_gcloud/provider.pyimport json import logging import requests from functools import partial import fs from fs.copy import copy_fs from google.oauth2 import service_account from google.auth.transport.requests import AuthorizedSession from snakeless.providers.base import BaseProvider from snakeless.exceptions import CommandFailure from .constants import EVENTS_MAPPING from .utils import event_to_mapper logger = logging.getLogger(__name__) logging.getLogger("google").setLevel(logging.WARNING) logging.getLogger("requests").setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING) class GCloudApi(object): domain = "https://cloudfunctions.googleapis.com" create_function = { "path": "/v1/projects/{project_id}/locations/{location_id}/functions", "default_method": "POST", } update_function = { "path": "/v1/projects/{project_id}/locations/{location_id}/functions/{function_id}", # noqa "default_method": "PATCH", } upload_code = { "path": ( "/v1/projects/{project_id}/locations/{location_id}/functions:generateUploadUrl" # noqa ), "default_method": "POST", } def call( self, endpoint, path_kwargs={}, session=requests.Session(), **kwargs ): method = kwargs.pop("method", endpoint.get("default_method", "GET")) endpoint_type = endpoint.get("response_type", "json") url = self.domain + endpoint["path"].format(**path_kwargs) r = session.request(method, url, **kwargs) if endpoint_type == "json": return r.json() return r.text class GCloudProvider(BaseProvider): def __init__(self, config): self.config = config service_account_info = None with fs.open_fs(".") as root_fs: service_account_info = json.loads( root_fs.gettext(self.config["project"]["creds"]) ) credentials = service_account.Credentials.from_service_account_info( service_account_info, scopes=["https://www.googleapis.com/auth/cloud-platform"], ) self.session = AuthorizedSession(credentials) self.api = GCloudApi() def add_triggers(self, func_name, resource): events = self.get_func_data(func_name, "events", []) for event in events: trigger_id = list(event.keys())[0] trigger_data = event[trigger_id] event_mapping = EVENTS_MAPPING[trigger_id] mapped_event = event_to_mapper(trigger_data, event_mapping) resource[event_mapping.id] = mapped_event return resource def generate_resource_function(self, func_name): get_data = partial(self.get_func_or_project_data, func_name) get_func_data = partial(self.get_func_data, func_name) project_id = self.get_project_data("name") location_id = get_data("region") deploy_func_name = get_func_data( "deploy_name", get_func_data("handler") ) env_variables = self.get_env_variables(func_name) source_url = self.upload_func(func_name) resource_function = { "name": "projects/{0}/locations/{1}/functions/{2}".format( project_id, location_id, deploy_func_name ), "description": get_func_data("description", ""), "entryPoint": get_func_data("handler"), "runtime": get_data("runtime"), "timeout": str(get_data("timeout")) + "s", "availableMemoryMb": get_data("memory_size"), "labels": get_func_data("tags", {}), "environmentVariables": env_variables, "sourceUploadUrl": source_url, } return self.add_triggers(func_name, resource_function) def package_code(self, config, func_name): handler_path = self.config["functions"][func_name]["handler_path"] with fs.open_fs( "./.snakeless/", create=True, writeable=True ) as root_fs: copy_fs(f"{handler_path}", f"zip://.snakeless/{func_name}.zip") return root_fs.getbytes(f"{func_name}.zip") def upload_func(self, func_name): packaged_code = self.package_code(self.config, func_name) response = self.api.call( self.api.upload_code, session=self.session, path_kwargs={ "project_id": self.config["project"]["name"], "location_id": ( self.get_func_data(func_name, "region") or self.config["project"]["region"] ), }, ) upload_url = response["uploadUrl"] r = requests.put( upload_url, headers={ "content-type": "application/zip", "x-goog-content-length-range": "0,104857600", }, data=packaged_code, ) r.raise_for_status() return upload_url def deploy_function( self, func_name, resource_function=None, only_update=False ): resource_function = ( resource_function if resource_function else self.generate_resource_function(func_name) ) deploy_func_name = self.get_func_data( func_name, "deploy_name", self.get_func_data(func_name, "handler") ) response = self.api.call( ( self.api.update_function if only_update else self.api.create_function ), session=self.session, json=resource_function, path_kwargs={ "project_id": self.config["project"]["name"], "location_id": ( self.get_func_data(func_name, "region") or self.config["project"]["region"] ), "function_id": deploy_func_name }, ) if response.get("error"): if response["error"]["status"] == "ALREADY_EXISTS": return self.deploy_function( func_name, only_update=True, resource_function=resource_function, ) logger.error(response) raise CommandFailure("Function deployment has failed") PK!%+ #snakeless_provider_gcloud/schema.pyfrom schema import And, Use, Optional SUPPORTED_RUNTIMES = ["python37"] SUPPORTED_MEMORY_SIZES = [128, 256, 512, 1024, 2048] GCLOUD_SCHEMA = { "project": { "creds": str, "runtime": And( Use(str), lambda runtime: runtime in SUPPORTED_RUNTIMES, error="Unsupported runtime", ), "region": str, Optional("api_prefix"): str, Optional("memory_size", default=128): And( Use(int), lambda memory_size: memory_size in SUPPORTED_MEMORY_SIZES, error="Invalid memory size", ), Optional("timeout", default=60): And( Use(int), lambda timeout: timeout > 0 ), # Optional("deployment_bucket"): str, Optional("env_file_path", default=".env"): str, }, # Optional("package"): { # Optional("include"): [str], # Optional("exclude"): [str], # Optional("exclude_dev_dependencies", default=True): bool, # Optional("individually", default=False): bool, # }, "functions": { str: { "handler": str, "handler_path": str, "events": [ { Optional("http"): { "path": str } }, { Optional("pubsub"): { "topic": str } } ], Optional("name"): str, Optional("description"): str, Optional("memory_size", default=128): And( Use(int), lambda memory_size: memory_size in SUPPORTED_MEMORY_SIZES, error="Invalid memory size", ), Optional("runtime"): And( Use(str), lambda runtime: runtime in SUPPORTED_RUNTIMES, error="Unsupported runtime", ), Optional("timeout", default=60): And( Use(int), lambda timeout: timeout > 0 ), Optional("env_file_path", default=""): str, Optional("merge_env", default=False): bool, Optional("tags"): {str: str}, # Optional("package"): { # Optional("include"): [str], # Optional("exclude"): [str], # Optional("individually", default=False): bool, # }, } }, } PK!  "snakeless_provider_gcloud/utils.pydef event_to_mapper(event, mapper): fields = mapper["fields"] mapped_event = dict() for field in event: mapped_event[ mapper[field] ] = event[field] mapped_event.update(mapper["additional_fields"]) return mapped_eventPK!HH:snakeless_provider_gcloud-0.2.0.dist-info/entry_points.txt.KNI-.+(/LI-*JO/M!2V **089#578C|C]⃝=\}PK!H\TT/snakeless_provider_gcloud-0.2.0.dist-info/WHEEL 1 0 нR \I$ơ7.ZON `h6oi14m,b4>4ɛpK>X;baP>PK!Hxs(2snakeless_provider_gcloud-0.2.0.dist-info/METADATAPMO0 W4)&&bϳz!Z$-N ąn1A 3hSFDfǿinP}5lT,nKU0m».]K)&H'1 PK!H"C0snakeless_provider_gcloud-0.2.0.dist-info/RECORD˒0}?KnbMQ JшpI~jVY5V2sN-/u[3ȡj?w %E?v.EEz+U'm{RuG:e7,^8-.o';kQu3zvN2ߨo6*|9K/WI[ Dʬ< .IP{*Țe:5Q3b~?J`'e$#8vKd橩SBc(%OOlsYPK!I^^%snakeless_provider_gcloud/__init__.pyPK!o(&snakeless_provider_gcloud/constants.pyPK!Os%}snakeless_provider_gcloud/provider.pyPK!%+ #snakeless_provider_gcloud/schema.pyPK!  "Z%snakeless_provider_gcloud/utils.pyPK!HH:&snakeless_provider_gcloud-0.2.0.dist-info/entry_points.txtPK!H\TT/C'snakeless_provider_gcloud-0.2.0.dist-info/WHEELPK!Hxs(2'snakeless_provider_gcloud-0.2.0.dist-info/METADATAPK!H"C0E)snakeless_provider_gcloud-0.2.0.dist-info/RECORDPK Z+