Changed crystal template, picked latest flask one

This commit is contained in:
Roberto Alsina 2023-06-01 18:52:39 -03:00
parent f2be91d628
commit 1cc3ef2e82
22 changed files with 242 additions and 112 deletions

1
.gitignore vendored
View File

@ -163,3 +163,4 @@ cython_debug/
.vscode/
build
.secrets

View File

@ -16,6 +16,6 @@ functions:
handler: ./tapas
image: ralsina/tapas:latest
tapas2:
lang: crystal-http
lang: crystal
handler: ./tapas2
image: ralsina/tapas2:latest

View File

@ -1,21 +1,7 @@
require "http/request"
require "http/headers"
require "json"
class Handler
def run(request : HTTP::Request)
if request.body.nil?
return {
body: "Foo",
status_code: 200,
headers: HTTP::Headers{"Content-Type" => "text/plain"},
}
end
{
body: "Hello, Crystal. You said: #{request.body.try(&.gets_to_end)}",
status_code: 200,
headers: HTTP::Headers{"Content-Type" => "text/plain"},
}
def run(req : String)
return JSON::Any.new("Hello, Crystal. You said: #{req}")
end
end

View File

@ -1,2 +1,7 @@
name: crystal-http-template
name: crystal_faas_function
version: 0.1.0
# dependencies:
# pg:
# github: will/crystal-pg
# version: "~> 0.5"

View File

@ -1,37 +0,0 @@
FROM ghcr.io/openfaas/of-watchdog:0.9.11 as watchdog
FROM crystallang/crystal:1.8.2
COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog
RUN chmod +x /usr/bin/fwatchdog
ARG ADDITIONAL_PACKAGE
RUN apt-get update \
&& apt-get install -qy --no-install-recommends ${ADDITIONAL_PACKAGE}
WORKDIR /home/app
COPY . .
COPY function/shard.yml shard.yml
RUN shards install
RUN crystal build main.cr -o handler --release
# Create a non-root user
RUN addgroup --system app \
&& adduser --system --ingroup app app
RUN chown app:app -R /home/app
USER app
WORKDIR /home/app
ENV fprocess="./handler"
EXPOSE 8080
HEALTHCHECK --interval=2s CMD [ -e /tmp/.lock ] || exit 1
ENV upstream_url="http://127.0.0.1:5000"
ENV mode="http"
CMD ["fwatchdog"]

View File

@ -1,12 +0,0 @@
require "http/request"
require "http/headers"
class Handler
def run(request : HTTP::Request)
{
body: "Hello, Crystal. You said: #{request.body.try(&.gets_to_end)}",
status_code: 200,
headers: HTTP::Headers{"Content-Type" => "text/plain"},
}
end
end

View File

@ -1,2 +0,0 @@
name: crystal-http-template
version: 0.1.0

View File

@ -1,41 +0,0 @@
require "http/server"
require "./function/handler"
server = HTTP::Server.new do |context|
response_triple : NamedTuple(body: String, headers: HTTP::Headers, status_code: Int32) |
NamedTuple(body: String, headers: HTTP::Headers) |
NamedTuple(body: String, status_code: Int32) |
NamedTuple(body: String) |
NamedTuple(headers: HTTP::Headers, status_code: Int32) |
NamedTuple(headers: HTTP::Headers) |
NamedTuple(status_code: Int32)
handler = Handler.new
response_triple = handler.run(context.request)
if response_triple.is_a?(NamedTuple(body: String, headers: HTTP::Headers, status_code: Int32) |
NamedTuple(body: String, status_code: Int32) |
NamedTuple(headers: HTTP::Headers, status_code: Int32) |
NamedTuple(status_code: Int32))
context.response.status_code = response_triple[:status_code]
end
if response_triple.is_a?(NamedTuple(body: String, headers: HTTP::Headers, status_code: Int32) |
NamedTuple(body: String, headers: HTTP::Headers) |
NamedTuple(headers: HTTP::Headers, status_code: Int32) |
NamedTuple(headers: HTTP::Headers))
response_triple[:headers].each do |key, value|
context.response.headers[key] = value
end
end
if response_triple.is_a?(NamedTuple(body: String, headers: HTTP::Headers, status_code: Int32) |
NamedTuple(body: String, headers: HTTP::Headers) |
NamedTuple(body: String, status_code: Int32) |
NamedTuple(body: String))
context.response.print(response_triple[:body])
end
end
server.bind_tcp "0.0.0.0", 5000
server.listen

View File

@ -0,0 +1,38 @@
FROM crystallang/crystal:1.8.0 as builder
RUN apt update \
&& apt install -y curl \
&& echo "Pulling watchdog binary from Github." \
&& curl -sSL https://github.com/openfaas/faas/releases/download/0.9.6/fwatchdog > /usr/bin/fwatchdog \
&& chmod +x /usr/bin/fwatchdog
WORKDIR /home/app
COPY . .
COPY function/shard.yml shard.yml
RUN shards install
RUN crystal build main.cr -o handler --release
FROM crystallang/crystal:1.8.0
RUN apt install ca-certificates
# Add non root user
RUN adduser app
RUN mkdir -p /home/app
WORKDIR /home/app
COPY --from=builder /usr/bin/fwatchdog .
COPY --from=builder /home/app/function/ .
COPY --from=builder /home/app/handler .
RUN chown -R app /home/app
USER app
ENV fprocess="./handler"
EXPOSE 8080
HEALTHCHECK --interval=2s CMD [ -e /tmp/.lock ] || exit 1
CMD ["./fwatchdog"]

View File

@ -0,0 +1,7 @@
require "json"
class Handler
def run(req : String)
return JSON::Any.new("Hello, Crystal. You said: #{req}")
end
end

View File

@ -0,0 +1,7 @@
name: crystal_faas_function
version: 0.1.0
# dependencies:
# pg:
# github: will/crystal-pg
# version: "~> 0.5"

11
template/crystal/main.cr Normal file
View File

@ -0,0 +1,11 @@
# Copyright (c) Thomas Peikert 2018. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
require "json"
require "./function/handler"
req = STDIN.gets_to_end
handler = Handler.new
res = handler.run req
puts res

View File

@ -1,6 +1,6 @@
language: crystal
fprocess: ./handler
welcome_message: |
You have created a new function which uses crystal 1.0.0.
You have created a new function which uses crystal 1.7.3 🎉
To include third-party dependencies, use a vendoring tool like shards:
shards documentation: https://github.com/crystal-lang/shards

View File

@ -0,0 +1,63 @@
ARG PYTHON_VERSION=3.11
FROM --platform=${TARGETPLATFORM:-linux/amd64} ghcr.io/openfaas/of-watchdog:0.9.10 as watchdog
FROM --platform=${TARGETPLATFORM:-linux/amd64} python:${PYTHON_VERSION}-alpine as build
COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog
RUN chmod +x /usr/bin/fwatchdog
ARG ADDITIONAL_PACKAGE
# Alternatively use ADD https:// (which will not be cached by Docker builder)
RUN apk --no-cache add openssl-dev ${ADDITIONAL_PACKAGE}
# Add non root user
RUN addgroup -S app && adduser app -S -G app
RUN chown app /home/app
USER app
ENV PATH=$PATH:/home/app/.local/bin
WORKDIR /home/app/
COPY --chown=app:app index.py .
COPY --chown=app:app requirements.txt .
USER root
RUN pip install --no-cache-dir -r requirements.txt
# Build the function directory and install any user-specified components
USER app
RUN mkdir -p function
RUN touch ./function/__init__.py
WORKDIR /home/app/function/
COPY --chown=app:app function/requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
#install function code
USER root
COPY --chown=app:app function/ .
FROM build as test
ARG TEST_COMMAND=tox
ARG TEST_ENABLED=true
RUN [ "$TEST_ENABLED" = "false" ] && echo "skipping tests" || eval "$TEST_COMMAND"
FROM build as ship
WORKDIR /home/app/
#configure WSGI server and healthcheck
USER app
ENV fprocess="python index.py"
ENV cgi_headers="true"
ENV mode="http"
ENV upstream_url="http://127.0.0.1:5000"
HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1
CMD ["fwatchdog"]

View File

@ -0,0 +1,7 @@
def handle(req):
"""handle a request to the function
Args:
req (str): request body
"""
return req

View File

@ -0,0 +1,10 @@
from .handler import handle
# Test your handler here
# To disable testing, you can set the build_arg `TEST_ENABLED=false` on the CLI or in your stack.yml
# https://docs.openfaas.com/reference/yaml/#function-build-args-build-args
def test_handle():
# assert handle("input") == "input"
pass

View File

@ -0,0 +1,41 @@
# If you would like to disable
# automated testing during faas-cli build,
# Replace the content of this file with
# [tox]
# skipsdist = true
# You can also edit, remove, or add additional test steps
# by editing, removing, or adding new testenv sections
# find out more about tox: https://tox.readthedocs.io/en/latest/
[tox]
envlist = lint,test
skipsdist = true
[testenv:test]
deps =
flask
pytest
-rrequirements.txt
commands =
# run unit tests with pytest
# https://docs.pytest.org/en/stable/
# configure by adding a pytest.ini to your handler
pytest
[testenv:lint]
deps =
flake8
commands =
flake8 .
[flake8]
count = true
max-line-length = 127
max-complexity = 10
statistics = true
# stop the build if there are Python syntax errors or undefined names
select = E9,F63,F7,F82
show-source = true

View File

@ -0,0 +1,41 @@
# Copyright (c) Alex Ellis 2017. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
from flask import Flask, request
from function import handler
from waitress import serve
import os
app = Flask(__name__)
# distutils.util.strtobool() can throw an exception
def is_true(val):
return len(val) > 0 and val.lower() == "true" or val == "1"
@app.before_request
def fix_transfer_encoding():
"""
Sets the "wsgi.input_terminated" environment flag, thus enabling
Werkzeug to pass chunked requests as streams. The gunicorn server
should set this, but it's not yet been implemented.
"""
transfer_encoding = request.headers.get("Transfer-Encoding", None)
if transfer_encoding == u"chunked":
request.environ["wsgi.input_terminated"] = True
@app.route("/", defaults={"path": ""}, methods=["POST", "GET"])
@app.route("/<path:path>", methods=["POST", "GET"])
def main_route(path):
raw_body = os.getenv("RAW_BODY", "false")
as_text = True
if is_true(raw_body):
as_text = False
ret = handler.handle(request.get_data(as_text=as_text))
return ret
if __name__ == '__main__':
serve(app, host='0.0.0.0', port=5000)

View File

@ -0,0 +1,3 @@
flask
waitress
tox==3.*

View File

@ -0,0 +1,2 @@
language: python3-flask
fprocess: python index.py