This repository has been archived by the owner on Dec 15, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 753
Add the support for perl runtime. #1014
Open
stanguturi
wants to merge
1
commit into
vmware-archive:master
Choose a base branch
from
stanguturi:topic/stanguturi/perl-runtime-support
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Copyright 2019 VMware, Inc. | ||
# | ||
# 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. | ||
|
||
FROM perl:5.26-threaded | ||
MAINTAINER Sankar Tanguturi [email protected] | ||
|
||
RUN apt-get update && \ | ||
apt-get upgrade -y && \ | ||
BUILD_PACKAGES="curl make gcc" && \ | ||
apt-get -y install $BUILD_PACKAGES --no-install-recommends && \ | ||
curl -L http://cpanmin.us | perl - Dancer2 && \ | ||
apt-get remove --purge -y curl make gcc && \ | ||
apt-get autoremove -y && \ | ||
apt-get clean && \ | ||
apt-get autoclean && \ | ||
echo -n > /var/lib/apt/extended_states && \ | ||
rm -rf /var/lib/apt/lists/* && \ | ||
rm -rf /usr/share/man/?? && \ | ||
rm -rf /usr/share/man/??_* && \ | ||
rm -rf ~/.cpanm/* | ||
|
||
WORKDIR / | ||
ADD kubeless.pl . | ||
|
||
USER 1000 | ||
|
||
CMD ["perl", "/kubeless.pl"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Copyright 2019 VMware, Inc. | ||
# | ||
# 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. | ||
|
||
build5.26: | ||
docker build -t kubeless/perl:5.26$$RUNTIME_TAG_MODIFIER -f Dockerfile.5.26 . | ||
|
||
push5.26: | ||
docker push kubeless/perl:5.26$$RUNTIME_TAG_MODIFIER | ||
|
||
# Mandatory jobs | ||
build-all: build5.26 | ||
push-all: push5.26 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
|
||
# Introduction | ||
`kubeless` framework supports deploying functions written in `python`, `golang`, `nodejs`, `ruby`, `php`, `java` and `dotnet`. Now, the support is added to deploy `perl` functions without much effort. | ||
|
||
# Architecture | ||
* [Dancer](http://perldancer.org) is a simple but powerful web application framework designed for `perl`. `Dancer` is used to implement the `perl` runtime support in `kubeless`. | ||
* A Docker Image is already created with `Dancer`, `perl 5.26` (compiled with Thread support) and other necessary dependencies. The main entry file i.e. `kubeless.pl` is packaged inside the docker image and is started when the image is deployed and started. | ||
* The main entry file `kubeless.pl` is a `dancer` perl application that starts and waits for the incoming HTTP requests. | ||
* When `kubeless` deploys the docker image, few key pieces of information are provided in the following environrment variables. | ||
|
||
```perl | ||
MOD_NAME => 'The name of the module to be loaded.' | ||
FUNC_HANDLER => 'The name of the function to be invoked.' | ||
FUNC_PORT => 'Port on which the web application should listen.' | ||
FUC_TIMEOUT => 'Timeout (in seconds) in which the function should complete the execution.' | ||
``` | ||
|
||
* The `kubeless.pl` file parses the incoming requests, executes the function (in a separate thread) and returns the result. The function must complete it's execution in a fixed timeout. (Default is 10 seconds). If the function is not done executing, it will | ||
be killed and an error is returned to the client. | ||
|
||
## Default Route End-points | ||
* Following are few default routes defined by `perl` framework in `kubelss` | ||
* `/healthz` : GET Method is implemented for /healthz route. This route can be used to check if the application is up and running or not. A successful GET request will return 'OK' string in the response. | ||
* `/metrics` : This route is useful to capture the event monitoring. Currently, it's not implemented and will return 'Not implemented' string in the response. | ||
* All other routes that match `/*` path are routed to the function handler. `GET`, `POST`, `PATCH`, `DELETE` methods are implemented for all these routes. | ||
|
||
## Structure of a perl module | ||
Following is a template for a simple perl module. | ||
```perl | ||
# helloget.pm | ||
# The file name must end with .pm suffix | ||
# Each function will be invoked with two arguments. | ||
# First one is a reference to an event object. | ||
# Second one is a reference to a function context object. | ||
use strict; | ||
sub hello_world { | ||
$event_ref = shift; | ||
$func_ref = shift; | ||
# The POST data in the incoming HTTP request is available | ||
# in data attribute of event object. | ||
$data = $event_ref->{data}; | ||
# If the client POSTS the JSON data, then the $data | ||
# will be a perl has variable with proper key / value / pairs. | ||
# Ex: JSON data: {'id' : 123, 'foo' : 'bar'} | ||
# data{id} = 123, data{foo} = 'bar' | ||
return "Result Value" | ||
} | ||
|
||
# The last line of the perl module MUST be 1 | ||
1 | ||
``` | ||
|
||
## Building Perl Docker image | ||
```bash | ||
cd docker/runtime/perl | ||
make # This will build docker image | ||
``` | ||
* By default, kubeless/perl:5.26 is the tag created for the images built. | ||
* You can use the following commands to push to your own repository. | ||
```bash | ||
docker tag kubeless/perl:5.26 <username>/perl-kubeless:5.26 | ||
docker push <username>/perl-kubeless:5.26 | ||
``` | ||
|
||
## Examples | ||
```bash | ||
cd examples/perl | ||
make # Make file to run all tests. | ||
#check all .pm files | ||
``` | ||
|
||
### Updating the kubeless config map. | ||
``` | ||
kubectl edit -n kubeless configmap kubeless-config | ||
# Add the following entry | ||
{ | ||
"ID": "perl", | ||
"compiled": false, | ||
"versions": [ | ||
{ | ||
"name": "perl526”, | ||
"version": "5.26”, | ||
"runtimeImage": "stanguturi/perl-kubeless:5.26”, | ||
"initImage": "perl:5.26-threaded" | ||
} | ||
], | ||
"depName": "", | ||
"fileNameSuffix": ".pm", | ||
} | ||
# load the controller manager. | ||
kubectl delete pods -n kubeless -l kubeless=controller | ||
``` | ||
|
||
## Deploying the perl module | ||
* Write a proper perl module as mentioned above. | ||
* Currently only perl5.26 is the supported perl runtime. | ||
* Use the following commandi in `kubeless` to deply a function. | ||
``` | ||
kubeless deploy function hello_world \ | ||
--from-file helloget.pm | ||
--handler helloget.hello | ||
--runtime perl5.26 | ||
``` | ||
|
||
## Tips for writing a proper perl Module | ||
* The name of the perl module file must end with .pm extension. | ||
* The last line of the perl module must be 1 | ||
```perl | ||
1 | ||
``` | ||
* Make sure that there are no compilation errors in the perl module. | ||
* Make sure that the perl module runs with perl 5.26. | ||
* Add `use strict` at the beginning of the perl module. | ||
* The `perl` package installed in the `container image` is built with `threading support`. If the perl module has any dependies with `perl threads`, it should just work. | ||
* Make sure that the function finishes the execution in the specific timeout. The default timeout is 10 seconds. | ||
* Make sure that a proper scalar string is returned from the perl function. | ||
|
||
**Happy Perl function Deploying** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
#!/usr/bin/env perl | ||
# Copyright 2019 VMware, Inc. | ||
# | ||
# 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. | ||
|
||
use threads; | ||
use JSON::MaybeXS qw(encode_json decode_json); | ||
use Dancer2; | ||
use Time::HiRes qw(usleep); | ||
|
||
# Port on which the dancer app will run. | ||
my $func_port = $ENV{FUNC_PORT} || 8080; | ||
|
||
# Timeout in seconds for the main function to finish. | ||
my $timeout = int($ENV{FUNC_TIMEOUT} || 10); | ||
|
||
# Module name and function handler. | ||
# While deploying the function in kubeless, | ||
# the module name is specified in --from-file command line argument and | ||
# the function name is specified in --handler command line option. | ||
my $mod_name = $ENV{MOD_NAME}; | ||
my $func_handler = $ENV{FUNC_HANDLER}; | ||
|
||
# This is the standard convention followed for kubeless runtimes. | ||
# The main module file will be located in /kubeless/ directory. | ||
# The runtime script needs to prepare the proper path and load it. | ||
my $mod_path = "/kubeless/" . $mod_name . ".pm"; | ||
|
||
# Only for testing. Need to remove for production purposes. | ||
if (!$mod_name) { | ||
$mod_path = $ENV{MOD_PATH}; | ||
} | ||
# TODO: Known bug in Perl Dancer app. Need to set the port here. | ||
set port => $func_port; | ||
|
||
# TODO: Set the proper serializer app for JSON. But known bug in Dancer app. | ||
# set serializer => 'JSON'; | ||
|
||
my %function_context = ( | ||
'function' => $func_handler, | ||
'module' => $mod_path, | ||
'timeout' => $timeout, | ||
'runtime' => $ENV{'FUNC_RUNTIME'}, | ||
'memory-limit' => $ENV{'FUNC_MEMORY_LIMIT'}, | ||
); | ||
|
||
# It's always better to load the module in the beginning itself. If there | ||
# are any errors, they will be caught early while the container is loading. | ||
# The same format / style is being followed in other runtimes i.e. python, | ||
# nodejs, etc. | ||
eval { | ||
require($mod_path); | ||
}; if ($@) { | ||
print("Error while invoking the module at $mod_path. Error: " . $@ . " \n"); | ||
exit(1); | ||
} | ||
|
||
# Endpoint for /healthz. This is one of the main requirements by kubeless | ||
# runtimes. | ||
get '/healthz' => sub { | ||
return 'OK'; | ||
}; | ||
|
||
# Endpoint for /metrics. This is one of the main requirements by kubeless | ||
# runtimes. | ||
get '/metrics' => sub { | ||
# TODO: Needs to implement. We need to use proper prometheus client | ||
# package in perl. But it seems to be complicated. Lets leave this as is | ||
# for now. | ||
return '!!!!! To be Implemented !!!!!'; | ||
}; | ||
|
||
# Wrapper around the main function execution. This function will run the | ||
# main function in a thread and waits for the timeout. If the function doesn'test | ||
# complete in the timeout, then it is explicitly killed and a proper error | ||
# is returned to the client. | ||
sub thread_wrap { | ||
my $event_ref = shift; | ||
my $func_ref = shift; | ||
|
||
my $return; | ||
|
||
# If the function takes a lot of time to finish, the Main | ||
# thread sends a kill signal to terminate the function. | ||
# Registering a new function for the KILL signal is the only cleanest | ||
# way to provide the timeout functionality. | ||
$SIG{'KILL'} = sub { threads->exit(); }; | ||
|
||
eval { | ||
my $function = $func_ref->{function}; | ||
my $func_result = &{\&{$function}}($event_ref, $func_ref); | ||
$return = $func_result; | ||
}; if ($@) { | ||
$return = "Error while invoking the function. Error: '" . $@ . "' \n"; | ||
} | ||
|
||
return $return; | ||
} | ||
|
||
# Main route mapping for all the incoming HTTP requests from the client. | ||
any ['get', 'post', 'patch', 'delete'] => qr{/.*} => sub { | ||
my $content_type = request->header('content-type'); | ||
my $data = request->body(); | ||
if ($content_type eq 'application/json') { | ||
eval { | ||
$data = decode_json($data); | ||
}; if ($@) { | ||
response->status(500); | ||
return "Unable to decode the json body. Error: '" . $@ . "' \n"; | ||
} | ||
} | ||
|
||
# TODO: Implement extensions | ||
# my %extensions = ( 'request' => request ); | ||
|
||
my %event = ( | ||
'data' => $data, | ||
'event-id' => request->header('event-id'), | ||
'event-type' => request->header('event-type'), | ||
'event-time' => request->header('event-time'), | ||
'event-namespace' => request->header('event-namespace'), | ||
); | ||
|
||
my $thr = threads->create('thread_wrap', \%event, \%function_context); | ||
my $sleep_interval = 250000; | ||
my $timeout_usecs = $timeout * 1000000; | ||
for (my $i = 0; $i < $timeout_usecs ; $i = $i + $sleep_interval) { | ||
if ($thr->is_running()) { | ||
usleep($sleep_interval); | ||
} else { | ||
last; | ||
} | ||
} | ||
|
||
my $result; | ||
if ($thr->is_joinable()) { | ||
$result = $thr->join(); | ||
} else { | ||
$thr->kill('KILL')->detach(); | ||
$result = "**** Error: Task killed since it exceeded " . | ||
$timeout . " seconds ... ***\n"; | ||
response->status(500); | ||
} | ||
|
||
return "$result"; | ||
}; | ||
|
||
# Main entry point for the Dancer web application framework. | ||
dance; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Copyright 2019 VMware, Inc. | ||
# | ||
# 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. | ||
# Create a custom image with a perl function | ||
FROM stanguturi/perl-kubeless:5.26 | ||
ENV FUNC_HANDLER=hello \ | ||
MOD_NAME=helloget | ||
ADD helloget.pm / | ||
RUN mkdir -p /kubeless/ | ||
RUN chown 1000:1000 /kubeless | ||
ENTRYPOINT [ "bash", "-c", "cp /helloget.pm /kubeless/ && perl /kubeless.pl"] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a typo
kubelss
-->kubeless