diff --git a/.github/workflows/publishGitHubPages.yml b/.github/workflows/publishGitHubPages.yml new file mode 100644 index 0000000..549e0fa --- /dev/null +++ b/.github/workflows/publishGitHubPages.yml @@ -0,0 +1,77 @@ +name: Publish GitHub Pages + +on: + pull_request: + branches: + - main + + push: + branches: + - main + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r doc/requirements.txt + + - name: get git info + id: getgitinfo + run: | + echo "GITHUB_COMMIT_DATE=$(git --no-pager log -1 --pretty='format:%cd' --date='format:%Y-%m-%d %H:%M:%S')" >> "$GITHUB_OUTPUT" + + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + + - name: build Documentation + env: + GH_ACTIONS_GIT_BRANCH: ${{ github.head_ref || github.ref_name }} + GH_ACTIONS_GIT_COMMIT_DATE: ${{ steps.getgitinfo.outputs.GITHUB_COMMIT_DATE }} + GH_ACTIONS_GIT_COMMIT_HASH: ${{ github.sha }} + + run: | + sphinx-build -W --keep-going -b html doc/source/ ./_site + + - name: Upload artifact + # Automatically uploads an artifact from the './_site' directory by default + uses: actions/upload-pages-artifact@v3 + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30c0a0c --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.venv +target +__pycache__ +**/.terraform/** +*.zip +.terraform +.terraform.* +terraform.* +.tox +doc/build diff --git a/README.md b/README.md new file mode 100644 index 0000000..72f221d --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# FunctionGraph example fg-http-python-fastapi + +Sample on how to create FunctionGraph HTTP function using python and [FastAPI framework](https://fastapi.tiangolo.com) + +> [!WARNING] +> ############################################### +> Work in progress.... +> ############################################### + + +## Documentation + +For documentation see: [otc-java-sdk-v1](https://opentelekomcloud-community.github.io/fg-http-python-fastapi/) + +>[!NOTE] +> To run documentation locally: +> +> Install ``tox`` as described in: [tox installation guide ](https://tox.wiki/en/4.26.0/installation.html) +> +> and run: +> +> ```bash +> tox -e docs-auto +> ``` +> Open documentation in browser: + diff --git a/createZip.py b/createZip.py new file mode 100644 index 0000000..f87d98a --- /dev/null +++ b/createZip.py @@ -0,0 +1,57 @@ +#!.venv/bin/python3 + +import os +import subprocess + +from zipfile import ZIP_DEFLATED, ZipFile + +#################################################################### +# Zip Code and dependencies +#################################################################### +def createZippedFunctionCode(packDependencies: bool): + # create temp venv, installdependencies + if packDependencies: + subprocess.run(["./packDependencies.sh", ""], shell=True) + + # zip_buffer = io.BytesIO() + + with ZipFile("./code.zip", "w", ZIP_DEFLATED) as zip: + # add files from folder src + src = "./src" + + for dirname, subdirs, files in os.walk(src): + for filename in files: + absname = os.path.abspath(os.path.join(dirname, filename)) + arcname = absname[absname.rindex("/src/") + len("/src/") :] + #print("zipping %s as %s" % (os.path.join(dirname, filename), arcname)) + if ( + "__pycache__" not in absname + and "__pycache__" not in arcname + ): + #print("zipping %s as %s" % (os.path.join(dirname, filename), arcname)) + zip.write(absname, arcname) + + # add files from dependencies + src = "./target/dependenciesVenv/lib/python3.10/site-packages" + + for dirname, subdirs, files in os.walk(src): + for filename in files: + absname = os.path.abspath(os.path.join(dirname, filename)) + arcname = absname[ + absname.rindex("/site-packages/") + len("/site-packages/") : + ] + if ( + not arcname.startswith("pip") + and not arcname.startswith("_distutils") + and not arcname.startswith("setuptools") + and "__pycache__" not in absname + and "__pycache__" not in arcname + ): + #print("zipping %s as %s" % (os.path.join(dirname, filename), arcname)) + zip.write(absname, arcname) + + print("created: code.zip") + + +if __name__ == "__main__": + createZippedFunctionCode(True) diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 0000000..bec116f --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,23 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +docutils>=0.11 # OSI-Approved Open Source, Public Domain +beautifulsoup4>=4.6.0 # MIT +reno>=4.1.0 # Apache-2.0 +otcdocstheme>=1.13.2 # Apache-2.0 +otc-api-ref>=0.1.0 # Apache-2.0 +sphinx>=2.0.0,!=2.1.0 # BSD +sphinxcontrib-apidoc>=0.2.0 # BSD +# cliff!=2.9.0,>=2.8.0 # Apache-2.0 +oslo.i18n>=3.15.3 # Apache-2.0 +osc-lib>=1.10.0 # Apache-2.0 +doc8 +rstcheck +javasphinx +sphinx-tabs +sphinx-copybutton +sphinx-design +Sphinx-Substitution-Extensions +gitpython +sphinx-autobuild +git+https://gitea.eco.tsi-dev.otc-service.com/infra/otc-metadata-rework.git#egg=otc_metadata \ No newline at end of file diff --git a/doc/setup.cfg b/doc/setup.cfg new file mode 100644 index 0000000..070a5de --- /dev/null +++ b/doc/setup.cfg @@ -0,0 +1,6 @@ +[rstcheck] +ignore_roles=github_repo_master + +[doc8] +ignore = ["D001"] +allow-long-titles = true diff --git a/doc/source/_static/css/custom.css b/doc/source/_static/css/custom.css new file mode 100644 index 0000000..71eba26 --- /dev/null +++ b/doc/source/_static/css/custom.css @@ -0,0 +1,34 @@ +/* START: Copy to Clipboard */ + +button.copybtn { + webkit-transition: opacity .3s ease-in-out; + -o-transition: opacity .3s ease-in-out; + transition: opacity .3s ease-in-out; + opacity: 0; + padding: 0px 6px; + position: absolute; + right: 4px; + top: 4px; +} +div.highlight:hover .copybtn, div.highlight .copybtn:focus { + opacity: .5; +} +div.highlight .copybtn:hover { + opacity: 1; + color: var(--telekom-color-primary-standard); +} + +div.highlight .btn:focus { + box-shadow: 0 0 0 .25rem rgba(226, 0, 116,.25); +} + +div.highlight { + position: relative; +} + +.fa-solid, .fas { + font-family: "Font Awesome 6 Free"; + font-weight: 900; +} + +/* END: Copy to Clipboard */ \ No newline at end of file diff --git a/doc/source/_templates/sidebartoc.html b/doc/source/_templates/sidebartoc.html new file mode 100644 index 0000000..eaa4385 --- /dev/null +++ b/doc/source/_templates/sidebartoc.html @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 0000000..21c2ddf --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,252 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import warnings +from git import Repo +from datetime import datetime + +# -- General configuration ---------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + 'sphinx.ext.autodoc', + 'otcdocstheme', +# 'cliff.sphinxext', + 'sphinx_tabs.tabs', + 'sphinx_copybutton', + 'sphinx_design', + 'sphinx_substitution_extensions', + 'sphinx.ext.extlinks', + "sphinx.ext.githubpages", + +] + +# openstackdocstheme options +otcdocs_repo_name = 'opentelekomcloud-community/fg-http-python-fastapi' +otcdocs_git_type = 'github' + +# Those variables are required for edit/bug links +otcdocs_edit_enabled = False +otcdocs_bug_report_enabled = False + +otcdocs_pdf_link = False + +# Analytics app name +otcdocs_analytics_app = 'fg-http-python-fastapi' + +# Those variables are needed for indexing into OpenSearch +otcdocs_doc_environment = 'public' +otcdocs_doc_link = '/' +otcdocs_doc_title = 'Example: FunctionGraph HTTP function using python and FastAPI' +otcdocs_doc_type = 'dev' +otcdocs_service_category = 'example' +otcdocs_service_title = 'fg-http-python-fastapi' +otcdocs_service_type = 'sdk' +otcdocs_search_environment = 'hc_de' +otcdocs_search_url = "https://opensearch.eco.tsi-dev.otc-service.com/" + + +otcdocs_projects = ["fg-http-python-fastapi"] + +otcdocs_auto_name = False +otcdocs_auto_version = False + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('../../')) +sys.path.insert(0, os.path.abspath('../')) +sys.path.insert(0, os.path.abspath('./')) +sys.path.insert(0, os.path.abspath('..')) + + +# TODO(shade) Set this to true once the build-openstack-sphinx-docs job is +# updated to use sphinx-build. +# When True, this will raise an exception that kills sphinx-build. +enforcer_warnings_as_errors = False + +# autodoc generation is a bit aggressive and a nuisance when doing heavy +# text edit cycles. +# execute "export SPHINX_DEBUG=1" in your terminal to disable + +# The suffix of source filenames. +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +current_year=datetime.now().year +project = u'fg-http-python-fastapi' +copyright = f'{current_year}, OpenTelekomCloud' +author = u'OpenTelekomCloud' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +language = 'en' + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = False + +# Get the Git commit values for last updated timestamp on each page +# Environment for GitHub actions +local_branch = "" +if os.environ.get("GH_ACTIONS_GIT_BRANCH") is not None: + local_branch = os.environ.get("GH_ACTIONS_GIT_BRANCH") + + current_commit_hash = os.environ.get("GH_ACTIONS_GIT_COMMIT_HASH","unknown") + current_commit_time = os.environ.get("GH_ACTIONS_GIT_COMMIT_DATE","unknown") +else: + repo = Repo(search_parent_directories=True) + commit = repo.head.commit + current_commit_hash = commit.hexsha + current_commit_time = commit.committed_datetime.strftime('%Y-%m-%d %H:%M:%S') + + local_branch = repo.active_branch.name + +# A few variables have to be set for the log-a-bug feature. +# gitsha: The SHA checksum of the bug description. Extracted from git log. +# bug_tag: Tag for categorizing the bug. Must be set manually. +# bug_project: Launchpad project to file bugs against. +# These variables are passed to the logabug code via html_context. +git_cmd = "/usr/bin/git log | head -n1 | cut -f2 -d' '" +try: + gitsha = os.popen(git_cmd).read().strip('\n') +except Exception: + warnings.warn("Can not get git sha.") + gitsha = "unknown" + +otcdocs_bug_tag = "docs" +pwd = os.getcwd() +# html_context allows us to pass arbitrary values into the html template +html_context = {"pwd": pwd, + "gitsha": gitsha} + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +add_module_names = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'native' + +autodoc_member_order = "bysource" + +# Locations to exclude when looking for source files. +exclude_patterns = [] + +# -- Options for HTML output -------------------------------------------------- + +html_last_updated_fmt = '%Y-%m-%d %H:%M' + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +# html_theme_path = ["."] +html_theme = 'otcdocs' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +html_theme_options = { + "show_other_versions": "True", + "logo_url": "https://docs.otc.t-systems.com", + "disable_search": "True", + "disable_global_nav": "True" +} + +html_title = "OTC Java SDK v1" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# These paths are either relative to html_static_path +# or fully qualified paths (eg. https://...) +html_css_files = [ + 'css/custom.css', +] + +templates_path = ['_templates'] + +# Do not include sources into the rendered results +html_copy_source = False + +# -- Options for HTML output ---------------------------------------------- + +# Don't let openstackdocstheme insert TOCs automatically. +theme_include_auto_toc = False + +# Output file base name for HTML help builder. +htmlhelp_basename = '%sdoc' % project + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto/manual]). +latex_documents = [ + ('index', + '%s.tex' % project, + u'%s Documentation' % project, + u'OpenTelekomCloud', 'manual'), +] + +# Include both the class and __init__ docstrings when describing the class +autoclass_content = "both" + +# -- Options for cliff.sphinxext plugin --------------------------------------- + +# autoprogram_cliff_application = 'openstack' + +# autoprogram_cliff_ignored = [ +# '--help', '--format', '--column', '--max-width', '--fit-width', +# '--print-empty', '--prefix', '--noindent', '--quote'] + + +# -- Options sphinx-tabs ------------------------- +sphinx_tabs_valid_builders = ['linkcheck'] + + +rst_prolog = f""" +.. |github_repo| replace:: "https://github.com/opentelekomcloud-community/fg-http-python-fastapi.git" +""" +# version = "1.0.0" +# release = "1.0.0" + +# -- Options for extlinks --------------------------------------- + +# https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html +extlinks= { + "github_repo_master": (f'https://github.com/opentelekomcloud-community/fg-http-python-fastapi/tree/{local_branch}/%s', "%s"), + "api_usage": ('https://docs.otc.t-systems.com/api-usage/%s', "%s") +} + +role_name = 'github_repo_master' + +extlinks_detect_hardcoded_links = True + +# Copybutton and otc theme button are not compatible. +# to, use custom selector: +copybutton_selector = "div.copy-button pre" +# do not copy line numbers and prompt character +copybutton_exclude = '.linenos, .gp' + + + +TEST_ROOT="/fg-http-python-fastapi/index.html" \ No newline at end of file diff --git a/doc/source/deploy/index.rst b/doc/source/deploy/index.rst new file mode 100644 index 0000000..6000af4 --- /dev/null +++ b/doc/source/deploy/index.rst @@ -0,0 +1,139 @@ +Deploy using Terraform +====================== + +.. toctree:: + :maxdepth: 3 + +Prerequisites +------------- + +Install terraform +^^^^^^^^^^^^^^^^^^^^^^^^ + +To install Terraform cli, see `Install Terraform <(https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli>`_ + +Adapt environment variables used in provider.tf +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: Environment variables + :widths: 20 20 25 + :header-rows: 1 + + * - Name + - Value + - Remark + + * - TF_VAR_OTC_SDK_AK + - + - see: :api_usage:`Generating an AK and SK` in API usage guide. + + * - TF_VAR_OTC_SDK_SK + - + - see: :api_usage:`Generating an AK and SK` in API usage guide. + + * - TF_VAR_OTC_SDK_DOMAIN_NAME + - + - Domain Name, eg. OTC-EU-DE-000000000010000XXXXX + + * - TF_VAR_OTC_SDK_PROJECTID + - + - Project Id + + * - TF_VAR_OTC_SDK_PROJECTNAME + - + - Project Name, eg. eu-de_MYPROJECT + + * - TF_VAR_OTC_SDK_REGION + - + - Region Name, eg. "eu-de", for available regions, see: + `Regions `_. + + * - AWS_ACCESS_KEY_ID + - + - same as TF_VAR_OTC_SDK_AK + + * - AWS_SECRET_ACCESS_KEY + - + - same as TF_VAR_OTC_SDK_SK + + +Create OBS Bucket for .tfstate files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Terraform must store state about your managed infrastructure and configuration. + +This state is used by Terraform to map real world resources to your configuration, +keep track of metadata, and to improve performance for large infrastructures. + +See: `Terraform state `_ + +Create OBS bucket for terraform state file either: + +* using OpenTelekomCloud OBS console with bucket name as defined in ``provider.tf`` file for ``bucket``. + +* using the OpenTelekomCloud CLI with command [s3cmd](https://github.com/opentelekomcloud/obs-s3/blob/master/s3cmd/README.md) + + .. code-block:: bash + + s3cmd \ + --access_key=${AWS_ACCESS_KEY_ID} \ + --secret_key=${AWS_SECRET_ACCESS_KEY} \ + --no-ssl \ + mb s3:// + + +Adapt provider.tf file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Check and adapt following values in [provider.tf](./terraform/provider.tf) (see comments there). + +* backend.s3.endpoints +* backend.s3.bucket +* backend.s3.key +* backend.s3.region + + +Adapt apigw.tf file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Adapt apigw.tf according to your needs (e.g. IP addresses). + +Deploy +------ + +In folder ``terraform`` + +.. code-block:: bash + + # change folder + cd terraform + + # initialize provider + terraform init + + # plan changes + terraform plan + + # apply changes + terraform apply + + # destroy all deployed resources on OpenTelekomCloud + terraform destroy + + +Terraform apply script will output the URLs to test endpoints +(APIGWGROUPID and REGION will be replaced with real values): + +.. code-block:: bash + + URL_Root = "https://APIGWGROUPID.apic.REGION.otc.t-systems.com/" + + URL_APIItems = "https://APIGWGROUPID.apic.REGION.otc.t-systems.com/api/items/100?q=100" + + URL_docs = "https://APIGWGROUPID.apic.REGION.otc.t-systems.com/docs" + + URL_redocs = "https://APIGWGROUPID.apic.REGION.otc.t-systems.com/redoc" + + URL_openapi = "https://APIGWGROUPID.apic.REGION.otc.t-systems.com/openapi.json" + + diff --git a/doc/source/docutils.conf b/doc/source/docutils.conf new file mode 100644 index 0000000..7cbe4c1 --- /dev/null +++ b/doc/source/docutils.conf @@ -0,0 +1,2 @@ +[html writers] +table-style: table, caption-top \ No newline at end of file diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 0000000..aa04f90 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,32 @@ +FunctionGraph example fg-http-python-fastapi +============================================ + +.. toctree:: + :maxdepth: 3 + +Sample on how to create FunctionGraph HTTP function using +python and `FastAPI framework `_ + +.. toctree:: + :includehidden: + :maxdepth: 1 + + Setup + Deploy + +Source Code +----------- + +For source code, see :github_repo_master:`fg-http-python-fastapi on Github <>`. + + +Disclaimer +---------- + +Warranty Disclaimer +^^^^^^^^^^^^^^^^^^^ +THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT +WILL BE USEFUL,BUT WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY +OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +SEE THE APPLICABLE LICENSES FOR MORE DETAILS. diff --git a/doc/source/setup/index.rst b/doc/source/setup/index.rst new file mode 100644 index 0000000..165863b --- /dev/null +++ b/doc/source/setup/index.rst @@ -0,0 +1,55 @@ +Setup and Deploy +================ + +.. toctree:: + :maxdepth: 3 + +Prerequisites +------------- + +Python 3.10 installed +^^^^^^^^^^^^^^^^^^^^^^ + +See `python.org `_ on how to install python. + +Python virtual environment venv +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Create and activate a python virtual environment like: + +.. code-block:: bash + + # Create python env + python3 -m venv .venv + + # activate venv + source .venv/bin/activate + + # install requirements + pip install -r requirements.txt + + +Test local +---------- + +.. code-block:: bash + + # change folder + cd src + + # start program + python3 app.py + + +Open browser with http://localhost:8000/ + +Further endpoints: + +* http://localhost:8000/openapi.json + +* http://localhost:8000/docs + +* http://localhost:8000/redoc + +* http://localhost:8000/api/items/100?q=100 + diff --git a/packDependencies.sh b/packDependencies.sh new file mode 100755 index 0000000..220775e --- /dev/null +++ b/packDependencies.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +##################################################### +# script to pack dependencies from requirements.txt +##################################################### + +echo "######################################## Pack dependencies" +# get current venv +CURRENT_VENV=$VIRTUAL_ENV + +TARGET_PATH="target" + +rm -r ./${TARGET_PATH} + +mkdir ./${TARGET_PATH} + +VENV_PATH="./${TARGET_PATH}/dependenciesVenv" + +# create new venv +python3 -m venv ${VENV_PATH} +source ${VENV_PATH}/bin/activate + +# install only requirements from requirements.txt +python3 -m pip install -r requirements.txt + +# deactivate venv +deactivate + +# pack dependencies +(cd ${VENV_PATH}/lib/python3.10/site-packages && zip -rq "$OLDPWD/${TARGET_PATH}/dependencies.zip" .) + +# reactivate old venv +source ${CURRENT_VENV}/bin/activate diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0ef8d2b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +fastapi[standard] +uvicorn >= 0.34.3 +requests +asgi-correlation-id >= 4.3.4 \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/api/__init__.py b/src/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/api/items.py b/src/api/items.py new file mode 100644 index 0000000..5d740a1 --- /dev/null +++ b/src/api/items.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter +from typing import Union + +items_router = APIRouter() + +@items_router.get("/items/{item_id}") +def read_item(item_id: int, q: Union[str, None] = None): + return {"item_id": item_id, "q": q} \ No newline at end of file diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..d316c74 --- /dev/null +++ b/src/app.py @@ -0,0 +1,174 @@ +from contextlib import asynccontextmanager +from fastapi import FastAPI +from fastapi.responses import FileResponse + +from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html +from fastapi.middleware.cors import CORSMiddleware + +import logging +import logging.config +from requests import Request +from api.items import items_router +from asgi_correlation_id import CorrelationIdMiddleware, correlation_id +import time + +from fastapi import HTTPException +from fastapi.exception_handlers import http_exception_handler +from fastapi.responses import JSONResponse + +import os +import traceback + +ROOT_LEVEL = "DEBUG" + +LOGGING_CONFIG = { + "version": 1, + "disable_existing_loggers": True, + "filters": { + "correlation_id": { + "()": "asgi_correlation_id.CorrelationIdFilter", + "uuid_length": 36, + "default_value": "-", + }, + }, + "formatters": { + "standard": { + "format": "%(asctime)s [%(levelname)s] [%(correlation_id)s] %(name)s: %(message)s" + }, + }, + "handlers": { + "default": { + "level": "INFO", + "formatter": "standard", + "class": "logging.StreamHandler", + "filters": ["correlation_id"], + "stream": "ext://sys.stdout", # Default is stderr + }, + }, + "loggers": { + "": { # root logger + "level": ROOT_LEVEL, # "INFO", + "handlers": ["default"], + "propagate": False, + }, + "uvicorn.error": { + "level": "DEBUG", + "handlers": ["default"], + }, + "uvicorn.access": { + "level": "DEBUG", + "handlers": ["default"], + }, + }, +} + +logger = logging.getLogger(__name__) + +tags_metadata = [ + {"name": "API", "description": "API samples"}, +] + + +# see: https://fastapi.tiangolo.com/advanced/events/?h=async+conte#lifespan +@asynccontextmanager +async def lifespan(app: FastAPI): + # startup + logging.config.dictConfig(LOGGING_CONFIG) + yield + # cleanup + + +app = FastAPI( + lifespan=lifespan, + title="FunctionGraph and FastAPI", + summary="OpenTelekomCloud sample for FunctionGraph HTTP function using FastAPI", + version="0.0.1", + debug=True, + docs_url=None, # will be overwritten, see below: overridden_swagger() + redoc_url=None, # will be overwritten, see below: overridden_redoc() + openapi_tags=tags_metadata, +) + + +@app.get("/docs", include_in_schema=False) +def overridden_swagger(): + return get_swagger_ui_html( + openapi_url=app.openapi_url, title=app.title, swagger_favicon_url="/favicon.ico" + ) + + +@app.get("/redoc", include_in_schema=False) +def overridden_redoc(): + return get_redoc_html( + openapi_url=app.openapi_url, title=app.title, redoc_favicon_url="/favicon.ico" + ) + + +@app.exception_handler(Exception) +async def unhandled_exception_handler(request: Request, exc: Exception) -> JSONResponse: + logger.error(traceback.format_exc()) + + return await http_exception_handler( + request, + HTTPException( + 500, + "Internal server error", + headers={"x-cff-request-id": correlation_id.get() or ""}, + ), + ) + + +@app.middleware("http") +async def add_process_time_header(request: Request, call_next): + start_time = time.time() + response = await call_next(request) + process_time = time.time() - start_time + response.headers["X-Process-Time"] = str(process_time) + return response + + +# add requestid to logging, see: https://github.com/snok/asgi-correlation-id +app.add_middleware(CorrelationIdMiddleware, header_name="x-cff-request-id") + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=[ + "X-Requested-With", + "X-Request-ID", + "x-cff-request-id", + "Access-Control-Allow-Origin", + "Access-Control-Expose-Headers", + ], + expose_headers=[ + "X-Request-ID", + "x-cff-request-id", + "Access-Control-Allow-Origin", + "Access-Control-Expose-Headers", + ], +) + + +@app.get("/") +def read_root(): + logging.info("Debug hello") + return {"Hello": "World"} + + +@app.get("/favicon.ico", include_in_schema=False) +async def favicon(): + if os.environ.get("RUNTIME_CODE_ROOT") is None: + file = os.path.join("static", "favicon.ico") + else: + file = os.path.join(os.environ["RUNTIME_CODE_ROOT"], "static", "favicon.ico") + + return FileResponse(file, media_type="image/x-icon", filename="favicon.ico") + + +app.include_router(items_router, prefix="/api", tags=["API"]) + +if __name__ == "__main__": + import uvicorn + + uvicorn.run("app:app", port=8000, log_level="debug", reload=True, host="localhost") diff --git a/src/bootstrap b/src/bootstrap new file mode 100644 index 0000000..2313ddd --- /dev/null +++ b/src/bootstrap @@ -0,0 +1 @@ +/opt/function/runtime/python3.10/rtsp/python/bin/python3 $RUNTIME_CODE_ROOT/app.py \ No newline at end of file diff --git a/src/static/__init__.py b/src/static/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/static/favicon.ico b/src/static/favicon.ico new file mode 100644 index 0000000..bc58e65 Binary files /dev/null and b/src/static/favicon.ico differ diff --git a/terraform/apigw.tf b/terraform/apigw.tf new file mode 100644 index 0000000..d6012f4 --- /dev/null +++ b/terraform/apigw.tf @@ -0,0 +1,140 @@ +############################################################################ +# Deploy API Gateway with VPC, Subnet, Route Table and Security Group +############################################################################ + +resource "opentelekomcloud_networking_secgroup_v2" "secgroup_1" { + name = format("%s_%s", var.prefix, "secgroup-1") + description = format("Security Group %s_%s", var.prefix, "secgroup-1") + region = var.OTC_SDK_REGION + delete_default_rules = true + tenant_id = var.OTC_SDK_PROJECTID + timeouts {} + +} + +resource "opentelekomcloud_networking_secgroup_rule_v2" "secgroup_rule_ingress_ipv4" { + direction = "ingress" + ethertype = "IPv4" + protocol = "tcp" + port_range_min = 80 + port_range_max = 80 + #remote_ip_prefix = "0.0.0.0/0" + security_group_id = opentelekomcloud_networking_secgroup_v2.secgroup_1.id + tenant_id = var.OTC_SDK_PROJECTID + description = "Allow incomming traffic on port 80" +} + +resource "opentelekomcloud_networking_secgroup_rule_v2" "secgroup_rule_ingress_ipv4_443" { + direction = "ingress" + ethertype = "IPv4" + protocol = "tcp" + port_range_min = 443 + port_range_max = 443 + #remote_ip_prefix = "0.0.0.0/0" + security_group_id = opentelekomcloud_networking_secgroup_v2.secgroup_1.id + tenant_id = var.OTC_SDK_PROJECTID + description = "Allow incomming traffic on port 443" +} + +resource "opentelekomcloud_networking_secgroup_rule_v2" "secgroup_rule_ingress_ipv6" { + direction = "ingress" + ethertype = "IPv6" + protocol = "tcp" + port_range_min = 80 + port_range_max = 80 + #remote_ip_prefix = "0.0.0.0/0" + security_group_id = opentelekomcloud_networking_secgroup_v2.secgroup_1.id + tenant_id = var.OTC_SDK_PROJECTID + description = "Allow incomming traffic on port 80" +} + +resource "opentelekomcloud_networking_secgroup_rule_v2" "secgroup_rule_ingress_ipv6_443" { + direction = "ingress" + ethertype = "IPv6" + protocol = "tcp" + port_range_min = 443 + port_range_max = 443 + #remote_ip_prefix = "0.0.0.0/0" + security_group_id = opentelekomcloud_networking_secgroup_v2.secgroup_1.id + tenant_id = var.OTC_SDK_PROJECTID + description = "Allow incomming traffic on port 443" +} + +resource "opentelekomcloud_networking_secgroup_rule_v2" "secgroup_rule_v4" { + direction = "egress" + ethertype = "IPv4" + security_group_id = opentelekomcloud_networking_secgroup_v2.secgroup_1.id + tenant_id = var.OTC_SDK_PROJECTID +} + +resource "opentelekomcloud_networking_secgroup_rule_v2" "secgroup_rule_v6" { + direction = "egress" + ethertype = "IPv6" + security_group_id = opentelekomcloud_networking_secgroup_v2.secgroup_1.id + tenant_id = var.OTC_SDK_PROJECTID +} + +resource "opentelekomcloud_vpc_route_table_v1" "table_1" { + name = format("%s_%s", var.prefix, "rtb-vpc-1") + vpc_id = opentelekomcloud_vpc_v1.vpc_v1.id + description = "" + subnets = [ + opentelekomcloud_vpc_subnet_v1.subnet_v1.id + ] + timeouts {} + +} + +resource "opentelekomcloud_vpc_subnet_v1" "subnet_v1" { + name = format("%s_%s", var.prefix, "subnet-1") + + availability_zone = format("%s-01", var.OTC_SDK_REGION) + cidr = "192.168.0.0/24" + dhcp_enable = true + ipv6_enable = false + + vpc_id = opentelekomcloud_vpc_v1.vpc_v1.id + + gateway_ip = "192.168.0.1" + + dns_list = [ + "100.125.4.25", + "100.125.129.199" + ] + + timeouts {} + +} + +resource "opentelekomcloud_vpc_v1" "vpc_v1" { + cidr = "192.168.0.0/16" + description = "" + name = format("%s_%s", var.prefix, "vpc-1") + region = var.OTC_SDK_REGION +} + + +resource "opentelekomcloud_apigw_gateway_v2" "gateway" { + + availability_zones = [format("%s-01", var.OTC_SDK_REGION)] + bandwidth_size = 5 + ingress_bandwidth_charging_mode = "bandwidth" + ingress_bandwidth_size = 5 + loadbalancer_provider = "elb" + maintain_begin = "22:00:00" + name = format("%s_%s", var.prefix, "apig-1") + + security_group_id = opentelekomcloud_networking_secgroup_v2.secgroup_1.id + spec_id = "BASIC" + + subnet_id = opentelekomcloud_vpc_subnet_v1.subnet_v1.id + + timeouts { + create = null + delete = null + update = null + } + + vpc_id = opentelekomcloud_vpc_v1.vpc_v1.id + +} diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..992d811 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,185 @@ +########################################################## +# code bucket +########################################################## +resource "opentelekomcloud_obs_bucket" "codebucket" { + bucket = replace(format("%s-%s", var.prefix, "codebucket"), "_", "-") + acl = "private" +} + +########################################################## +# create zip file +########################################################## +resource "null_resource" "createZip" { + provisioner "local-exec" { + command = "python3 createZip.py" + working_dir = "${path.root}/.." + } + triggers = { + always_run = "${timestamp()}" + } + +} + + +########################################################## +# upload zip file to bucket +########################################################## +resource "opentelekomcloud_obs_bucket_object" "code_object" { + bucket = opentelekomcloud_obs_bucket.codebucket.bucket + key = format("%s/%s", "code", var.zip_file_name) + source = format("${path.root}/../%s", var.zip_file_name) + etag = filemd5(format("${path.root}/../%s", var.zip_file_name)) +} + +########################################################## +# store md5 of zip file in state +########################################################## +resource "terraform_data" "replacement" { + input = [ + filemd5(format("${path.root}/../%s", var.zip_file_name)) + ] +} + +########################################################## +# opentelekomcloud_fgs_function_v2.function +########################################################## +resource "opentelekomcloud_fgs_function_v2" "function" { + depends_on = [opentelekomcloud_obs_bucket_object.code_object] + + name = format("%s_%s", var.prefix, var.function_name) + app = "default" + # agency = var.agency_name + handler = "index.handler" + memory_size = 128 + timeout = 30 + max_instance_num = 400 + + runtime = "http" + + code_type = "obs" + code_url = format("https://%s/code/%s", opentelekomcloud_obs_bucket.codebucket.bucket_domain_name, var.zip_file_name) + code_filename = var.zip_file_name + + log_group_id = opentelekomcloud_lts_group_v2.FunctionLogGroup.id + log_group_name = opentelekomcloud_lts_group_v2.FunctionLogGroup.group_name + + log_topic_id = opentelekomcloud_lts_stream_v2.FunctionLogStream.id + log_topic_name = opentelekomcloud_lts_stream_v2.FunctionLogStream.stream_name + + lifecycle { + # replace if code in bucket changed + replace_triggered_by = [ + terraform_data.replacement + ] + } + + reserved_instances { + count = 1 + qualifier_name = "latest" + qualifier_type = "version" + } + +} + +########################################################## +# opentelekomcloud_apigw_group_v2.group +########################################################## +resource "opentelekomcloud_apigw_group_v2" "group1" { + name = format("%s_%s", var.prefix, "group1") + instance_id = opentelekomcloud_apigw_gateway_v2.gateway.id + description = format("API Group for %s", var.prefix) +} + +########################################################## +# opentelekomcloud_apigw_api_v2.api1 +########################################################## +resource "opentelekomcloud_apigw_api_v2" "api1" { + gateway_id = opentelekomcloud_apigw_gateway_v2.gateway.id + group_id = opentelekomcloud_apigw_group_v2.group1.id + name = format("%s_%s", var.prefix, "api1") + type = "Public" + request_protocol = "HTTP" + request_method = "ANY" + request_uri = "/" + security_authentication_type = "NONE" + match_mode = "PREFIX" + success_response = "Success response" + failure_response = "Failed response" + description = format("Created by script for %s", var.prefix) + + func_graph { + function_urn = opentelekomcloud_fgs_function_v2.function.urn + version = "latest" + timeout = 5000 + invocation_type = "sync" + network_type = "NON-VPC" + } + +} + +########################################################### +# random_id.trigger_replacer +########################################################### +resource "random_id" "trigger_replacer" { + keepers = { + # change to force new resource on each deploy + timestamp = timestamp() + } + byte_length = 8 +} + +########################################################## +# opentelekomcloud_fgs_trigger_v2.apig1 +########################################################## +resource "opentelekomcloud_fgs_trigger_v2" "apig1" { + function_urn = opentelekomcloud_fgs_function_v2.function.urn + type = "DEDICATEDGATEWAY" + status = "ACTIVE" + + lifecycle { + replace_triggered_by = [random_id.trigger_replacer] + } + + event_data = jsonencode({ + "name" = format("%s_%s", var.prefix, "api1") #trigger_1 + "type" = 1 + "path" = "/" + "protocol" = "HTTPS" + "req_method" = "ANY" + "group_id" = opentelekomcloud_apigw_group_v2.group1.id + "group_name" = opentelekomcloud_apigw_group_v2.group1.name + "match_mode" = "SWA" + "env_name" = "RELEASE" #opentelekomcloud_apigw_environment_v2.env.name + "env_id" = "DEFAULT_ENVIRONMENT_RELEASE_ID" #opentelekomcloud_apigw_environment_v2.env.id + "auth" = "NONE" + "func_info" = { + "function_urn" = opentelekomcloud_fgs_function_v2.function.urn + "invocation_type" = "sync" + "timeout" = 5000 + "version" = "latest" + } + "sl_domain" = format("https://%s.apic.%s.otc.t-systems.com/", opentelekomcloud_apigw_group_v2.group1.id, var.OTC_SDK_REGION) + "backend_type" = "FUNCTION" + "instance_id" = opentelekomcloud_apigw_gateway_v2.gateway.id + + }) +} + +########################################################## +# opentelekomcloud_lts_group_v2.FunctionLogGroup +########################################################## +resource "opentelekomcloud_lts_group_v2" "FunctionLogGroup" { + group_name = format("%s_%s_%s", var.prefix, var.function_name, "log_group") + ttl_in_days = 1 + +} + +########################################################## +# opentelekomcloud_lts_stream_v2.FunctionLogStream +########################################################## +resource "opentelekomcloud_lts_stream_v2" "FunctionLogStream" { + group_id = opentelekomcloud_lts_group_v2.FunctionLogGroup.id + stream_name = format("%s_%s_%s", var.prefix, var.function_name, "log_stream") +} + + diff --git a/terraform/output.tf b/terraform/output.tf new file mode 100644 index 0000000..8151884 --- /dev/null +++ b/terraform/output.tf @@ -0,0 +1,32 @@ +########################################################## +# Output the URL of the deployed API +########################################################## +output "URL" { + value = format("https://%s.apic.%s.otc.t-systems.com", + opentelekomcloud_apigw_group_v2.group1.id, var.OTC_SDK_REGION) +} + +output "URL_Root" { + value = format("https://%s.apic.%s.otc.t-systems.com/", + opentelekomcloud_apigw_group_v2.group1.id, var.OTC_SDK_REGION) +} + +output "URL_APIItems" { + value = format("https://%s.apic.%s.otc.t-systems.com/api/items/100", + opentelekomcloud_apigw_group_v2.group1.id, var.OTC_SDK_REGION) +} + +output "URL_docs" { + value = format("https://%s.apic.%s.otc.t-systems.com/docs", + opentelekomcloud_apigw_group_v2.group1.id, var.OTC_SDK_REGION) +} + +output "URL_redocs" { + value = format("https://%s.apic.%s.otc.t-systems.com/redoc", + opentelekomcloud_apigw_group_v2.group1.id, var.OTC_SDK_REGION) +} + +output "URL_openapi" { + value = format("https://%s.apic.%s.otc.t-systems.com/openapi.json", + opentelekomcloud_apigw_group_v2.group1.id, var.OTC_SDK_REGION) +} diff --git a/terraform/provider.tf b/terraform/provider.tf new file mode 100644 index 0000000..9a6afae --- /dev/null +++ b/terraform/provider.tf @@ -0,0 +1,127 @@ +# ---------------------------------------------------------------------------- +# Secret variables to be injected as envvar (capital letters for Windows systems) +# - no defaults +# - Declared as sensitive --> Not printed in console or log if used in resources +# ---------------------------------------------------------------------------- + + +# set by environment variable TF_VAR_OTC_SDK_AK +variable "OTC_SDK_AK" { + description = "Personal access key" + type = string + sensitive = true +} + +# set by environment variable TF_VAR_OTC_SDK_SK +variable "OTC_SDK_SK" { + description = "Personal secret key" + type = string + sensitive = true +} + +# set by environment variable TF_VAR_OTC_SDK_DOMAIN_NAME +variable "OTC_SDK_DOMAIN_NAME" { + description = "Domain Name, eg. OTC-EU-DE-000000000010000XXXXX" + type = string +} + +# set by environment variable TF_VAR_OTC_SDK_PROJECTID +variable "OTC_SDK_PROJECTID" { + description = "Project Id" + type = string +} + +# set by environment variable TF_VAR_OTC_SDK_PROJECTNAME +variable "OTC_SDK_PROJECTNAME" { + description = "Project Name, eg. eu-de_MYPROJECT" + type = string +} + +# set by environment variable OTC_SDK_REGION +variable "OTC_SDK_REGION" { + description = "Region Name, eg. eu-de" + type = string +} + +terraform { + required_providers { + # specifies required provider, source and version + # see https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest + + opentelekomcloud = { + source = "opentelekomcloud/opentelekomcloud" + version = ">= 1.36.47" + } + } + backend "s3" { + # See: https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest/docs/guides/backends + + # (Required) Specifies the endpoint for OpenTelekomCloud OBS. + # The value is https://obs.{{region}}.otc.t-systems.com. + # This can also be sourced from the AWS_S3_ENDPOINT environment variable + endpoints = { + s3 = "https://obs.eu-de.otc.t-systems.com" + } + + # (Required) Specifies the bucket name where to store the state. + # Make sure to create it before. + bucket = "sample-tf-backend" + + # (Required) Specifies the path to the state file inside the bucket. + key = "terraform_state/fg-http-python-fastapi2.tfstate" + + # (Required) Specifies the region where the bucket is located. + # This can also be sourced from the AWS_DEFAULT_REGION and + # AWS_REGION environment variables. + region = "eu-de" + + # (Required) Skip credentials validation via the STS API. + # It's mandatory for OpenTelekomCloud. + skip_credentials_validation = true + + # (Required) Skip validation of provided region name. + # It's mandatory for OpenTelekomCloud. + skip_region_validation = true + + skip_requesting_account_id = true + + # (Required) Skip usage of EC2 Metadata API. + # It's mandatory for OpenTelekomCloud. + skip_metadata_api_check = true + + # (Optional) Do not include checksum when uploading S3 Objects. + # Useful for some S3-Compatible APIs. + skip_s3_checksum = true + + # Although the terraform block does not accept variables or locals and + # all backend configuration values must be hardcoded, you can provide + # the credentials via the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY + # environment variables to access OBS, respectively: + # + # export AWS_ACCESS_KEY_ID="your accesskey" + # export AWS_SECRET_ACCESS_KEY="your secretkey" + # + # secret_key set env var: AWS_ACCESS_KEY_ID + # access_key set env var: AWS_SECRET_ACCESS_KEY + + } + +} + +# ---------------------------------------------------------------------------- +# Providers settings --> OTC +# We use the AKSK auth scheme +# See https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest/docs +# ---------------------------------------------------------------------------- +# + +provider "opentelekomcloud" { + auth_url = "https://iam.eu-de.otc.t-systems.com/v3" + + access_key = var.OTC_SDK_AK + secret_key = var.OTC_SDK_SK + + domain_name = var.OTC_SDK_DOMAIN_NAME + tenant_name = var.OTC_SDK_PROJECTNAME + +} diff --git a/terraform/setupTFvars.sh b/terraform/setupTFvars.sh new file mode 100644 index 0000000..816a4aa --- /dev/null +++ b/terraform/setupTFvars.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# get all env variables starting with "OTC_" and prepend with "TF_VAR_" +for var in "${!OTC_@}"; do + export $(printf 'TF_VAR_%s=%s\n' "$var" "${!var}") +done + +# for provider configuration set AK/SK to be used. +export AWS_ACCESS_KEY_ID=$OTC_SDK_AK +export AWS_SECRET_ACCESS_KEY=$OTC_SDK_SK + +# configure terraform s3 backend to work with obs +# https://community.open-telekom-cloud.com/community?id=community_question&sys_id=1207be61138086d0d15a246ea6744162&view_source=searchResult +export AWS_REQUEST_CHECKSUM_CALCULATION=when_required +export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required \ No newline at end of file diff --git a/terraform/testevents.tf b/terraform/testevents.tf new file mode 100644 index 0000000..9c1509d --- /dev/null +++ b/terraform/testevents.tf @@ -0,0 +1,94 @@ +########################################################## +# Test events for FastAPI function to be used in +# OpenTelekomCloud FunctionGraph console +########################################################## + +########################################################## +# Test event for / +########################################################## +resource "opentelekomcloud_fgs_event_v2" "event_root" { + function_urn = opentelekomcloud_fgs_function_v2.function.urn + name = "root" + content = base64encode(jsonencode({ + body = "" + requestContext = {} + queryStringParameters = { "responseType" : "html" } + httpMethod = "GET" + pathParameters = {} + headers = {} + path = "/" + isBase64Encoded = true + })) +} + +########################################################## +# Test event for /api/items/100?q=20 +########################################################## +resource "opentelekomcloud_fgs_event_v2" "event_items" { + function_urn = opentelekomcloud_fgs_function_v2.function.urn + name = "items" + content = base64encode(jsonencode({ + body = "" + requestContext = {} + queryStringParameters = { "q" : "20" } + httpMethod = "GET" + pathParameters = {} + headers = {} + path = "/api/items/100" + isBase64Encoded = true + })) +} + +########################################################## +# Test event for /docs +########################################################## +resource "opentelekomcloud_fgs_event_v2" "event_docs" { + function_urn = opentelekomcloud_fgs_function_v2.function.urn + name = "docs" + content = base64encode(jsonencode({ + body = "" + requestContext = {} + queryStringParameters = {} + httpMethod = "GET" + pathParameters = {} + headers = {} + path = "/docs" + isBase64Encoded = true + })) +} + +########################################################## +# Test event for /redoc +########################################################## +resource "opentelekomcloud_fgs_event_v2" "event_redocs" { + function_urn = opentelekomcloud_fgs_function_v2.function.urn + name = "redocs" + content = base64encode(jsonencode({ + body = "" + requestContext = {} + queryStringParameters = {} + httpMethod = "GET" + pathParameters = {} + headers = {} + path = "/redoc" + isBase64Encoded = true + })) +} + +########################################################## +# Test event for /openapi.json +########################################################## +resource "opentelekomcloud_fgs_event_v2" "event_openapi" { + function_urn = opentelekomcloud_fgs_function_v2.function.urn + name = "openapi" + content = base64encode(jsonencode({ + body = "" + requestContext = {} + queryStringParameters = {} + httpMethod = "GET" + pathParameters = {} + headers = {} + path = "/openapi.json" + isBase64Encoded = true + })) +} \ No newline at end of file diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..3d27d6a --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,16 @@ + +variable "prefix" { + type = string + default = "sample_fastapi_2" +} + +variable "function_name" { + type = string + default = "app" +} + +# name of zip file to deploy, generated by createZip.py +variable "zip_file_name" { + type = string + default = "code.zip" +} \ No newline at end of file diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..23594c7 --- /dev/null +++ b/tox.ini @@ -0,0 +1,18 @@ +[tox] +minversion = 3.6 +ignore_basepython_conflict = True + + +[testenv:docs] +passenv = GH_ACTIONS_* +deps = + -r{toxinidir}/doc/requirements.txt +commands = + sphinx-build -W --keep-going -b html doc/source/ doc/build/html + +[testenv:docs-auto] +deps = + -r{toxinidir}/doc/requirements.txt +commands = + sphinx-autobuild --port 8882 doc/source/ doc/build/html +