Module framework.cloudhelpers.cloudfunctions

Source code
import os
import shutil
import zipfile
import httplib2
import jinja2

import google.auth
from googleapiclient import discovery


def upload_cloud_function(function_path, location_id, template_args={}):
    '''Uploads the source code of a cloud function and returns the upload url.

    Before a cloud function can be created, the source code must be uploaded to Google Cloud.
    This function will upload the cloud function source code at the specified path and then will return the upload url that will be put into the "source upload url" field when creating the cloud function.

    Parameters:
        function_path (str): The relative path to the function source code, starting with "core/"
        location_id (str): The gcs zone that the function will be uploaded to.
        template_args (dict, optional): Optional dictionary to specify arguments 
            to be used to render jinja templates in cloud function code.
    
    Returns:
        str: The url that the source code has been uploaded to.

            This url should be inputted into the "source upload url" field when creating the cloud function.
            In the cloud_function.jinja template, the property name for the source upload url is "upload_url"
    '''
    temp_func_path = function_path + '-temp'
    zip_path = os.path.dirname(temp_func_path) + '/' + 'function.zip'
    try:
        _create_temp_cf_files(function_path, temp_func_path,
                             template_args=template_args)
        credentials, project_id = google.auth.default()
        # Create zip
        with zipfile.ZipFile(zip_path, 'w') as z:
            for dir_path, subdir_paths, f_names in os.walk(temp_func_path):
                for f in f_names:
                    file_path = dir_path + '/' + f
                    arc_path = file_path.replace(temp_func_path+'/', '')
                    z.write(file_path, arcname=arc_path)
        # Build api object
        cf_api = discovery.build('cloudfunctions',
                                 'v1', credentials=credentials)
        parent = f'projects/{project_id}/locations/{location_id}'
        # Generate upload URL
        upload_url = cf_api.projects().locations().functions(
        ).generateUploadUrl(parent=parent).execute()['uploadUrl']
        # Make Http object
        h = httplib2.Http()
        # Upload to url
        headers = {'Content-Type': 'application/zip',
                   'x-goog-content-length-range': '0,104857600'}
        with open(zip_path, 'rb') as f:
            h.request(upload_url, method='PUT', headers=headers, body=f)
        # Return signed url for creating cloud function
        return upload_url
    finally:
        # Delete zip
        if os.path.exists(zip_path):
            os.remove(zip_path)
        # Delete temp file
        if os.path.exists(temp_func_path):
            shutil.rmtree(temp_func_path)


def _create_temp_cf_files(func_path, temp_func_path, template_args={}):
    # Iterate recursively through all subfiles
    for dir_path, subdir_paths, f_names in os.walk(func_path):
        for f in f_names:
            file_path = dir_path + '/' + f
            temp_path = file_path.replace(func_path, temp_func_path)
            # Read and render function template
            with open(file_path) as f:
                rendered_template = jinja2.Template(
                    f.read()).render(**template_args)
            # If temporary path doesn't exist yet, create the directory structure
            if not os.path.exists(os.path.dirname(temp_path)):
                os.makedirs(os.path.dirname(temp_path))
            # Write to temporary file
            with open(temp_path, 'w+') as f:
                f.write(rendered_template)

Functions

def upload_cloud_function(function_path, location_id, template_args={})

Uploads the source code of a cloud function and returns the upload url.

Before a cloud function can be created, the source code must be uploaded to Google Cloud. This function will upload the cloud function source code at the specified path and then will return the upload url that will be put into the "source upload url" field when creating the cloud function.

Parameters

function_path : str
The relative path to the function source code, starting with "core/"
location_id : str
The gcs zone that the function will be uploaded to.
template_args : dict, optional
Optional dictionary to specify arguments to be used to render jinja templates in cloud function code.

Returns

str

The url that the source code has been uploaded to.

This url should be inputted into the "source upload url" field when creating the cloud function. In the cloud_function.jinja template, the property name for the source upload url is "upload_url"

Source code
def upload_cloud_function(function_path, location_id, template_args={}):
    '''Uploads the source code of a cloud function and returns the upload url.

    Before a cloud function can be created, the source code must be uploaded to Google Cloud.
    This function will upload the cloud function source code at the specified path and then will return the upload url that will be put into the "source upload url" field when creating the cloud function.

    Parameters:
        function_path (str): The relative path to the function source code, starting with "core/"
        location_id (str): The gcs zone that the function will be uploaded to.
        template_args (dict, optional): Optional dictionary to specify arguments 
            to be used to render jinja templates in cloud function code.
    
    Returns:
        str: The url that the source code has been uploaded to.

            This url should be inputted into the "source upload url" field when creating the cloud function.
            In the cloud_function.jinja template, the property name for the source upload url is "upload_url"
    '''
    temp_func_path = function_path + '-temp'
    zip_path = os.path.dirname(temp_func_path) + '/' + 'function.zip'
    try:
        _create_temp_cf_files(function_path, temp_func_path,
                             template_args=template_args)
        credentials, project_id = google.auth.default()
        # Create zip
        with zipfile.ZipFile(zip_path, 'w') as z:
            for dir_path, subdir_paths, f_names in os.walk(temp_func_path):
                for f in f_names:
                    file_path = dir_path + '/' + f
                    arc_path = file_path.replace(temp_func_path+'/', '')
                    z.write(file_path, arcname=arc_path)
        # Build api object
        cf_api = discovery.build('cloudfunctions',
                                 'v1', credentials=credentials)
        parent = f'projects/{project_id}/locations/{location_id}'
        # Generate upload URL
        upload_url = cf_api.projects().locations().functions(
        ).generateUploadUrl(parent=parent).execute()['uploadUrl']
        # Make Http object
        h = httplib2.Http()
        # Upload to url
        headers = {'Content-Type': 'application/zip',
                   'x-goog-content-length-range': '0,104857600'}
        with open(zip_path, 'rb') as f:
            h.request(upload_url, method='PUT', headers=headers, body=f)
        # Return signed url for creating cloud function
        return upload_url
    finally:
        # Delete zip
        if os.path.exists(zip_path):
            os.remove(zip_path)
        # Delete temp file
        if os.path.exists(temp_func_path):
            shutil.rmtree(temp_func_path)