Moving out Environments to own repo (#1810)

Moved out environments and examples out of main Fission repo to their own repo. This is to allow changes to environment releases to be independent of main Fission releases.
This commit is contained in:
Vishal
2021-05-01 15:42:35 +05:30
committed by GitHub
parent f72bfd8f3a
commit 5b4bf5964e
252 changed files with 8 additions and 10139 deletions
+7 -7
View File
@@ -14,10 +14,16 @@ jobs:
- name: Checkout sources
uses: actions/checkout@v2.3.4
- name: Checkout sources
uses: actions/checkout@v2.3.4
with:
repository: fission/examples
path: examples
- name: setup go
uses: actions/setup-go@v2.1.3
with:
go-version: '1.12.17'
go-version: '1.14.0'
- name: Helm installation
uses: Azure/setup-helm@v1
@@ -64,12 +70,6 @@ jobs:
go build -o fission cmd/fission-cli/main.go
sudo mv fission /usr/local/bin
fission version
# - name: Skip & install fission
# run: kubectl create ns fission && helm install --namespace fission --name-template fission https://github.com/fission/fission/releases/download/1.11.2/fission-all-1.11.2.tgz --set pruneInterval=1
# - name: Skip & install cli
# run: curl -Lo fission https://github.com/fission/fission/releases/download/1.11.2/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
- name: Port-forward fission components
run: |
-61
View File
@@ -1,61 +0,0 @@
sudo: required
dist: trusty
branches:
only:
- master
language: go
go:
- 1.12.x
env:
- KUBECONFIG=${HOME}/.kube/config PATH=$HOME/k8scli:$HOME/tool:$HOME/google-cloud-sdk/bin:${PATH} GO111MODULE=on DOCKER_CACHE_DIR=${HOME}/docker/
cache:
directories:
- $HOME/google-cloud-sdk/
- $HOME/k8scli
- $HOME/tool
- $HOME/.cache/go-build
- $HOME/gopath/pkg/mod
services:
- docker
before_install:
- sudo apt-get update
# - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
- sudo apt-get -y install apache2-utils parallel realpath
- sudo sysctl net.ipv6.conf.all.disable_ipv6=0
install:
- hack/travis-kube-setup.sh
before_script:
- cd ${TRAVIS_BUILD_DIR}
- go mod download
- hack/verify-staticcheck.sh
- hack/verify-gofmt.sh
- hack/verify-govet.sh
- helm lint charts/fission-all/ charts/fission-core/
- hack/runtests.sh
- test/build.sh
script:
- cd ${TRAVIS_BUILD_DIR}
- test/test.sh
- test/upgrade/fission_upgrade_test.sh
after_script:
- bash <(curl -s https://codecov.io/bash)
- kubectl --namespace default delete configmap -l travisID=${TRAVIS_BUILD_ID} # remove in-test configmap to indicate this CI build finished
notifications:
slack:
rooms:
secure: YZ34vsfw1TtftJypg1MyP4+ihONI4gaxeS3FghBQli6+EezjzcxOXyj5VD+x0ucXfDeTaDrFfmVN0SAGObOoZUE+ea9KAoTo50tRLaD9kwOTACiekZalC4uuBguH0D1/A6vlbU8dchsr9mvIhbisG6mTncdPtqGYYHtyBQme6ngmmHbVAQFZcIBHmNuDb/HWhSr8KMEuyB6+mBXLYELHnXnf26cOhdGNaagqCOJTiemX85RGwIuOPxyBhKDFMLyHohDT7FJMH/qijveE6YgOYTQC5nYc2Np1KvC7hQkIu4nuyczyYlrNQl/TWv+SVI8PjIs0PYuCuD3gUqoVEi8d94HbrOPzFEpbwDS9P4qL39DGmco1Q56Vqxe6sRI0vDWPb5gCP1lSgs3PMECVn7Wor/pTvcL+C+U2jLwWJUl0vbyWCL7ngl/3iTssV7qBpUrI7Oclwp8LrQo9fPj0DL4gE9rNanpEWjQ6yPGaysIL1zLHtRghhm52A22NJGp71jkS2KEpLi6ZWFYjMeuXw5eOQFhqFlzyRJOmLYa3B607TLWuyo2L2CxAfMmq0FGfemvrkLZIWtlQKK4y9ImpsURwaGT2XCtThFtHl77wEss913nC+T2dX3O5Bl0UmxFd5S3mVM109I8c4lDosxnAjRfS9MheFlrG0gjSJSBCw57x7f0=
on_success: change
on_failure: always
-18
View File
@@ -1,18 +0,0 @@
FROM golang:onbuild
WORKDIR /go
COPY *.go /go/
RUN GOOS=linux GOARCH=386 go build -o server .
FROM alpine:3.5
WORKDIR /app
RUN apk update
RUN apk add coreutils binutils findutils grep
COPY --from=0 /go/server /app/server
EXPOSE 8888
ENTRYPOINT ["./server"]
-59
View File
@@ -1,59 +0,0 @@
# Binary Environment Examples
The `binary` runtime is a go server that uses a subprocess to invoke executables or execute shell scripts.
Use Cases
- Execute bash scripts
- Execute arbitrary binaries (such as common sysadmin tools)
- Get support in _any_ programming language by executing the generated executable.
⚠️ **Words of Caution** ⚠️
The environment runs on an alpine image with some additional utility command line tools installed, such as 'grep'.
However, in case you want to make use of more esoteric command line tools, you should add the relevant apk to the
Dockerfile and build a new binary environment. See 'Compiling' for instructions.
When executing functions using binaries, **ensure that the executable is built for the right architecture**.
Using the default binary environment this means that the binary should be build for Linux.
Looking for ready-to-run examples? See the [binary examples directory](../../examples/binary).
## Usage
To get started with the latest binary environment:
```bash
fission env create --name binary --image fission/binary-env --builder fission/binary-builder
```
The interface to the executable used by this environment is somewhat similar to a [CGI interface](https://en.wikipedia.org/wiki/Common_Gateway_Interface).
This means that any HTTP headers are converted to environment variables of the form "HTTP_<header-name>". For example these
are some of frequently occurring headers:
```bash
# Request Metadata
CONTENT_LENGTH
REQUEST_URI
REQUEST_METHOD
# HTTP Headers
HTTP_ACCEPT
HTTP_USER-AGENT
HTTP_CONTENT-TYPE
# ...
```
The body of HTTP piped over the STDIN to the executable.
All output that is provided to the server over the STDOUT will be transformed into the HTTP response.
## Compiling
To build the runtime environment:
```bash
docker build --tag=${USER}/binary-env .
```
To build the builder environment:
```bash
(cd builder/ && docker build --tag=${USER}/binary-builder .)
```
-4
View File
@@ -1,4 +0,0 @@
ARG BUILDER_IMAGE=fission/builder:latest
FROM ${BUILDER_IMAGE}
ADD build.sh /usr/local/bin/build
-13
View File
@@ -1,13 +0,0 @@
#!/bin/sh
apk update
CWD=$(pwd)
if [ -f ${SRC_PKG}/build.sh ]; then
cd ${SRC_PKG}
./build.sh
cd ${CWD}
fi
cp -rf ${SRC_PKG} ${DEPLOY_PKG}
-45
View File
@@ -1,45 +0,0 @@
package main
import (
"fmt"
"strings"
)
// Utility functions for working with environment variables
type Env struct {
Vars []*EnvVar
}
type EnvVar struct {
Key string
Val string
}
func FromString(rawEnvVar string) *EnvVar {
parts := strings.SplitN(rawEnvVar, "=", 2)
return &EnvVar{parts[0], parts[1]}
}
func (ev *EnvVar) ToString() string {
return fmt.Sprintf("%s=%s", ev.Key, ev.Val)
}
func (e *Env) SetEnv(envVar *EnvVar) {
e.Vars = append(e.Vars, envVar)
}
func (e *Env) ToStringEnv() []string {
var result []string
for _, envVar := range e.Vars {
result = append(result, envVar.ToString())
}
return result
}
func NewEnv(stringEnv []string) *Env {
env := &Env{}
for _, rawEnvVar := range stringEnv {
env.SetEnv(FromString(rawEnvVar))
}
return env
}
-178
View File
@@ -1,178 +0,0 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
)
const (
DEFAULT_CODE_PATH = "/userfunc/user"
DEFAULT_INTERNAL_CODE_PATH = "/bin/userfunc"
)
var specialized bool
type (
BinaryServer struct {
fetchedCodePath string
internalCodePath string
}
FunctionLoadRequest struct {
// FilePath is an absolute filesystem path to the
// function. What exactly is stored here is
// env-specific. Optional.
FilePath string `json:"filepath"`
// FunctionName has an environment-specific meaning;
// usually, it defines a function within a module
// containing multiple functions. Optional; default is
// environment-specific.
FunctionName string `json:"functionName"`
// URL to expose this function at. Optional; defaults
// to "/".
URL string `json:"url"`
}
)
func (bs *BinaryServer) SpecializeHandler(w http.ResponseWriter, r *http.Request) {
if specialized {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Not a generic container"))
return
}
request := FunctionLoadRequest{}
codePath := bs.fetchedCodePath
err := json.NewDecoder(r.Body).Decode(&request)
switch {
case err == io.EOF:
case err != nil:
panic(err)
}
if request.FilePath != "" {
fileStat, err := os.Stat(request.FilePath)
if err != nil {
panic(err)
}
codePath = request.FilePath
switch mode := fileStat.Mode(); {
case mode.IsDir():
codePath = filepath.Join(request.FilePath, request.FunctionName)
}
}
_, err = os.Stat(codePath)
if err != nil {
if os.IsNotExist(err) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(codePath + ": not found"))
return
} else {
panic(err)
}
}
// Future: Check if executable is correct architecture/executable.
// Copy the executable to ensure that file is executable and immutable.
userFunc, err := ioutil.ReadFile(codePath)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Failed to read executable."))
return
}
err = ioutil.WriteFile(bs.internalCodePath, userFunc, 0555)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Failed to write executable to target location."))
return
}
fmt.Println("Specializing ...")
specialized = true
fmt.Println("Done")
}
func (bs *BinaryServer) InvocationHandler(w http.ResponseWriter, r *http.Request) {
if !specialized {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Generic container: no requests supported"))
return
}
// CGI-like passing of environment variables
execEnv := NewEnv(nil)
execEnv.SetEnv(&EnvVar{"REQUEST_METHOD", r.Method})
execEnv.SetEnv(&EnvVar{"REQUEST_URI", r.RequestURI})
execEnv.SetEnv(&EnvVar{"CONTENT_LENGTH", fmt.Sprintf("%d", r.ContentLength)})
for header, val := range r.Header {
execEnv.SetEnv(&EnvVar{fmt.Sprintf("HTTP_%s", strings.ToUpper(header)), val[0]})
}
// Future: could be improved by keeping subprocess open while environment is specialized
cmd := exec.Command(bs.internalCodePath)
cmd.Env = execEnv.ToStringEnv()
if r.ContentLength != 0 {
fmt.Println(r.ContentLength)
stdin, err := cmd.StdinPipe()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Failed to get STDIN pipe: %s", err)))
panic(err)
}
_, err = io.Copy(stdin, r.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Failed to pipe input: %s", err)))
}
stdin.Close()
}
out, err := cmd.Output()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Function error: %s", err)))
return
}
w.WriteHeader(http.StatusOK)
w.Write(out)
}
func main() {
codePath := flag.String("c", DEFAULT_CODE_PATH, "Path to expected fetched executable.")
internalCodePath := flag.String("i", DEFAULT_INTERNAL_CODE_PATH, "Path to specialized executable.")
flag.Parse()
absInternalCodePath, err := filepath.Abs(*internalCodePath)
if err != nil {
panic(err)
}
fmt.Printf("Using fetched code path: %s\n", *codePath)
fmt.Printf("Using internal code path: %s\n", absInternalCodePath)
server := &BinaryServer{*codePath, absInternalCodePath}
http.HandleFunc("/", server.InvocationHandler)
http.HandleFunc("/specialize", server.SpecializeHandler)
http.HandleFunc("/v2/specialize", server.SpecializeHandler)
fmt.Println("Listening on 8888 ...")
err = http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
-6
View File
@@ -1,6 +0,0 @@
bin/*
obj/*
out/*
.vscode/*
project.lock*
TODO_dotnet
-15
View File
@@ -1,15 +0,0 @@
FROM microsoft/dotnet:1.1-sdk AS builder
COPY * /proj/
RUN cd /proj && ./project-build.sh
# Build env image
FROM microsoft/dotnet:1.1.0-runtime
WORKDIR /fission-workdir
COPY --from=builder /proj/out .
EXPOSE 8888
ENTRYPOINT ["dotnet"]
CMD ["fission-dotnet.dll"]
-82
View File
@@ -1,82 +0,0 @@
using Fission.DotNetCore.Compiler;
using Fission.DotNetCore.Api;
using System.Collections.Generic;
using Nancy;
using System.IO;
using System;
namespace Fission.DotNetCore
{
public class ExecutorModule : NancyModule
{
#if DEBUG
private const string CODE_PATH = "/tmp/func.cs";
#else
private const string CODE_PATH = "/userfunc/user";
#endif
private static Function _userFunc;
private static Logger _logger = new Logger();
public ExecutorModule()
{
Post("/specialize", args => Specialize());
Get("/", _ => Run());
Post("/", _ => Run());
Put("/", _ => Run());
Head("/", _ => Run());
Options("/", _ => Run());
Delete("/", _ => Run());
}
private object Specialize()
{
var errors = new List<string>();
if (File.Exists(CODE_PATH))
{
var code = File.ReadAllText(CODE_PATH);
_userFunc = FissionCompiler.Compile(code, out errors);
if (_userFunc == null)
{
var errstr = string.Join(Environment.NewLine, errors);
_logger.WriteError(errstr);
var response = (Response)errstr;
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
return null;
}
else
{
var errstr = $"Unable to locate code at '{CODE_PATH}'";
_logger.WriteError(errstr);
var response = (Response)errstr;
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
}
private object Run()
{
if (_userFunc == null)
{
var response = (Response)"Generic container: no requests supported";
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
try
{
return _userFunc.Invoke(FissionContext.Build(Request, new Logger()));
}
catch (Exception e)
{
_logger.WriteError(e.ToString());
var response = (Response)e.Message;
response.StatusCode = HttpStatusCode.BadRequest;
return response;
}
}
}
}
-75
View File
@@ -1,75 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
namespace Fission.DotNetCore.Compiler
{
//adapted from this article http://www.tugberkugurlu.com/archive/compiling-c-sharp-code-into-memory-and-executing-it-with-roslyn
class FissionCompiler
{
public static Function Compile(string code, out List<string> errors)
{
errors = new List<string>();
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
string assemblyName = Path.GetRandomFileName();
var coreDir = Directory.GetParent(typeof(Enumerable).GetTypeInfo().Assembly.Location);
List<MetadataReference> references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "mscorlib.dll"),
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.Serialization.Json.DataContractJsonSerializer).GetTypeInfo().Assembly.Location)
};
foreach (var referencedAssembly in Assembly.GetEntryAssembly().GetReferencedAssemblies())
{
var assembly = Assembly.Load(referencedAssembly);
references.Add(MetadataReference.CreateFromFile(assembly.Location));
}
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release));
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error).ToList();
foreach (Diagnostic diagnostic in failures)
{
errors.Add($"{diagnostic.Id}: {diagnostic.GetMessage()}");
}
}
else
{
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(ms);
var type = assembly.GetType("FissionFunction");
var info = type.GetMember("Execute").First() as MethodInfo;
return new Function(assembly, type, info);
}
}
return null;
}
}
}
-114
View File
@@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Nancy;
namespace Fission.DotNetCore.Api
{
public class FissionContext
{
public FissionContext(Dictionary<string, object> args, Logger logger, FissionHttpRequest request)
{
if (args == null) throw new ArgumentNullException(nameof(args));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (request == null) throw new ArgumentNullException(nameof(request));
Arguments = args;
Logger = logger;
Request = request;
}
public Dictionary<string, object> Arguments { get; private set; }
public FissionHttpRequest Request { get; private set; }
public Logger Logger { get; private set; }
public static FissionContext Build(Request request, Logger logger)
{
return new FissionContext(((DynamicDictionary)request.Query).ToDictionary(),
logger,
new FissionHttpRequest(request));
}
}
public class Logger
{
public void Write(Severity severity, string format, params object[] args)
{
Console.WriteLine($"{DateTime.Now.ToString("MM/dd/yy H:mm:ss zzz")} {severity}: " + format, args);
}
public void WriteInfo(string format, params object[] args)
{
Write(Severity.Info, format, args);
}
public void WriteWarning(string format, params object[] args)
{
Write(Severity.Warning, format, args);
}
public void WriteError(string format, params object[] args)
{
Write(Severity.Error, format, args);
}
public void WriteCritical(string format, params object[] args)
{
Write(Severity.Critical, format, args);
}
public void WriteVerbose(string format, params object[] args)
{
Write(Severity.Verbose, format, args);
}
}
public enum Severity
{
Info,
Warning,
Error,
Critical,
Verbose
}
public class FissionHttpRequest
{
private readonly Request _request;
internal FissionHttpRequest(Request request)
{
if (request == null) throw new ArgumentNullException(nameof(request));
_request = request;
}
public Stream Body { get { return _request.Body; } }
public string BodyAsString()
{
int length = (int)_request.Body.Length;
byte[] data = new byte[length];
_request.Body.Read(data, 0, length);
return Encoding.UTF8.GetString(data);
}
public Dictionary<string, IEnumerable<string>> Headers
{
get
{
var headers = new Dictionary<string, IEnumerable<string>>();
foreach (var kv in _request.Headers)
{
headers.Add(kv.Key, kv.Value);
}
return headers;
}
}
public X509Certificate Certificate { get { return _request.ClientCertificate; } }
public string Url { get { return _request.Url.ToString(); } }
public string Method { get { return _request.Method; } }
}
}
-28
View File
@@ -1,28 +0,0 @@
using System;
using System.Reflection;
using Fission.DotNetCore.Api;
namespace Fission.DotNetCore.Compiler
{
class Function
{
private readonly Assembly _assembly;
private readonly Type _type;
private readonly MethodInfo _info;
public Function(Assembly assembly, Type type, MethodInfo info)
{
if (info == null) throw new ArgumentNullException(nameof(info));
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
if (type == null) throw new ArgumentNullException(nameof(type));
_assembly = assembly;
_type = type;
_info = info;
}
public object Invoke(FissionContext context)
{
return _info.Invoke(_assembly.CreateInstance(_type.FullName), new[] { context });
}
}
}
-21
View File
@@ -1,21 +0,0 @@
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Nancy.Owin;
namespace Fission.DotNetCore
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
.UseUrls("http://*:8888")
.Configure(app => app.UseOwin(x => x.UseNancy()))
.Build();
host.Run();
}
}
}
-270
View File
@@ -1,270 +0,0 @@
# Fission: dotnet C# Environment
This is a simple dotnet C# environment for Fission.
It's a Docker image containing the dotnet 1.1.0 runtime. The image
uses Kestrel with Nancy to host the internal web server and uses
Roslyn to compile the uploaded code.
The image supports compiling and running code with types defined in
mscorlib and does not at present support other library references.
One workaround for this would be to add the references to this project's
project.json file and rebuild the container.
The environment works via convention where you create a C# class
called FissionFunction which has a method named Execute taking a single
parameter, a FissionContext object.
The FissionContext object gives access to the arguments and other items
like logging. Please see FissionContext.cs for public API.
Example of simplest possible class to be executed:
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction {
public string Execute(FissionContext context) {
return null;
}
}
```
Please see examples below, or if you are looking for ready-to-run examples, see
the [DotNet examples directory](../../examples/dotnet).
## Rebuilding and pushing the image
To rebuild the image you will have to install Docker with version higher than 17.05+
in order to support multi-stage builds feature.
### Rebuild containers
Move to the directory containing the source and start the container build process:
```
docker build -t USER/dotnet-env .
```
After the build finishes push the new image to a Docker registry using the
standard procedure.
## Echo example
### Setup fission environment
First you need to setup the fission according to your cluster setup as
specified here: https://github.com/fission/fission
### Create the class to run
Secondly you need to create a file /tmp/func.cs containing the following code:
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context){
context.Logger.WriteInfo("executing.. {0}", context.Arguments["text"]);
return (string)context.Arguments["text"];
}
}
```
### Run the example
Lastly to run the example:
```
$ fission env create --name dotnet --image fission/dotnet-env
$ fission function create --name echo --env dotnet --code /tmp/func.cs
$ fission route create --method GET --url /echo --function echo
$ curl http://$FISSION_ROUTER/echo?text=hello%20world!
hello world
```
## Addition service example
### Setup fission environment
First you need to setup the fission according to your cluster setup as
specified here: https://github.com/fission/fission
### Create the class to run
Secondly you need to create a file /tmp/func.cs containing the following code:
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context){
var x = Convert.ToInt32(context.Arguments["x"]);
var y = Convert.ToInt32(context.Arguments["y"]);
return (x+y).ToString();
}
}
```
### Run the example
Lastly to run the example:
```
$ fission env create --name dotnet --image fission/dotnet-env
$ fission function create --name addition --env dotnet --code /tmp/func.cs
$ fission route create --method GET --url /add --function addition
$ curl "http://$FISSION_ROUTER/add?x=30&y=12"
42
```
## Accessing http request information example
### Setup fission environment
First you need to setup the fission according to your cluster setup as
specified here: https://github.com/fission/fission
### Create the class to run
Secondly you need to create a file /tmp/func.cs containing the following code:
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context){
var buffer = new System.Text.StringBuilder();
foreach(var header in context.Request.Headers){
buffer.AppendLine(header.Key);
foreach(var item in header.Value){
buffer.AppendLine($"\t{item}");
}
}
buffer.AppendLine($"Url: {context.Request.Url}, method: {context.Request.Method}");
return buffer.ToString();
}
}
```
### Run the example
Lastly to run the example:
```
$ fission env create --name dotnet --image fission/dotnet-env
$ fission function create --name httpinfo --env dotnet --code /tmp/func.cs
$ fission route create --method GET --url /http_info --function httpinfo
$ curl "http://$FISSION_ROUTER/http_info"
Accept
*/*;q=1
Host
fissionserver:8888
User-Agent
curl/7.47.0
Url: http://fissionserver:8888, method: GET
```
## Accessing http request body example
### Setup fission environment
First you need to setup the fission according to your cluster setup as
specified here: https://github.com/fission/fission
### Create the class to run
Secondly you need to create a file /tmp/func.cs containing the following code:
```
using System.IO;
using System.Runtime.Serialization.Json;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context)
{
var person = Person.Deserialize(context.Request.Body);
return $"Hello, my name is {person.Name} and I am {person.Age} years old.";
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public static Person Deserialize(Stream json)
{
var serializer = new DataContractJsonSerializer(typeof(Person));
return (Person)serializer.ReadObject(json);
}
}
```
### Run the example
Lastly to run the example:
```
$ fission env create --name dotnet --image fission/dotnet-env
$ fission function create --name httpbody --env dotnet --code /tmp/func.cs
$ fission route create --method GET --url /http_body --function httpbody
$ curl -XPOST "http://$FISSION_ROUTER/http_body" -d '{ "Name":"Arthur", "Age":42}'
Hello, my name is Arthur and I am 42 years old.
```
## Developing/debugging the enviroment locally
The easiest way to debug the environment is to open the directory in
Visual Studio Code (VSCode) as that will setup debugger for you the
first time.
Remember to install the excellent extension
"C# for Visual Studio Code(powered by OmniSharp)" to get statement completion
The class ExecutorModule contain preprocessor directive overriding where
the input code file should be found:
```
#if DEBUG
private const string CODE_PATH = "/tmp/func.cs";
#else
private const string CODE_PATH = "/userfunc/user";
#endif
```
So what you need to do is:
1. Open the directory in VSCode.
This will prompt restore of packages and query is debugger setup is needed. Accept both prompts.
2. Press F5 to start the web server. Set breakpoints etc..
3. Add a code file containing valid C# at /tmp/func.cs
4. Specialize the service with curl via post
```
$ curl -XPOST http://localhost:8888/specialize
```
5. Call your function with curl
```
$ curl -XGET http://localhost:8888
```
-21
View File
@@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
<AssemblyName>fission-dotnet</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>dotnet</PackageId>
<PackageTargetFallback>$(PackageTargetFallback);netstandard1.3</PackageTargetFallback>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Owin" Version="1.0.2" />
<PackageReference Include="Nancy" Version="2.0.0-barneyrubble" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="1.3.0-*" />
<PackageReference Include="System.Runtime.Loader" Version="4.0.0-*" />
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.0.2" />
</ItemGroup>
</Project>
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
dotnet restore fission-dotnet.csproj
dotnet publish fission-dotnet.csproj -c Release -o out
-1
View File
@@ -1 +0,0 @@
builder
-15
View File
@@ -1,15 +0,0 @@
FROM microsoft/dotnet:2.0.0-sdk AS builder
COPY * /proj/
RUN cd /proj && ./project-build.sh
# Build env image
FROM microsoft/dotnet:2.0-runtime
WORKDIR /fission-workdir
COPY --from=builder /proj/out .
EXPOSE 8888
ENTRYPOINT ["dotnet"]
CMD ["fission-dotnet20.dll"]
-179
View File
@@ -1,179 +0,0 @@
using Fission.DotNetCore.Compiler;
using Fission.DotNetCore.Api;
using System.Collections.Generic;
using Nancy;
using System.IO;
using System;
using Nancy.IO;
using Fission.DotNetCore.Utilty;
using Fission.DotNetCore.Model;
using Nancy.Extensions;
namespace Fission.DotNetCore
{
public class ExecutorModule : NancyModule
{
private static string PackagePath = string.Empty;
#if DEBUG
private const string CODE_PATH = "/tmp/func.cs";
#else
private const string CODE_PATH = "/userfunc/user";
#endif
private static Function _userFunc;
private static Logger _logger = new Logger();
public ExecutorModule()
{
Post("/specialize", args => Specialize());
Post("/v2/specialize", args => Specializev2());
Get("/", _ => Run());
Post("/", _ => Run());
Put("/", _ => Run());
Head("/", _ => Run());
Options("/", _ => Run());
Delete("/", _ => Run());
}
private object Specializev2()
{
Console.WriteLine("Call Reached at /v2/specialize");
try
{
var errors = new List<string>();
var oinfo = new List<string>();
var _request = Request;
var _body = Request.Body;
// Request.Body.Position = 0; use it only if request has already been read before that
var _requestBodystring = RequestStream.FromStream(Request.Body).AsString();
Console.WriteLine($"Request received by endpoint from builder : {_requestBodystring}");
BuilderRequest builderRequest = EnvironmentHelper.Instance.GetBuilderRequest(_requestBodystring);
if (builderRequest == null)
{
Console.WriteLine("Error : Unable to parse builder request!!");
throw new Exception("Error : Unable to parse builder request!!");
}
string functionPath = string.Empty;
// functionPath = Path.Combine(builderRequest.filepath, $"{builderRequest.functionName}.cs");
PackagePath = builderRequest.filepath;
//following will enable us to skip --entrypoint flag during function creation
if (!string.IsNullOrWhiteSpace(builderRequest.functionName))
{
functionPath = Path.Combine(builderRequest.filepath, $"{builderRequest.functionName}.cs");
}
else
{
functionPath = Path.Combine(builderRequest.filepath, EnvironmentHelper.Instance.environmentSettings.functionBodyFileName);
}
Console.WriteLine($"Going to read function body from path : {functionPath}");
if (File.Exists(functionPath))
{
var code = File.ReadAllText(functionPath);
try
{
FissionCompiler fissionCompiler = new FissionCompiler(builderRequest.filepath);
_userFunc = fissionCompiler.Compilev2(code, out errors, out oinfo);
}
catch (Exception ex)
{
Console.WriteLine($"Error getting _userFunc :{ex.Message} , Trace : {ex.StackTrace}");
}
if (_userFunc == null)
{
var errstr = string.Join(Environment.NewLine, errors);
_logger.WriteError(errstr);
Console.WriteLine($"Error _userFunc is null :{errstr}");
var response = (Response)errstr;
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
else
{
//try to retrun few details
var infostr = string.Join(Environment.NewLine, oinfo);
_logger.WriteInfo(infostr);
var response = (Response)infostr;
response.StatusCode = HttpStatusCode.OK;
return response;
}
}
else
{
var errstr = $"Unable to locate code at '{functionPath}'";
_logger.WriteError(errstr);
var response = (Response)errstr;
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception occurred {ex.Message} | {ex.StackTrace}");
var errstr = $"Exception occurred {ex.Message} | {ex.StackTrace}";
_logger.WriteError(errstr);
var response = (Response)errstr;
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
}
private object Specialize()
{
var errors = new List<string>();
if (File.Exists(CODE_PATH))
{
var code = File.ReadAllText(CODE_PATH);
_userFunc = FissionCompiler.Compile(code, out errors);
if (_userFunc == null)
{
var errstr = string.Join(Environment.NewLine, errors);
_logger.WriteError(errstr);
var response = (Response)errstr;
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
return null;
}
else
{
var errstr = $"Unable to locate code at '{CODE_PATH}'";
_logger.WriteError(errstr);
var response = (Response)errstr;
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
}
private object Run()
{
if (_userFunc == null)
{
var response = (Response)"Generic container: no requests supported";
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
try
{
var context = FissionContext.Build(Request, new Logger());
//set the package path ,as that will be required to get appsetting files from package
context.PackagePath = PackagePath;
return _userFunc.Invoke(context);
}
catch (Exception e)
{
_logger.WriteError(e.ToString());
var response = (Response)e.Message;
response.StatusCode = HttpStatusCode.BadRequest;
return response;
}
}
}
}
-224
View File
@@ -1,224 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Fission.DotNetCore.Model;
using Fission.DotNetCore.Utilty;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
namespace Fission.DotNetCore.Compiler
{
//adapted from this article http://www.tugberkugurlu.com/archive/compiling-c-sharp-code-into-memory-and-executing-it-with-roslyn
class FissionCompiler
{
string packagepath = string.Empty;
FunctionSpecification functionSpecification = null;
public FissionCompiler(string _packagePath)
{
this.packagepath = _packagePath;
}
public static Function Compile(string code, out List<string> errors)
{
errors = new List<string>();
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
string assemblyName = Path.GetRandomFileName();
var coreDir = Directory.GetParent(typeof(Enumerable).GetTypeInfo().Assembly.Location);
List<MetadataReference> references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "mscorlib.dll"),
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.Serialization.Json.DataContractJsonSerializer).GetTypeInfo().Assembly.Location)
};
foreach (var referencedAssembly in Assembly.GetEntryAssembly().GetReferencedAssemblies())
{
var assembly = Assembly.Load(referencedAssembly);
references.Add(MetadataReference.CreateFromFile(assembly.Location));
}
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release));
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error).ToList();
foreach (Diagnostic diagnostic in failures)
{
errors.Add($"{diagnostic.Id}: {diagnostic.GetMessage()}");
}
}
else
{
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(ms);
//support for Namespace , as well as backward compatibility for existing functions
var type = assembly.GetTypes().FirstOrDefault(x => x.Name.EndsWith("FissionFunction"));
var info = type.GetMember("Execute").First() as MethodInfo;
return new Function(assembly, type, info);
}
}
return null;
}
public Function Compilev2(string code, out List<string> errors, out List<string> oinfo)
{
errors = new List<string>();
oinfo = new List<string>();
#region syntext tree and default reference build
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
string assemblyName = Path.GetRandomFileName();
var coreDir = Directory.GetParent(typeof(Enumerable).GetTypeInfo().Assembly.Location);
Console.WriteLine("Adding core references !!");
List<MetadataReference> references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "mscorlib.dll"),
MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "netstandard.dll"),
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.Serialization.Json.DataContractJsonSerializer).GetTypeInfo().Assembly.Location)
};
Console.WriteLine("Adding parent assembly based references !!");
foreach (var referencedAssembly in Assembly.GetEntryAssembly().GetReferencedAssemblies())
{
var assembly = Assembly.Load(referencedAssembly);
references.Add(MetadataReference.CreateFromFile(assembly.Location));
}
#endregion
#region load function specs based dlls
Console.WriteLine($"going to get function specification...");
//load all available dlls from deployment folder in dllinfo object
functionSpecification = EnvironmentHelper.Instance.GetFunctionSpecs(packagepath);
Console.WriteLine($"going to get package dlls...");
//iterate and all all libraries mentioned
foreach (var library in functionSpecification.libraries)
{
string dllCompletePath = Path.Combine(packagepath, library.path).GetrelevantPathAsPerOS();
references.Add(MetadataReference.CreateFromFile(dllCompletePath));
Console.WriteLine($"referred folder based dll : {dllCompletePath} from package {library.nugetPackage}");
}
Console.WriteLine($"referred all available dlls!!");
oinfo.Add("referred all available dlls!!");
#endregion
#region dynamic resolve handler registration
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
#endregion
#region function compile
Console.WriteLine($"Trying to Compile");
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release));
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
Console.WriteLine($"Compile Failed , see pod logs for more details");
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error).ToList();
foreach (Diagnostic diagnostic in failures)
{
errors.Add($"{diagnostic.Id}: {diagnostic.GetMessage()}");
Console.WriteLine($"COMPILE ERROR :{diagnostic.Id}: {diagnostic.GetMessage()}", "ERROR");
}
}
else
{
oinfo.Add("COMPILE SUCCESS!!");
Console.WriteLine($"COMPILE SUCCESS!!");
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(ms);
//var type = assembly.GetType("FissionFunction");
//support for Namespace , as well as backward compatibility for existing functions
var type = assembly.GetTypes().FirstOrDefault(x => x.Name.EndsWith("FissionFunction"));
//assembly.GetTypes().Where(x=>x.Name.ToLower().EndsWith("FissionFunction".ToLower())).FirstOrDefault();
var info = type.GetMember("Execute").First() as MethodInfo;
return new Function(assembly, type, info);
}
}
return null;
#endregion
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//This handler is called only when the common language runtime tries to bind to the assembly and fails.
Console.WriteLine($"Dynamically trying to load dll {(args.Name.Substring(0, args.Name.IndexOf(",")).ToString() + ".dll").ToLower()} in parent assembaly");
//Retrieve the list of referenced assemblies in an array of AssemblyName.
Assembly MyAssembly = null, objExecutingAssemblies;
string strTempAssmbPath_relative = "", strTempAssmbPath_absolute = "";
objExecutingAssemblies = Assembly.GetExecutingAssembly();
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
////Loop through the array of referenced assembly names.
//load all available dlls from deployment folder in dllinfo object
if (functionSpecification.libraries.Any(x => x.name.ToLower() == (args.Name.Substring(0, args.Name.IndexOf(",")).ToString() + ".dll").ToLower()))
{
strTempAssmbPath_relative = functionSpecification.libraries.Where(x => x.name.ToLower() == (args.Name.Substring(0, args.Name.IndexOf(",")).ToString() + ".dll").ToLower()).FirstOrDefault().path;
strTempAssmbPath_absolute = Path.Combine(packagepath, strTempAssmbPath_relative);
Console.WriteLine($"loading dll in parent assembly :{strTempAssmbPath_absolute.GetrelevantPathAsPerOS()}");
//Load the assembly from the specified path.
MyAssembly = Assembly.LoadFile(strTempAssmbPath_absolute.GetrelevantPathAsPerOS());
Console.WriteLine($"Load success for :{strTempAssmbPath_absolute.GetrelevantPathAsPerOS()}");
}
if (MyAssembly == null)
{
Console.WriteLine($"WARNING !!! unabel to locate dll :{(args.Name.Substring(0, args.Name.IndexOf(",")).ToString() + ".dll").ToLower()} ", "WARNING");
}
//Return the loaded assembly.
return MyAssembly;
}
}
}
-130
View File
@@ -1,130 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Nancy;
using Newtonsoft.Json;
namespace Fission.DotNetCore.Api
{
public class FissionContext
{
public string PackagePath { get; set; }
public FissionContext(Dictionary<string, object> args, Logger logger, FissionHttpRequest request)
{
if (args == null) throw new ArgumentNullException(nameof(args));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (request == null) throw new ArgumentNullException(nameof(request));
Arguments = args;
Logger = logger;
Request = request;
}
public Dictionary<string, object> Arguments { get; private set; }
public FissionHttpRequest Request { get; private set; }
public Logger Logger { get; private set; }
public static FissionContext Build(Request request, Logger logger)
{
return new FissionContext(((DynamicDictionary)request.Query).ToDictionary(),
logger,
new FissionHttpRequest(request));
}
//this is to support aditional setting file read from source/deployment package via function
public T GetSettings<T>(string relativePath)
{
var filePath = Path.Combine(this.PackagePath, relativePath);
Console.WriteLine($"Going to Get Setting from :{filePath}");
string json = GetSettingsJson(filePath);
return JsonConvert.DeserializeObject<T>(json);
}
private string GetSettingsJson(string relativePath)
{
return File.ReadAllText(Path.Combine(this.PackagePath, relativePath));
}
}
public class Logger
{
public void Write(Severity severity, string format, params object[] args)
{
Console.WriteLine($"{DateTime.Now.ToString("MM/dd/yy H:mm:ss zzz")} {severity}: " + format, args);
}
public void WriteInfo(string format, params object[] args)
{
Write(Severity.Info, format, args);
}
public void WriteWarning(string format, params object[] args)
{
Write(Severity.Warning, format, args);
}
public void WriteError(string format, params object[] args)
{
Write(Severity.Error, format, args);
}
public void WriteCritical(string format, params object[] args)
{
Write(Severity.Critical, format, args);
}
public void WriteVerbose(string format, params object[] args)
{
Write(Severity.Verbose, format, args);
}
}
public enum Severity
{
Info,
Warning,
Error,
Critical,
Verbose
}
public class FissionHttpRequest
{
private readonly Request _request;
internal FissionHttpRequest(Request request)
{
if (request == null) throw new ArgumentNullException(nameof(request));
_request = request;
}
public Stream Body { get { return _request.Body; } }
public string BodyAsString()
{
int length = (int)_request.Body.Length;
byte[] data = new byte[length];
_request.Body.Read(data, 0, length);
return Encoding.UTF8.GetString(data);
}
public Dictionary<string, IEnumerable<string>> Headers
{
get
{
var headers = new Dictionary<string, IEnumerable<string>>();
foreach (var kv in _request.Headers)
{
headers.Add(kv.Key, kv.Value);
}
return headers;
}
}
public X509Certificate Certificate { get { return _request.ClientCertificate; } }
public string Url { get { return _request.Url.ToString(); } }
public string Method { get { return _request.Method; } }
}
}
-28
View File
@@ -1,28 +0,0 @@
using System;
using System.Reflection;
using Fission.DotNetCore.Api;
namespace Fission.DotNetCore.Compiler
{
class Function
{
private readonly Assembly _assembly;
private readonly Type _type;
private readonly MethodInfo _info;
public Function(Assembly assembly, Type type, MethodInfo info)
{
if (info == null) throw new ArgumentNullException(nameof(info));
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
if (type == null) throw new ArgumentNullException(nameof(type));
_assembly = assembly;
_type = type;
_info = info;
}
public object Invoke(FissionContext context)
{
return _info.Invoke(_assembly.CreateInstance(_type.FullName), new[] { context });
}
}
}
@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Fission.DotNetCore.Model
{
public class BuilderRequest
{
/// <summary>
/// this is folder path of deployment package which contains all deployment content copied to env
/// </summary>
public string filepath { get; set; }
public string functionName { get; set; }
public string url { get; set; }
public FunctionMetadata FunctionMetadata { get; set; }
}
public class FunctionMetadata
{
public string name { get; set; }
public string @namespace { get; set; }
public string selfLink { get; set; }
public string uid { get; set; }
public string resourceVersion { get; set; }
public int generation { get; set; }
public DateTime creationTimestamp { get; set; }
}
}
-16
View File
@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Fission.DotNetCore.Model
{
public class DllInfo
{
public string name { get; set; }
public string rootPackage { get; set; }
public string framework { get; set; }
public string processor { get; set; }
public string path { get; set; }
}
}
@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Fission.DotNetCore.Model
{
public class EnvironmentSettings
{
public string LogDirectory { get; set; }
public string DllDirectory { get; set; }
public string functionBodyFileName { get; set; }
public string functionSpecFileName { get; set; }
public bool RunningOnwindows { get; set; }
}
}
@@ -1,37 +0,0 @@
using Fission.DotNetCore.Model;
using System;
using System.Collections.Generic;
using System.Text;
namespace Fission.DotNetCore.Model
{
public class FunctionSpecification
{
public FunctionSpecification()
{
this.libraries = new List<Library>();
}
public string functionName { get; set; }
public List<Library> libraries { get; set; }
public string hash { get; set; }
public string certificatePath { get; set; }
}
public class Library
{
public Library()
{
}
public Library(DllInfo dllInfo)
{
this.name = dllInfo.name;
this.nugetPackage = dllInfo.rootPackage;
this.path = dllInfo.path;
}
public string name { get; set; }
//public string version { get; set; }
public string path { get; set; }
public string nugetPackage { get; set; }
}
}
-21
View File
@@ -1,21 +0,0 @@
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Nancy.Owin;
namespace Fission.DotNetCore
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
.UseUrls("http://*:8888")
.Configure(app => app.UseOwin(x => x.UseNancy()))
.Build();
host.Run();
}
}
}
-365
View File
@@ -1,365 +0,0 @@
# Fission: dotnet 2.0 C# Environment
This is a simple dotnet core 2.0 C# environment for Fission.
It's a Docker image containing the dotnet 2.0.0 runtime. The image
uses Kestrel with Nancy to host the internal web server and uses
Roslyn to compile the uploaded code.
The image supports compiling and running code with types defined in
mscorlib and does not at present support other library references.
One workaround for this would be to add the references to this project's
project.json file and rebuild the container.
The environment works via convention where you create a C# class
called FissionFunction which has a method named Execute taking a single
parameter, a FissionContext object.
The FissionContext object gives access to the arguments and other items
like logging. Please see FissionContext.cs for public API.
Example of simplest possible class to be executed:
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction {
public string Execute(FissionContext context) {
return null;
}
}
```
Please see examples below, or if you are looking for ready-to-run examples, see
the [DotNet20 examples directory](../../examples/dotnet20).
## Rebuilding and pushing the image
To rebuild the image you will have to install Docker with version higher than 17.05+
in order to support multi-stage builds feature.
### Rebuild containers
Move to the directory containing the source and start the container build process:
```
docker build -t USER/dotnet20-env .
```
After the build finishes push the new image to a Docker registry using the
standard procedure.
## Echo example
### Setup fission environment
First you need to setup the fission according to your cluster setup as
specified here: https://github.com/fission/fission
### Create the class to run
Secondly you need to create a file /tmp/func.cs containing the following code:
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context){
context.Logger.WriteInfo("executing.. {0}", context.Arguments["text"]);
return (string)context.Arguments["text"];
}
}
```
### Run the example
Lastly to run the example:
```
$ fission env create --name dotnet --image fission/dotnet20-env
$ fission function create --name echo --env dotnet --code /tmp/func.cs
$ fission route create --method GET --url /echo --function echo
$ curl http://$FISSION_ROUTER/echo?text=hello%20world!
hello world
```
## Addition service example
### Setup fission environment
First you need to setup the fission according to your cluster setup as
specified here: https://github.com/fission/fission
### Create the class to run
Secondly you need to create a file /tmp/func.cs containing the following code:
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context){
var x = Convert.ToInt32(context.Arguments["x"]);
var y = Convert.ToInt32(context.Arguments["y"]);
return (x+y).ToString();
}
}
```
### Run the example
Lastly to run the example:
```
$ fission env create --name dotnet --image fission/dotnet20-env
$ fission function create --name addition --env dotnet --code /tmp/func.cs
$ fission route create --method GET --url /add --function addition
$ curl "http://$FISSION_ROUTER/add?x=30&y=12"
42
```
## Accessing http request information example
### Setup fission environment
First you need to setup the fission according to your cluster setup as
specified here: https://github.com/fission/fission
### Create the class to run
Secondly you need to create a file /tmp/func.cs containing the following code:
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context){
var buffer = new System.Text.StringBuilder();
foreach(var header in context.Request.Headers){
buffer.AppendLine(header.Key);
foreach(var item in header.Value){
buffer.AppendLine($"\t{item}");
}
}
buffer.AppendLine($"Url: {context.Request.Url}, method: {context.Request.Method}");
return buffer.ToString();
}
}
```
### Run the example
Lastly to run the example:
```
$ fission env create --name dotnet --image fission/dotnet20-env
$ fission function create --name httpinfo --env dotnet --code /tmp/func.cs
$ fission route create --method GET --url /http_info --function httpinfo
$ curl "http://$FISSION_ROUTER/http_info"
Accept
*/*;q=1
Host
fissionserver:8888
User-Agent
curl/7.47.0
Url: http://fissionserver:8888, method: GET
```
## Accessing http request body example
### Setup fission environment
First you need to setup the fission according to your cluster setup as
specified here: https://github.com/fission/fission
### Create the class to run
Secondly you need to create a file /tmp/func.cs containing the following code:
```
using System.IO;
using System.Runtime.Serialization.Json;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context)
{
var person = Person.Deserialize(context.Request.Body);
return $"Hello, my name is {person.Name} and I am {person.Age} years old.";
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public static Person Deserialize(Stream json)
{
var serializer = new DataContractJsonSerializer(typeof(Person));
return (Person)serializer.ReadObject(json);
}
}
```
### Run the example
Lastly to run the example:
```
$ fission env create --name dotnet --image fission/dotnet20-env
$ fission function create --name httpbody --env dotnet --code /tmp/func.cs
$ fission route create --method GET --url /http_body --function httpbody
$ curl -XPOST "http://$FISSION_ROUTER/http_body" -d '{ "Name":"Arthur", "Age":42}'
Hello, my name is Arthur and I am 42 years old.
```
## Developing/debugging the environment locally
The easiest way to debug the environment is to open the directory in
Visual Studio Code (VSCode) as that will setup debugger for you the
first time.
Remember to install the excellent extension
"C# for Visual Studio Code(powered by OmniSharp)" to get statement completion
The class ExecutorModule contain preprocessor directive overriding where
the input code file should be found:
```
#if DEBUG
private const string CODE_PATH = "/tmp/func.cs";
#else
private const string CODE_PATH = "/userfunc/user";
#endif
```
So what you need to do is:
1. Open the directory in VSCode.
This will prompt restore of packages and query is debugger setup is needed. Accept both prompts.
2. Press F5 to start the web server. Set breakpoints etc..
3. Add a code file containing valid C# at /tmp/func.cs
4. Specialize the service with curl via post
```
$ curl -XPOST http://localhost:8888/specialize
```
5. Call your function with curl
```
$ curl -XGET http://localhost:8888
```
## Few Additional Features
**1. NameSpace support :**
Now , You can use namespace for Fission function class and have many other classes in same namespace , this is also backward compatible , however main execution class would always be *FissionFunction* and its method *Execute*
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context){
//original logic
}
public string AnotherClass(string myVal){
//do something
}
}
```
**2. Additional **setting/configuration file** support :**
Now , with Fission V2 end point with builder , in source package you can have additional setting
files which can be read by fission function .
Lets say you are writing a function and you need some configurable option and setting to be available in function and thus you want to use some additional configuration file , then you can also achieve the same by having a JSON based configuration file and a corresponding POCO Class for the same.
Please use https://csharp2json.io/ & http://json2csharp.com/ to create correct POCO class for you JSON configuration file.
Here is an example of a such file which we want to use in function , lets say your package.zip contains :
```
Source Package zip :
--source.zip
|--Func.cs
|--nuget.txt
|--exclude.txt
|--mysetting.json
|--....MiscFiles(optional)
|--....MiscFiles(optional)
```
here is what ***mysetting.json*** looks like :
```
{
"name": "Alpha",
"sendGridEndPoints":
[
{ "port": 1002 },
{ "port": 3004 }
]
}
```
here is what ***func.cs*** looks like :
```
using System;
using Fission.DotNetCore.Api;
namespace FuncNameSpace
{
public class FissionFunction
{
public string Execute(FissionContext context){
string res="initial value";
context.Logger.WriteInfo("Staring..... ");
var settings =context.GetSettings<SendGridSettings>("mysetting.json");
context.Logger.WriteInfo($"SendGridEndPoint port : {settings.SendGridEndPoints[0].port} ..... ");
res=settings.SendGridEndPoints[0].port;
context.Logger.WriteInfo("Done!!");
return res;
}
}
public class SendGridSettings
{
public string name { get; set; }
public System.Collections.Generic.List<SendGridEndPoint> SendGridEndPoints { get; set; }
}
public class SendGridEndPoint
{
public string port { get; set; }
}
}
```
**3. Nuget support :**
with use of fission builder we can now add various compatible nugets with our deployment package so that it can be leverage via our function code. Please go through detailed documentation of [fission builder for dotnet 2.0 environment](https://github.com/fission/fission/tree/master/environments/dotnet20/builder).
@@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Fission.DotNetCore.Utilty
{
public static class EnvironmentExtension
{
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
var knownKeys = new HashSet<TKey>();
return source.Where(element => knownKeys.Add(keySelector(element)));
}
public static string GetrelevantPathAsPerOS(this string curruntPath)
{
if (EnvironmentHelper.Instance.environmentSettings.RunningOnwindows && curruntPath.Contains("\\"))
{
return curruntPath;
}
else
{
return curruntPath.Replace("\\", "/");
}
}
}
}
@@ -1,117 +0,0 @@
using Fission.DotNetCore.Api;
using Fission.DotNetCore.Model;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Fission.DotNetCore.Utilty
{
public sealed class EnvironmentHelper
{
private static readonly Lazy<EnvironmentHelper> lazy =
new Lazy<EnvironmentHelper>(() => new EnvironmentHelper());
public static EnvironmentHelper Instance { get { return lazy.Value; } }
public EnvironmentSettings environmentSettings
{
get
{
if (_envSettings == null)
{
string watcherSettingsjson = GetEnvironmentSettingsJson();
_envSettings = ObjectConverter.Instance.GetWatcherSettingsFromJson(watcherSettingsjson);
}
return _envSettings;
}
set
{
_envSettings = value;
}
}
private EnvironmentSettings _envSettings;
static EnvironmentHelper()
{
}
private EnvironmentHelper()
{
}
public void writeToFile(string msg)
{
using (StreamWriter sw = new StreamWriter("main.log",true))
{
sw.AutoFlush = true;
sw.WriteLine($"{DateTime.Now} : {msg}");
}
}
private string GetEnvironmentSettingsJson()
{
var baselocation = AppDomain.CurrentDomain.BaseDirectory;
var FileLocation = baselocation + "envsettings.json";
return File.ReadAllText(FileLocation);
}
public BuilderRequest GetBuilderRequest(string json)
{
BuilderRequest builderRequest = new BuilderRequest();
try
{
builderRequest = JsonConvert.DeserializeObject<BuilderRequest>(json);
}
catch(Exception ex)
{
Console.WriteLine("Error : Unable to intersept request json " + ex.Message + ex.StackTrace);
}
return builderRequest;
}
public List<DllInfo> GetDllInfoFromDirectory(string directorypath)
{
List<DllInfo> dllInfos = new List<DllInfo>();
Console.WriteLine($"finding dll in folder {directorypath}");
DirectoryInfo d = new DirectoryInfo(directorypath);//Assuming packagepath is your Folder
var files = d.GetFiles("*.dll"); //Getting dll files
foreach(var file in files)
{
Console.WriteLine($"found dll {file.Name.ToLower()} at {Path.Combine(directorypath, file.Name)}");
dllInfos.Add(new DllInfo() {
name = file.Name.ToLower(),
path = Path.Combine(directorypath, file.Name)
});
}
return dllInfos;
}
public FunctionSpecification GetFunctionSpecs(string directorypath)
{
string functionSpecsFilePath = Path.Combine(directorypath, this.environmentSettings.functionSpecFileName);
if (File.Exists(functionSpecsFilePath))
{
string specsJson = File.ReadAllText(functionSpecsFilePath);
return ObjectConverter.Instance.GetFunctionSpecificationFromJson(specsJson);
}
else
throw new Exception($"Function Specification file not found at {functionSpecsFilePath}");
}
}
}
@@ -1,28 +0,0 @@
using Fission.DotNetCore.Model;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace Fission.DotNetCore.Utilty
{
public sealed class ObjectConverter
{
private static readonly Lazy<ObjectConverter> lazy =
new Lazy<ObjectConverter>(() => new ObjectConverter());
public static ObjectConverter Instance { get { return lazy.Value; } }
private ObjectConverter() {}
public EnvironmentSettings GetWatcherSettingsFromJson(string json)
{
return JsonConvert.DeserializeObject<EnvironmentSettings>(json);
}
public FunctionSpecification GetFunctionSpecificationFromJson(string json)
{
return JsonConvert.DeserializeObject<FunctionSpecification>(json);
}
}
}
-55
View File
@@ -1,55 +0,0 @@
using Builder.Utility;
using NugetWorker;
using System;
using System.IO;
using Builder.Engine;
namespace Builder
{
class Builder
{
static void Main(string[] args)
{
Console.WriteLine("Builder Task Begins!");
//create log file name for this session
var logFileName = $"{DateTime.Now.ToString("yyyy_MM_dd")}_{Guid.NewGuid().ToString()}.log";
Console.WriteLine($"going to create logger!!");
try
{
string _logdirectory = BuilderHelper.Instance.builderSettings.BuildLogDirectory;
BuilderHelper.Instance._logFileName = Path.Combine(_logdirectory, logFileName);
BuilderHelper.Instance.logger = new Utility.Logger(
BuilderHelper.Instance._logFileName);
//set the same for nuget engine dll
NugetHelper.Instance.logger = new NugetWorker.Logger(BuilderHelper.Instance._logFileName);
Console.WriteLine($"detailed logs for this build will be at : {Path.Combine(_logdirectory, logFileName)}!!");
BuilderEngine builderEngine = new BuilderEngine();
builderEngine.BuildPackage().Wait();
}
catch (Exception ex)
{
string detailedException = string.Empty;
try
{
detailedException= BuilderHelper.Instance.DeepException(ex);
Console.WriteLine($"Exception During Build : {Environment.NewLine} {ex.Message} | {ex.StackTrace} | {Environment.NewLine} {detailedException}");
}
catch(Exception childEx)
{
//do nothing , just log original exception
Console.WriteLine($"{Environment.NewLine} Exception During Build :{ex.Message} |{Environment.NewLine} {ex.StackTrace} {Environment.NewLine} ");
}
//now throw back exception so that build gets failed via builder
throw;
}
Console.WriteLine("Builder Task Ends!");
}
}
}
@@ -1,40 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nancy" Version="2.0.0-clinteastwood" />
<PackageReference Include="nugetdownloader" Version="2.0.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.9.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System">
<HintPath>System</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<None Update="build.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="builder">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="builderSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="log4net.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="nugetSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
-25
View File
@@ -1,25 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27906.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Builder", "Builder.csproj", "{D479FF57-44A0-483D-B5B0-B6C475D12292}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D479FF57-44A0-483D-B5B0-B6C475D12292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D479FF57-44A0-483D-B5B0-B6C475D12292}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D479FF57-44A0-483D-B5B0-B6C475D12292}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D479FF57-44A0-483D-B5B0-B6C475D12292}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AA20D0B8-E5D5-4BA1-9E29-FC5132A4DD00}
EndGlobalSection
EndGlobal
@@ -1,306 +0,0 @@
using Builder.Model;
using Builder.Utility;
using NugetWorker;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using NuGet.Packaging;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using Microsoft.CodeAnalysis.Emit;
using System.Runtime.Loader;
using Newtonsoft.Json;
namespace Builder.Engine
{
public class BuilderEngine
{
public string SRC_PKG = string.Empty;
public string DEPLOY_PKG = string.Empty;
List<DllInfo> dllInfos = new List<DllInfo>();
List<ExcludeDll> excludeDlls = new List<ExcludeDll>();
List<IncludeNuget> includeNugets = new List<IncludeNuget>();
List<string> compile_errors = new List<string>();
List<string> compile_info = new List<string>();
public BuilderEngine()
{
SRC_PKG = Environment.GetEnvironmentVariable("SRC_PKG");
DEPLOY_PKG = Environment.GetEnvironmentVariable("DEPLOY_PKG");
}
public async Task BuildPackage()
{
await BuildDllInfo();
Console.WriteLine("DLL Info Gathered!!");
// try to compile the function and if compilation succeed ,then create func spec file
//this enables us to find compilation issues during package creation itself thus saving time
// however this feature impose that the function file name should be func.cs
//if we don't want it , we can comment the TryCompile() logic
Console.WriteLine("Trying to compile it during build itself !!");
bool compiled =await TryCompile();
Console.WriteLine($"Compilation result Gathered as : {compiled}!!");
if (compiled)
{
//nowwhatever has been done so far and all files which are generated are in /app folder where dll resides
//thus copy relevant thing in SRC_PKG as it is ,but lest skip it here , we shall do it in build.sh
CopyToSourceDir();
Console.WriteLine($"Copy to Source Done!!");
//build the function specs
await BuildSpecs();
Console.WriteLine("BuildSpecs Done!!");
}
else
{
Console.WriteLine("Compilation failed , throwing exception !!");
foreach(var error in compile_errors)
{
Console.WriteLine($"COMPILATION ERROR : {error}");
}
throw new Exception($"COMPILATION FAILED !! , See builder logs for details , total Errors : {compile_errors.Count}");
}
}
public void CopyToSourceDir()
{
//to create folder if it doesnt already exists
string destinationFile=Path.Combine(SRC_PKG, BuilderHelper.Instance.builderSettings.DllDirectory, "dummy.txt");
new FileInfo(destinationFile).Directory.Create();
//copy all dlls
foreach (var dllinfo in dllInfos)
{
string filename = Path.GetFileName(dllinfo.path);
destinationFile = Path.Combine(SRC_PKG, BuilderHelper.Instance.builderSettings.DllDirectory, filename);
File.Copy(dllinfo.path, destinationFile,true);
}
//copy logs , well there is not point as logs are still being generated
//create dir if not exist
//new FileInfo(Path.Combine(SRC_PKG, BuilderHelper.Instance._logFileName)).Directory.Create();
//File.Copy(BuilderHelper.Instance._logFileName, Path.Combine(SRC_PKG, BuilderHelper.Instance._logFileName));
//BuilderHelper.Instance.logger.Log($"All Required Files copied to {SRC_PKG}");
}
public async Task<bool> TryCompile()
{
bool isSuccess = false;
string CODE_PATH = Path.Combine(SRC_PKG, BuilderHelper.Instance.builderSettings.functionBodyFileName);
if (!File.Exists(CODE_PATH))
{
Console.WriteLine($"Source Code not found at : {CODE_PATH} !" +
$" to use TryCompile() in Builder, make sure , your main function file name is " +
$"{BuilderHelper.Instance.builderSettings.functionBodyFileName} and " +
$"it is located at root of zip!!" );
return isSuccess;
}
var code = File.ReadAllText(CODE_PATH);
isSuccess = await Compile(code);
return isSuccess;
}
public async Task<bool> Compile(string code)
{
bool isSuccess = false;
#region assembly init and parent dll references
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
string assemblyName = Path.GetRandomFileName();
var coreDir = Directory.GetParent(typeof(Enumerable).GetTypeInfo().Assembly.Location);
List<MetadataReference> references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "mscorlib.dll"),
MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "netstandard.dll"),
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.Serialization.Json.DataContractJsonSerializer).GetTypeInfo().Assembly.Location)
};
foreach (var referencedAssembly in Assembly.GetEntryAssembly().GetReferencedAssemblies())
{
var assembly = Assembly.Load(referencedAssembly);
references.Add(MetadataReference.CreateFromFile(assembly.Location));
BuilderHelper.Instance.logger.Log($"Refering assembly based dls : {assembly.Location}");
}
#endregion
#region handler registration for runtime resolution
//now add handler for missing dlls for parent app domain as same assemblies should be needed
//for parent , thus refering from https://support.microsoft.com/en-in/help/837908/how-to-load-an-assembly-at-runtime-that-is-located-in-a-folder-that-is
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
#endregion
BuilderHelper.Instance.logger.Log($"dynamic handlar registered!!");
#region nuget dll reference add
//now add those dll reference
foreach (var dll in dllInfos)
{
BuilderHelper.Instance.logger.Log($"refering nuget based dll : {dll.path}");
references.Add(MetadataReference.CreateFromFile(dll.path));
}
#endregion
#region compilation
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release));
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
BuilderHelper.Instance.logger.Log($"Compile Failed!!!!",true);
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error).ToList();
foreach (Diagnostic diagnostic in failures)
{
compile_errors.Add($"{diagnostic.Id}: {diagnostic.GetMessage()}");
BuilderHelper.Instance.logger.Log($"COMPILE ERROR :{diagnostic.Id}: {diagnostic.GetMessage()}");
}
}
else
{
BuilderHelper.Instance.logger.Log("Compile Success!!",true);
isSuccess = true;
}
}
#endregion
return isSuccess;
}
public async Task BuildSpecs()
{
//create function specs C# object
FunctionSpecification functionSpecification = new FunctionSpecification();
functionSpecification.functionName = BuilderHelper.Instance.builderSettings.functionBodyFileName;
foreach (var dllinfo in dllInfos)
{
//here is the tweak , as this path is based on execution directoy , thus choose the relative path
string destinationFile = Path.Combine(BuilderHelper.Instance.builderSettings.DllDirectory, Path.GetFileName(dllinfo.path)).GetrelevantPathAsPerOS();
Library library = new Library()
{
name=dllinfo.name,
nugetPackage=dllinfo.rootPackage,
path = destinationFile
};
functionSpecification.libraries.Add(library);
}
//serialize that object to save it in json file
string funcMetaJson= JsonConvert.SerializeObject(functionSpecification);
string funcMetaFile = Path.Combine(this.SRC_PKG, BuilderHelper.Instance.builderSettings.functionSpecFileName);
BuilderHelper.Instance.WriteTofile(funcMetaFile, funcMetaJson);
}
public async Task BuildDllInfo()
{
//read the nuget file and download nuget packages
includeNugets = BuilderHelper.Instance.GetNugettoInclude(SRC_PKG);
//set the nuget logger to same logger
foreach (var nuget in includeNugets)
{
NugetEngine nugetEngine = new NugetEngine();
await nugetEngine.GetPackage(nuget.packageName, nuget.version);
//add the list of dlls received via this package in master list
dllInfos.AddRange(nugetEngine.dllInfos);
}
//now do a distinct of all dlls paths as multiple packaged might have added same dll
dllInfos = dllInfos.DistinctBy(x => x.path).ToList();
#if DEBUG
dllInfos.LogDllPathstoCSV("preFilter.CSV");
#endif
//exclude the dlls from exclude file
excludeDlls = BuilderHelper.Instance.GetDllstoExclude(SRC_PKG);
foreach (var excludedll in excludeDlls)
{
BuilderHelper.Instance.logger.Log($"trying to remove , if available : {excludedll.dllName} from package {excludedll.packageName}");
dllInfos.RemoveAll(x => x.rootPackage.ToLower() == excludedll.packageName.ToLower() && x.name.ToLower() == excludedll.dllName.ToLower());
}
#if DEBUG
//log dlls in debug mode
dllInfos.LogDllPathstoCSV("PostFilter.CSV");
#endif
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//This handler is called only when the common language runtime tries to bind to the assembly and fails.
BuilderHelper.Instance.logger.Log($"Dynamically trying to load dll {(args.Name.Substring(0, args.Name.IndexOf(",")).ToString() + ".dll").ToLower()} in parent assembaly");
//Retrieve the list of referenced assemblies in an array of AssemblyName.
Assembly MyAssembly = null, objExecutingAssemblies;
string strTempAssmbPath = "";
objExecutingAssemblies = Assembly.GetExecutingAssembly();
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
////Loop through the array of referenced assembly names.
if (dllInfos.Any(x => x.name.ToLower() == (args.Name.Substring(0, args.Name.IndexOf(",")).ToString() + ".dll").ToLower()))
{
strTempAssmbPath = dllInfos.Where(x => x.name.ToLower() == (args.Name.Substring(0, args.Name.IndexOf(",")).ToString() + ".dll").ToLower()).FirstOrDefault().path;
BuilderHelper.Instance.logger.Log($"loading dll in parent :{strTempAssmbPath}");
//Load the assembly from the specified path.
MyAssembly = Assembly.LoadFile(strTempAssmbPath);
}
if (MyAssembly == null)
{
BuilderHelper.Instance.logger.Log($"WARNING !!! unabel to locate dll :{(args.Name.Substring(0, args.Name.IndexOf(",")).ToString() + ".dll").ToLower()} ", true);
}
//Return the loaded assembly.
return MyAssembly;
}
}
}
-37
View File
@@ -1,37 +0,0 @@
ARG BUILDER_IMAGE=fission/builder
FROM ${BUILDER_IMAGE} AS fission-builder
FROM microsoft/dotnet:2.0.0-sdk AS builderimage
WORKDIR /app
# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
# Build runtime image
FROM microsoft/dotnet:aspnetcore-runtime
WORKDIR /app
COPY --from=builderimage /app/out .
# this builder is actually compilation from : https://github.com/fission/fission/tree/master/builder/cmd and renamed cmd.exe to builder
# make sure to compile it in linux only else you will get exec execute error as binary was compiled in windows and running on linux
COPY --from=fission-builder /builder /builder
# ADD builder /builder
ADD build.sh /usr/local/bin/build
RUN chmod +x /usr/local/bin/build
ADD build.sh /bin/build
RUN chmod +x /bin/build
EXPOSE 8001
@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Builder.Model
{
public class BuilderSettings
{
public string NugetSpecsFile { get; set; }
public string DllExcludeFile { get; set; }
public string BuildLogDirectory { get; set; }
public string NugetPackageRegEx { get; set; }
public string ExcludeDllRegEx { get; set; }
public bool RunningOnwindows { get; set; }
public string functionBodyFileName { get; set; }
public string functionSpecFileName { get; set; }
public string DllDirectory { get; set; }
}
}
@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Builder.Model
{
public class ExcludeDll
{
public string dllName { get; set; }
public string packageName { get; set; }
}
}
@@ -1,137 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Nancy;
using Newtonsoft.Json;
namespace Fission.DotNetCore.Api
{
public class FissionContext
{
public FissionContext(Dictionary<string, object> args, Logger logger, FissionHttpRequest request)
{
if (args == null) throw new ArgumentNullException(nameof(args));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (request == null) throw new ArgumentNullException(nameof(request));
Arguments = args;
Logger = logger;
Request = request;
}
public Dictionary<string, object> Arguments { get; private set; }
public FissionHttpRequest Request { get; private set; }
public Logger Logger { get; private set; }
public static FissionContext Build(Request request, Logger logger)
{
return new FissionContext(((DynamicDictionary)request.Query).ToDictionary(),
logger,
new FissionHttpRequest(request));
}
//this is a dummy, not being implemented, just to pass compilation
//actual execution is written in environment to use the app settings as there we need it
public T GetSettings<T>(string relativePath)
{
//intentionally doing it as these are just dummy methods not being called
//but if tomorrow if we decide to give implementation for execution in build then we
//need to implement it
throw new NotImplementedException();
}
//this is a dummy, not being implemented, just to pass compilation
//actual execution is written in environment to use the app settings as there we need it
private string GetSettingsJson(string relativePath)
{
//intentionally doing it as these are just dummy methods not being called
//but tomorrow if we decide to give implementation for execution in build then we
//need to implement it
throw new NotImplementedException();
}
}
public class Logger
{
public void Write(Severity severity, string format, params object[] args)
{
Console.WriteLine($"{DateTime.Now.ToString("MM/dd/yy H:mm:ss zzz")} {severity}: " + format, args);
}
public void WriteInfo(string format, params object[] args)
{
Write(Severity.Info, format, args);
}
public void WriteWarning(string format, params object[] args)
{
Write(Severity.Warning, format, args);
}
public void WriteError(string format, params object[] args)
{
Write(Severity.Error, format, args);
}
public void WriteCritical(string format, params object[] args)
{
Write(Severity.Critical, format, args);
}
public void WriteVerbose(string format, params object[] args)
{
Write(Severity.Verbose, format, args);
}
}
public enum Severity
{
Info,
Warning,
Error,
Critical,
Verbose
}
public class FissionHttpRequest
{
private readonly Request _request;
internal FissionHttpRequest(Request request)
{
if (request == null) throw new ArgumentNullException(nameof(request));
_request = request;
}
public Stream Body { get { return _request.Body; } }
public string BodyAsString()
{
int length = (int)_request.Body.Length;
byte[] data = new byte[length];
_request.Body.Read(data, 0, length);
return Encoding.UTF8.GetString(data);
}
public Dictionary<string, IEnumerable<string>> Headers
{
get
{
var headers = new Dictionary<string, IEnumerable<string>>();
foreach (var kv in _request.Headers)
{
headers.Add(kv.Key, kv.Value);
}
return headers;
}
}
public X509Certificate Certificate { get { return _request.ClientCertificate; } }
public string Url { get { return _request.Url.ToString(); } }
public string Method { get { return _request.Method; } }
}
}
@@ -1,40 +0,0 @@
using NugetWorker;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Builder.Model
{
public class FunctionSpecification
{
public FunctionSpecification()
{
this.libraries = new List<Library>();
}
public string functionName { get; set; }
public List<Library> libraries { get; set; }
public string hash { get; set; }
public string certificatePath { get; set; }
}
public class Library
{
public Library()
{
}
public Library(DllInfo dllInfo)
{
this.name = dllInfo.name;
this.nugetPackage = dllInfo.rootPackage;
this.path = dllInfo.path;
}
public string name { get; set; }
//public string version { get; set; }
public string path { get; set; }
public string nugetPackage { get; set; }
}
}
@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Builder.Model
{
public class IncludeNuget
{
public string packageName { get; set; }
public string version { get; set; }
}
}
-175
View File
@@ -1,175 +0,0 @@
# Fission: dotnet 2.0 C# Environment Builder
This is a simple dotnet core 2.0 C# environment builder for Fission.
It's a docker image containing the dotnet 2.0.0 (core) run-time builder. This image read the source package and uses
*roslyn* to compile the source package code and creates deployment package out of it.
This enables using nuget packages as part of function and thus user can use extended functionality in fission functions via nuget.
During build , builder also does a pre-compile to prevent any compilation issues during function environment pod specialization.
Thus we get the function compilation issues during builder phase in package info's build logs itself.
**Note** : In future we can further enhance the compiled assembly to be saved as physical file in deployment package ,
as this will save cold start time for function.
Now , once after the build is finished, the output package (deploy archive) will be uploaded to storagesvc to store.
Then, during the specialization, the fetcher inside function pod will fetch the package from storagesvc for function loading
and will call on the **/v2/specialized** endpoint of fission environment with required parameters.
There further environment will compile it and execute the function.
Example of simplest possible class to be executed:
The source package structure in zip file :
```
Source Package zip :
--source.zip
|--func.cs
|--nuget.txt
|--exclude.txt
|--....MiscFiles(optional)
|--....MiscFiles(optional)
```
**func.cs** --> This contains original function body with Executing method name as : Execute
**nuget.txt**--> this file contains list of nuget packages required by your function , in this file put one line per nuget with nugetpackage name:version(optional) format, for example :
```
RestSharp
CsvHelper
Newtonsoft.json:10.2.1.0
```
this should match the following regex as mentioned in builderSetting.json
```
"NugetPackageRegEx": "\\:?\\s*(?<package>[^:\\n]*)(?:\\:)?(?<version>.*)?"
```
**exclude.txt**--> as nuget.txt will download original package and their dependent packages , thus sometime dependent packages might not be
that useful and can break compilation , thus this file contains list of dlls of specific nuget packages which doesn't need to be added during compilation if they break compilation .Put one line per nuget with dllname:nugetpackagename formate ,for example :
```
Newtonsoft.json:Newtonsoft.json.dll
```
this should match the following regex as mentions in builderSetting.json
```
"ExcludeDllRegEx": "\\:?\\s*(?<package>[^:\\n]*)(?:\\:)?(?<dll>.*)?",
```
From above , builder will create a deployment package with all dlls in a folder and one function specification file :
Deployment Package zip :
```
--Deploye.zip
|--func.cs
|--nuget.txt
|--exclude.txt
|--dll()
|--newtonsoft.json.dll
|--restsharp.dll
|--csvhelper.dll
|--logs()
|-->logFileName
|--func.meta.json // this is the function specific file
|--....MiscFiles(optional)
|--....MiscFiles(optional)
```
Here are commands and detailed example for the same .
lets say my source package zip name is *funccsv.zip* :
**Content of func.cs:**
```
using System;
using Fission.DotNetCore.Api;
public class FissionFunction
{
public string Execute(FissionContext context){
string res="initial value";
try
{
context.Logger.WriteInfo("Staring..... ");
res=$" sample object by getting Enum of CsvHelper nuget dll: { CsvHelper.Caches.NamedIndex.ToString()}";
}
catch(Exception ex)
{
context.Logger.WriteError(ex.Message);
res = ex.Message;
}
context.Logger.WriteInfo("Done!");
return res;
}
}
```
**Content of *nuget.txt***
```
CsvHelper
```
**Content of exclude.txt**
As we don't want to exclude any specific dll thus we shall leave it as empty.
Now check name of existing environments & functions as we want to create a unique environment for this .Net Core if not already present
```
fission env list
fission fn list
```
Create Environment with builder (choose a unique which doesn't exist , here we have chosen : dotnetcorewithnuget )
also suppose the builder image name is fissiondotnet20-builder and hosted on Docker Hub as fission/dotnet20-builder
```
fission environment create --name dotnetcorewithnuget --image fission/dotnet20-env --builder fission/dotnet20-builder
```
Verify fission-builder and fission-function namespace for new pods (pods name beginning with env name which we have given like *dotnetcorewithnuget-xxx-xxx*)
```
kubectl get pods -n fission-builder
kubectl get pods -n fission-function
```
Create Package from source zip using this environment name , this will output some package name created..
```
fission package create --src funccsv.zip --env dotnetcorewithnuget
```
Note down output package name lets say it is *funccsv-zip-xyz* now check its status using package info command , this will give the status
on what happened with builder and test compilation in builder.
```
fission package info --name funccsv-zip-xyz
```
#Status of package should be f*ailed / running / succeeded* .
Wait if the status is running , until it fails or succeeded. For detailed build logs, you can shell into builder pod in fission-builder namespace and verify log location mentioned in above command's result output.
**Note** : Even If the result is succeeded , please have a look at detailed build logs to see compilation success and builder job done.
Now If the result is succeeded , then go ahead and create function using this package.
*--entrypoint* flag is optional if your function body file name is func.cs (which it should be as builder need that), else put the filename (without extension )
```
fission fn create --name dotnetcsvtest --pkg funccsv-zip-xyz --env dotnetcorewithnuget --entrypoint "func"
```
Test the function execution :
```
fission fn test --name dotnetcsvtest
```
above would execute the function and will output the enum value as written in dll.
rest of the feature are same as normal fission environment.
**Benefit of using builder** :
1. Ability to use various nuget packages.
2. Ability to use many additional files and functions as part of deployment package.
3. Ability to know the compilation issue in advance via package logs , instead of environment giving compilation issue.
4. Reusability of same deployment package.
@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Builder.Utility
{
public static class BuilderExtensions
{
public static string GetrelevantPathAsPerOS(this string curruntPath)
{
if (BuilderHelper.Instance.builderSettings.RunningOnwindows)
{
return curruntPath;
}
else
{
return curruntPath.Replace("\\","/");
}
}
}
}
@@ -1,180 +0,0 @@
using Builder.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace Builder.Utility
{
public sealed class BuilderHelper
{
private static readonly Lazy<BuilderHelper> lazy =
new Lazy<BuilderHelper>(() => new BuilderHelper());
public string _logFileName = string.Empty;
public static BuilderHelper Instance { get { return lazy.Value; } }
private static Logger _logger = new Logger("Initial.log");
private BuilderSettings _builderSettings = null;
public BuilderSettings builderSettings
{
get
{
if (_builderSettings == null)
{
string builderSettingsjson = GetBuilderSettingsJson();
_builderSettings = ObjectConverter.Instance.GetBuilderSettingsFromJson(builderSettingsjson);
}
return _builderSettings;
}
set
{
builderSettings = value;
}
}
public Logger logger
{
get
{
return _logger;
}
set
{
_logger = value;
}
}
static BuilderHelper()
{
}
private BuilderHelper()
{
}
private string GetBuilderSettingsJson()
{
var baselocation = AppDomain.CurrentDomain.BaseDirectory;
var FileLocation = baselocation + "builderSettings.json";
return File.ReadAllText(FileLocation);
}
public List<IncludeNuget> GetNugettoInclude(string directoryPath)
{
List<IncludeNuget> includeNugets = new List<IncludeNuget>();
string includeNugetsFilePath = Path.Combine(directoryPath, this.builderSettings.NugetSpecsFile);
if (File.Exists(includeNugetsFilePath))
{
Regex _pkgName = new Regex(this.builderSettings.NugetPackageRegEx,
RegexOptions.Compiled);
string filetext = File.ReadAllText(includeNugetsFilePath);
var _pkgMatchCollection = _pkgName.Matches(filetext);
foreach (Match match in _pkgMatchCollection)
{
if(!string.IsNullOrWhiteSpace(match.Value))
{
string package = match.Groups["package"]?.Value?.Trim();
string version = match.Groups["version"]?.Value?.Trim();
this.logger.Log($"adding {package} | {version} to includeNugets collection");
includeNugets.Add(
new IncludeNuget()
{
packageName = package,
version = version
}
);
}
}
}
return includeNugets;
}
public List<ExcludeDll> GetDllstoExclude(string directoryPath)
{
List<ExcludeDll> excludeDlls = new List<ExcludeDll>();
string excludeDllsFilePath = Path.Combine(directoryPath, this.builderSettings.DllExcludeFile);
if (File.Exists(excludeDllsFilePath))
{
//xyzPackage:abc.dll
Regex _exclude = new Regex(this.builderSettings.ExcludeDllRegEx,
RegexOptions.Compiled);
string filetext = File.ReadAllText(excludeDllsFilePath);
var _excludeMatchCollection = _exclude.Matches(filetext);
foreach (Match match in _excludeMatchCollection)
{
if (!string.IsNullOrWhiteSpace(match.Value))
{
string _package = match.Groups["package"]?.Value?.Trim();
string _dllName = match.Groups["dll"]?.Value?.Trim();
this.logger.Log($"adding {_package} | {_dllName} to excludeDlls collection");
excludeDlls.Add(
new ExcludeDll()
{
packageName = _package,
dllName = _dllName
}
);
}
}
}
return excludeDlls;
}
public string DeepException(Exception ex)
{
string responce = string.Empty;
responce = " Exception : LEVEL 1: " + Environment.NewLine + ex.Message;
if (ex.InnerException != null)
{
responce = responce + Environment.NewLine + "LEVEL 2:" + Environment.NewLine + ex.InnerException.Message;
if (ex.InnerException.InnerException != null)
{
responce =responce + Environment.NewLine + "LEVEL 3:" + Environment.NewLine + ex.InnerException.InnerException.Message;
if (ex.InnerException.InnerException.InnerException != null)
{
responce = responce + Environment.NewLine + "LEVEL 4:" + Environment.NewLine + ex.InnerException.InnerException.InnerException.Message;
if (ex.InnerException.InnerException.InnerException.InnerException != null)
{
responce = responce + Environment.NewLine + "LEVEL 5:" + Environment.NewLine + ex.InnerException.InnerException.InnerException.InnerException.Message;
}
}
}
}
if(ex.StackTrace!=null)
{
responce = responce + "|| STACK :"+ ex.StackTrace;
}
return responce;
}
public void WriteTofile(string filenameWithPath , string content)
{
using (StreamWriter sw = new StreamWriter(filenameWithPath, false))
{
sw.AutoFlush = true;
sw.Write(content);
}
}
}
}
@@ -1,111 +0,0 @@
using log4net;
using NuGet.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace Builder.Utility
{
public class Logger : NuGet.Common.ILogger
{
private ILog _ILog { get; set; }
public Logger(string logpath)
{
//read the log4net config and create logger instance form log4net
XmlDocument ConfigLoader = new XmlDocument();
ConfigLoader.Load(File.OpenRead("log4net.config"));
var repo = LogManager.CreateRepository(Assembly.GetEntryAssembly(),
typeof(log4net.Repository.Hierarchy.Hierarchy));
log4net.Config.XmlConfigurator.Configure(repo, ConfigLoader["log4net"]);
var appender = ((log4net.Appender.FileAppender)repo.GetAppenders().Where(x => x.Name == "RollingLogFileAppender").FirstOrDefault());
appender.File = logpath;// $"directoryPath/{DateTime.Now.ToString("yyyy_MM_dd")}_{Guid.NewGuid().ToString()}_.log";
appender.ActivateOptions();
_ILog = LogManager.GetLogger(typeof(Logger));
}
public void Log(string message,bool logToConsoleAsWell=false)
{
if(logToConsoleAsWell)
Console.WriteLine(message);
_ILog.Info(message);
}
public void Log(LogLevel level, string data)
{
//Console.WriteLine(data);
_ILog.Info(data);
}
public void Log(ILogMessage message)
{
//Console.WriteLine(message);
_ILog.Info(message.Message);
}
public Task LogAsync(LogLevel level, string data)
{
//Console.WriteLine(data);
_ILog.Info(data);
return null;
}
public Task LogAsync(ILogMessage message)
{
//Console.WriteLine(message);
_ILog.Info(message.Message);
return null;
}
public void LogDebug(string data)
{
//Console.WriteLine(data);
_ILog.Debug(data);
}
public void LogError(string data)
{
//Console.WriteLine(data);
_ILog.Error(data);
}
public void LogInformation(string data)
{
//Console.WriteLine(data);
_ILog.Info(data);
}
public void LogInformationSummary(string data)
{
//Console.WriteLine(data);
_ILog.Info(data);
}
public void LogMinimal(string data)
{
//Console.WriteLine(data);
_ILog.Info(data);
}
public void LogVerbose(string data)
{
//Console.WriteLine(data);
_ILog.Debug(data);
}
public void LogWarning(string data)
{
//Console.WriteLine(data);
_ILog.Warn(data);
}
}
}
@@ -1,31 +0,0 @@
using Builder.Model;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace Builder.Utility
{
public sealed class ObjectConverter
{
private static readonly Lazy<ObjectConverter> lazy =
new Lazy<ObjectConverter>(() => new ObjectConverter());
public static ObjectConverter Instance { get { return lazy.Value; } }
private ObjectConverter()
{
}
public BuilderSettings GetBuilderSettingsFromJson(string json)
{
return JsonConvert.DeserializeObject<BuilderSettings>(json);
}
}
}
-17
View File
@@ -1,17 +0,0 @@
#!/bin/bash
set -euxo pipefail
cd ${SRC_PKG}
#now start execution of custom logic dll in such a way that it should copy everything in ${SRC_PKG}
#first lets try putting sample file which will prove that this worked
echo src : ${SRC_PKG} ,dest: ${DEPLOY_PKG} >builderpaths.txt
#now run actual dll for custom builder logic
# please note as this need to be executed from app folder so that all dependent files are avilable
# else you will end up getting File not found error
cd /app
#now execute dll
dotnet Builder.dll
#copy entire content to deployment package
cp -r ${SRC_PKG} ${DEPLOY_PKG}
@@ -1,11 +0,0 @@
{
"NugetSpecsFile": "nuget.txt",
"DllExcludeFile": "exclude.txt",
"BuildLogDirectory": "logs",
"DllDirectory": "Dlls",
"NugetPackageRegEx": "\\s*(?<package>[^:\\n]*)(?:\\:)?(?<version>.*)?",
"ExcludeDllRegEx": "\\:?\\s*(?<package>[^:\\n]*)(?:\\:)?(?<dll>.*)?",
"RunningOnwindows": false,
"functionBodyFileName": "func.cs",
"functionSpecFileName": "func.meta.json"
}
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="Console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level: %message%newline" />
</layout>
</appender>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<File value=""/>
<appendToFile value="true" />
<rollingStyle value="Size" />
<!--<datePattern value="yyyyMMdd-HHmm" />-->
<maxSizeRollBackups value="6" />
<preserveLogFileNameExtension value="true"/>
<staticLogFileName value="false" />
<maxSizeRollBackups value="100"/>
<maximumFileSize value="1MB"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level : %message %newline %newline"/>
</layout>
</appender>
<root>
<level value="ALL"/>
<!--<appender-ref ref="Console" />-->
<appender-ref ref="RollingLogFileAppender"/>
</root>
</log4net>
@@ -1,37 +0,0 @@
{
"NugetFolder": "Nugetdownload",
"DisableCache": false,
"CSVDirectory": "logs",
"RunningOnwindows": false,
"NugetRepositories": [
{
"Order": 1,
"IsPrivate": false,
"Name": "local",
"Source": "Nugetdownload", //in case of local , relative path to folder
"IsPasswordClearText": false,
"Username": "",
"Password": ""
},
{
"Order": 2,
"IsPrivate": false,
"Name": "NugetV3",
"Source": "https://api.nuget.org/v3/index.json",
"IsPasswordClearText": false,
"Username": "",
"Password": ""
}
//,
//{
// "Order": 3,
// "IsPrivate": true,
// "Name": "myPrivateRepository", //in case of private repo , if not using, then just remove this section No 3
// "Source": "https://URL-FOR-myPrivateRepository/api/nuget/nugetcore/COMPLETE-END-POINT",
// "IsPasswordClearText": true,
// "Username": "johndeo@myorg.com",
// "Password": "AlPH24GAMAc49fDELTA"
//}
]
}
-25
View File
@@ -1,25 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fission-dotnet20", "fission-dotnet20.csproj", "{3F044DE1-74E5-48F7-8D23-233C24AAA45C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3F044DE1-74E5-48F7-8D23-233C24AAA45C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F044DE1-74E5-48F7-8D23-233C24AAA45C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F044DE1-74E5-48F7-8D23-233C24AAA45C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F044DE1-74E5-48F7-8D23-233C24AAA45C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {272000F7-BBEC-4B61-8865-783EF7E5CEE9}
EndGlobalSection
EndGlobal
-7
View File
@@ -1,7 +0,0 @@
{
"LogDirectory": "logs",
"RunningOnwindows": false,
"DllDirectory": "Dlls",
"functionBodyFileName": "func.cs",
"functionSpecFileName": "func.meta.json"
}
@@ -1,34 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Builder\**" />
<EmbeddedResource Remove="Builder\**" />
<None Remove="Builder\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Owin" Version="2.0.0" />
<PackageReference Include="Nancy" Version="2.0.0-clinteastwood" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.3.2" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Model\" />
</ItemGroup>
<ItemGroup>
<None Update="envsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
dotnet restore fission-dotnet20.csproj
dotnet publish fission-dotnet20.csproj -c Release -o out
-24
View File
@@ -1,24 +0,0 @@
ARG GO_VERSION=1.9.2
FROM ubuntu:18.04 AS base
WORKDIR /
RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/lists/*
FROM golang:${GO_VERSION} AS builder
ENV GOPATH /usr
ENV APP ${GOPATH}/src/github.com/fission/fission/environments/go
WORKDIR ${APP}
ADD context ${APP}/context
ADD server.go ${APP}
RUN go get
RUN go build -a -o /server server.go
FROM base
COPY --from=builder /server /
ENTRYPOINT ["/server"]
EXPOSE 8888
-24
View File
@@ -1,24 +0,0 @@
ARG GO_VERSION=1.11.4
FROM ubuntu:18.04 AS base
WORKDIR /
RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/lists/*
FROM golang:${GO_VERSION} AS builder
ENV GOPATH /usr
ENV APP ${GOPATH}/src/github.com/fission/fission/environments/go
WORKDIR ${APP}
ADD context ${APP}/context
ADD server.go ${APP}
RUN go get
RUN go build -a -o /server server.go
FROM base
COPY --from=builder /server /
ENTRYPOINT ["/server"]
EXPOSE 8888
-24
View File
@@ -1,24 +0,0 @@
ARG GO_VERSION=1.12
FROM ubuntu:18.04 AS base
WORKDIR /
RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/lists/*
FROM golang:${GO_VERSION} AS builder
ENV GOPATH /usr
ENV APP ${GOPATH}/src/github.com/fission/fission/environments/go
WORKDIR ${APP}
ADD context ${APP}/context
ADD server.go ${APP}
RUN go get
RUN go build -a -o /server server.go
FROM base
COPY --from=builder /server /
ENTRYPOINT ["/server"]
EXPOSE 8888
-24
View File
@@ -1,24 +0,0 @@
ARG GO_VERSION=1.13
FROM ubuntu:18.04 AS base
WORKDIR /
RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/lists/*
FROM golang:${GO_VERSION} AS builder
ENV GOPATH /usr
ENV APP ${GOPATH}/src/github.com/fission/fission/environments/go
WORKDIR ${APP}
ADD context ${APP}/context
ADD server.go ${APP}
RUN go get
RUN go build -a -o /server server.go
FROM base
COPY --from=builder /server /
ENTRYPOINT ["/server"]
EXPOSE 8888
-24
View File
@@ -1,24 +0,0 @@
ARG GO_VERSION=1.14
FROM ubuntu:18.04 AS base
WORKDIR /
RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/lists/*
FROM golang:${GO_VERSION} AS builder
ENV GOPATH /usr
ENV APP ${GOPATH}/src/github.com/fission/fission/environments/go
WORKDIR ${APP}
ADD context ${APP}/context
ADD server.go ${APP}
RUN go get
RUN go build -a -o /server server.go
FROM base
COPY --from=builder /server /
ENTRYPOINT ["/server"]
EXPOSE 8888
-38
View File
@@ -1,38 +0,0 @@
# Fission: Go Environment
This is the Go environment for Fission.
It's a Docker image containing a Go runtime, along with a dynamic loader.
Looking for ready-to-run examples? See the [Go examples directory](../../examples/go).
## Build this image
```
docker build -t USER/go-runtime . && docker push USER/go-runtime
```
Note that if you build the runtime, you must also build the go-builder
image, to ensure that it's at the same version of go:
```
cd builder && docker build -t USER/go-builder . && docker push USER/go-builder
```
## Using the image in fission
You can add this customized image to fission with "fission env
create":
```
fission env create --name go --image USER/go-runtime --builder USER/go-builder --version 2
```
Or, if you already have an environment, you can update its image:
```
fission env update --name go --image USER/go-runtime --builder USER/go-builder
```
After this, fission functions that have the env parameter set to the
same environment name as this command will use this environment.
-13
View File
@@ -1,13 +0,0 @@
ARG BUILDER_IMAGE=fission/builder
ARG GO_VERSION=1.9.2
FROM ${BUILDER_IMAGE}
FROM golang:${GO_VERSION}
ENV GOPATH /usr
WORKDIR ${GOPATH}
COPY --from=0 /builder /builder
ADD build.sh /usr/local/bin/build
-13
View File
@@ -1,13 +0,0 @@
ARG BUILDER_IMAGE=fission/builder
ARG GO_VERSION=1.11.4
FROM ${BUILDER_IMAGE}
FROM golang:${GO_VERSION}
ENV GOPATH /usr
WORKDIR ${GOPATH}
COPY --from=0 /builder /builder
ADD build.sh /usr/local/bin/build
-14
View File
@@ -1,14 +0,0 @@
ARG BUILDER_IMAGE=fission/builder
ARG GO_VERSION=1.12
FROM ${BUILDER_IMAGE}
FROM golang:${GO_VERSION}
ENV GOPATH /usr
ENV GO111MODULE on
WORKDIR ${GOPATH}
COPY --from=0 /builder /builder
ADD build.sh /usr/local/bin/build
-14
View File
@@ -1,14 +0,0 @@
ARG BUILDER_IMAGE=fission/builder
ARG GO_VERSION=1.13
FROM ${BUILDER_IMAGE}
FROM golang:${GO_VERSION}
ENV GOPATH /usr
ENV GO111MODULE on
WORKDIR ${GOPATH}
COPY --from=0 /builder /builder
ADD build.sh /usr/local/bin/build
-14
View File
@@ -1,14 +0,0 @@
ARG BUILDER_IMAGE=fission/builder
ARG GO_VERSION=1.14
FROM ${BUILDER_IMAGE}
FROM golang:${GO_VERSION}
ENV GOPATH /usr
ENV GO111MODULE on
WORKDIR ${GOPATH}
COPY --from=0 /builder /builder
ADD build.sh /usr/local/bin/build
-45
View File
@@ -1,45 +0,0 @@
#!/bin/bash
set -eux
srcDir=${GOPATH}/src/$(basename ${SRC_PKG})
trap "rm -rf ${srcDir}" EXIT
# http://ask.xmodulo.com/compare-two-version-numbers.html
version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
if [ -d ${SRC_PKG} ]
then
echo "Building in directory ${srcDir}"
ln -sf ${SRC_PKG} ${srcDir}
elif [ -f ${SRC_PKG} ]
then
echo "Building file ${SRC_PKG} in ${srcDir}"
mkdir -p ${srcDir}
cp ${SRC_PKG} ${srcDir}/function.go
fi
cd ${srcDir}
if [ ! -z ${GOLANG_VERSION} ] && version_ge ${GOLANG_VERSION} "1.12"; then
if [ -f "go.mod" ]; then
go mod download
else
# still need to do this; otherwise, go will complain "cannot find main module".
go mod init
fi
else # go version lower than go 1.12
if [ -f "go.mod" ]; then
echo "Please update fission/go-builder and fission/go-env image to the latest version to support go module"
exit 1
fi
fi
# use vendor mode if the vendor dir exists when go version is greater
# than 1.12 (the version that fission started to support go module).
if [ -d "vendor" ] && [ ! -z ${GOLANG_VERSION} ] && version_ge ${GOLANG_VERSION} "1.12"; then
go build -mod=vendor -buildmode=plugin -i -o ${DEPLOY_PKG} .
else
go build -buildmode=plugin -i -o ${DEPLOY_PKG} .
fi
-10
View File
@@ -1,10 +0,0 @@
package context
type (
Context map[string]interface{}
)
func New() Context {
ctx := make(map[string]interface{})
return ctx
}
-203
View File
@@ -1,203 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"plugin"
// DO NOT IMPORT THIRD PARTY PACKAGES
// The 3rd party package version used by go server may be
// different from the one in user's source code and will
// cause plugin version mismatched. Hence, we should never
// import any external packages except the Fission or built-in
// packages.
"github.com/fission/fission/environments/go/context"
)
const (
CODE_PATH = "/userfunc/user"
)
type (
FunctionLoadRequest struct {
// FilePath is an absolute filesystem path to the
// function. What exactly is stored here is
// env-specific. Optional.
FilePath string `json:"filepath"`
// FunctionName has an environment-specific meaning;
// usually, it defines a function within a module
// containing multiple functions. Optional; default is
// environment-specific.
FunctionName string `json:"functionName"`
// URL to expose this function at. Optional; defaults
// to "/".
URL string `json:"url"`
}
)
var userFunc http.HandlerFunc
func loadPlugin(codePath, entrypoint string) (http.HandlerFunc, error) {
// if codepath's a directory, load the file inside it
info, err := os.Stat(codePath)
if err != nil {
return nil, fmt.Errorf("error checking plugin path: %v", err)
}
if info.IsDir() {
files, err := ioutil.ReadDir(codePath)
if err != nil {
return nil, fmt.Errorf("error reading directory: %v", err)
}
if len(files) == 0 {
return nil, fmt.Errorf("no files to load: %v", codePath)
}
fi := files[0]
codePath = filepath.Join(codePath, fi.Name())
}
log.Printf("loading plugin from %v", codePath)
p, err := plugin.Open(codePath)
if err != nil {
return nil, fmt.Errorf("error loading plugin: %v", err)
}
sym, err := p.Lookup(entrypoint)
if err != nil {
return nil, fmt.Errorf("entry point not found: %v", err)
}
switch h := sym.(type) {
case *http.Handler:
return (*h).ServeHTTP, nil
case *http.HandlerFunc:
return *h, nil
case func(http.ResponseWriter, *http.Request):
return h, nil
case func(context.Context, http.ResponseWriter, *http.Request):
return func(w http.ResponseWriter, r *http.Request) {
c := context.New()
h(c, w, r)
}, nil
default:
panic("Entry point not found: bad type")
}
}
func specializeHandler() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if userFunc != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Not a generic container"))
return
}
_, err := os.Stat(CODE_PATH)
if err != nil {
if os.IsNotExist(err) {
log.Printf("code path (%v) does not exist: %v", CODE_PATH, err)
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(CODE_PATH + ": not found"))
return
} else {
log.Printf("unknown error looking for code path(%v): %v", CODE_PATH, err)
err = fmt.Errorf("unknown error: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
}
log.Println("specializing ...")
userFunc, err = loadPlugin(CODE_PATH, "Handler")
if err != nil {
err = fmt.Errorf("error specializing function: %v", err)
log.Println(err.Error())
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
log.Println("done")
}
}
func specializeHandlerV2() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if userFunc != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Not a generic container"))
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Printf("error reading request body: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
var loadreq FunctionLoadRequest
err = json.Unmarshal(body, &loadreq)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
_, err = os.Stat(loadreq.FilePath)
if err != nil {
if os.IsNotExist(err) {
log.Printf("code path (%v) does not exist: %v", loadreq.FilePath, err)
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(loadreq.FilePath + ": not found"))
return
} else {
log.Printf("unknown error looking for code path(%v): %v", loadreq.FilePath, err)
err = fmt.Errorf("unknown error: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
}
log.Println("specializing ...")
userFunc, err = loadPlugin(loadreq.FilePath, loadreq.FunctionName)
if err != nil {
err = fmt.Errorf("error specializing function: %v", err)
log.Println(err.Error())
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
log.Println("done")
}
}
func readinessProbeHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func main() {
http.HandleFunc("/healthz", readinessProbeHandler)
http.HandleFunc("/specialize", specializeHandler())
http.HandleFunc("/v2/specialize", specializeHandlerV2())
// Generic route -- all http requests go to the user function.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if userFunc == nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Generic container: no requests supported"))
return
}
userFunc(w, r)
})
log.Println("listening on 8888 ...")
http.ListenAndServe(":8888", nil)
}
-2
View File
@@ -1,2 +0,0 @@
target/
bin/
-15
View File
@@ -1,15 +0,0 @@
FROM maven:3.5-jdk-8 as BUILD
WORKDIR /usr/src/myapp/
# To reuse the build cache, here we split maven dependency
# download and package into two RUN commands to avoid cache invalidation.
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src /usr/src/myapp/src/
RUN mvn package
FROM openjdk:8-jre-alpine
COPY --from=BUILD /usr/src/myapp/target/env-jvm-jersey-0.0.1.jar /app.jar
ENTRYPOINT java ${JVM_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar 8888
EXPOSE 8888
-15
View File
@@ -1,15 +0,0 @@
FROM maven:3.6-jdk-11 as BUILD
WORKDIR /usr/src/myapp/
# To reuse the build cache, here we split maven dependency
# download and package into two RUN commands to avoid cache invalidation.
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src /usr/src/myapp/src/
RUN mvn package
FROM openjdk:11-jre
COPY --from=BUILD /usr/src/myapp/target/env-jvm-jersey-0.0.1.jar /app.jar
ENTRYPOINT java ${JVM_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.jar --server.port=8888
EXPOSE 8888
-68
View File
@@ -1,68 +0,0 @@
# Fission: Java and JVM-Jersey Environment
This is the JVM (Jersey based) environment for Fission.
It's a Docker image containing a OpenJDK8 runtime, along with a
dynamic loader. A few dependencies are included in the
pom.xml file.
Unlike the other [JVM environment](../jvm) which is based on the Spring framework, this environment uses Jersey.
Looking for ready-to-run examples? See the [JVM examples directory](../../examples/jvm-jersey).
## Customizing this image
To add package dependencies, edit pom.xml to add what you
need, and rebuild this image (instructions below).
## Rebuilding and pushing the image
You'll need access to a Docker registry to push the image: you can
sign up for Docker hub at hub.docker.com, or use registries from
gcr.io, quay.io, etc. Let's assume you're using a docker hub account
called USER. Build and push the image to the the registry:
```
docker build -t USER/jvm-jersey-env . && docker push USER/jvm-jersey-env
```
You can also create environment image based on JVM 11 using Dockerfile-11 in this directory.
## Using the image in fission
You can add this customized image to fission with "fission env
create":
```
fission env create --name jvm --image USER/jvm-jersey-env
```
Or, if you already have an environment, you can update its image:
```
fission env update --name jvm --image USER/jvm-jersey-env
```
After this, fission functions that have the env parameter set to the
same environment name as this command will use this environment.
## Web Server Framework
JVM Jersey environment uses an embedded Jetty HTTP server by default, as can be seen in the pom.xml file.
```
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.0.4.v20130625</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.0.4.v20130625</version>
</dependency>
```
## Java and JVM builder
There are two JVM environment builder based on OpenJDK8 and OpenJDK 11 and using Maven 3.5.4. The default build command runs `mvn clean package` and uses the target/*with-dependencies.jar file for function. The default build command can be overridden as long as the uber jar file is copied to ${DEPLOY_PKG}.
@@ -1,48 +0,0 @@
## Fission builder base image
ARG BUILDER_IMAGE=fission/builder:latest
FROM ${BUILDER_IMAGE}
## Section copied from the openjdk:8-jdk-alpine Dockerfile - (https://github.com/docker-library/openjdk/blob/47a6539cd18023dafb45db9013455136cc0bca07/8/jdk/alpine/Dockerfile)
ENV LANG C.UTF-8
RUN { \
echo '#!/bin/sh'; \
echo 'set -e'; \
echo; \
echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \
} > /usr/local/bin/docker-java-home \
&& chmod +x /usr/local/bin/docker-java-home
ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk
ENV PATH $PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin
ENV JAVA_VERSION 8u181
## Use "fuzzy" version matching to pin the version to a major/minor release
ENV JAVA_ALPINE_VERSION "~8"
RUN set -x \
&& apk add --no-cache \
openjdk8="$JAVA_ALPINE_VERSION" \
&& [ "$JAVA_HOME" = "$(docker-java-home)" ]
## Section copied from the Maven Dockerfile
RUN apk add --no-cache curl tar bash procps
ARG MAVEN_VERSION=3.5.4
ARG USER_HOME_DIR="/root"
ARG SHA=ce50b1c91364cb77efe3776f756a6d92b76d9038b0a0782f7d53acf1e997a14d
ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries
RUN mkdir -p /usr/share/maven /usr/share/maven/ref \
&& curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
&& echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \
&& tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \
&& rm -f /tmp/apache-maven.tar.gz \
&& ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
## Fission builder specific section
ADD build.sh /usr/local/bin/build
EXPOSE 8001
@@ -1,43 +0,0 @@
## Fission builder base image
ARG BUILDER_IMAGE=fission/builder:latest
FROM ${BUILDER_IMAGE}
## Section referred from the openjdk:8-jdk-alpine Dockerfile - (https://github.com/docker-library/openjdk/blob/47a6539cd18023dafb45db9013455136cc0bca07/8/jdk/alpine/Dockerfile)
ENV LANG C.UTF-8
RUN { \
echo '#!/bin/sh'; \
echo 'set -e'; \
echo; \
echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \
} > /usr/local/bin/docker-java-home \
&& chmod +x /usr/local/bin/docker-java-home
ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk
ENV PATH $PATH:/usr/lib/jvm/java-11-openjdk/jre/bin:/usr/lib/jvm/java-11-openjdk/bin
RUN set -x \
&& apk add --no-cache openjdk11 \
&& [ "$JAVA_HOME" = "$(docker-java-home)" ]
## Section copied from the Maven Dockerfile
RUN apk add --no-cache curl tar bash procps
ARG MAVEN_VERSION=3.5.4
ARG USER_HOME_DIR="/root"
ARG SHA=ce50b1c91364cb77efe3776f756a6d92b76d9038b0a0782f7d53acf1e997a14d
ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries
RUN mkdir -p /usr/share/maven /usr/share/maven/ref \
&& curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
&& echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \
&& tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \
&& rm -f /tmp/apache-maven.tar.gz \
&& ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
## Fission builder specific section
ADD build.sh /usr/local/bin/build
EXPOSE 8001
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
set -eou pipefail
mvn clean package
cp ${SRC_PKG}/target/*with-dependencies.jar ${DEPLOY_PKG}
-86
View File
@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.fission</groupId>
<artifactId>env-jvm-jersey</artifactId>
<version>0.0.1</version>
<properties>
<java.source.level>1.6</java.source.level>
<java.target.level>1.6</java.target.level>
</properties>
<dependencies>
<dependency>
<groupId>io.fission</groupId>
<artifactId>fission-jvm-jersey</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.17.v20190418</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.4.17.v20190418</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.source.level}</source>
<target>${java.target.level}</target>
<encoding>UTF-8</encoding>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>io.fission.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,35 +0,0 @@
package io.fission;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class FunctionLoadRequest {
private String filepath;
private String functionName;
private String url;
String getFilepath() {
return filepath;
}
void setFilepath(String filepath) {
this.filepath = filepath;
}
String getUrl() {
return url;
}
void setUrl(String url) {
this.url = url;
}
public String getFunctionName() {
return functionName;
}
public void setFunctionName(String functionName) {
this.functionName = functionName;
}
}
@@ -1,146 +0,0 @@
package io.fission;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.DELETE;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Context;
import javax.ws.rs.container.ContainerRequestContext;
import io.fission.Function;
@Path("/")
public class JerseyServer {
private static Function<ContainerRequestContext,Response> fn;
private static final int CLASS_LENGTH = 6;
private static Logger logger = Logger.getGlobal();
@GET
public Response home(@Context ContainerRequestContext request) {
return callUserFunction(request);
}
@POST
public Response homePost(@Context ContainerRequestContext request) {
return callUserFunction(request);
}
@PUT
public Response homePut(@Context ContainerRequestContext request) {
return callUserFunction(request);
}
@DELETE
public Response homeDelete(@Context ContainerRequestContext request) {
return callUserFunction(request);
}
@Path("v2/specialize")
@POST
public Response specialize(FunctionLoadRequest req) {
long startTime = System.nanoTime();
File file = new File(req.getFilepath());
if (!file.exists()) {
return Response.status(Response.Status.BAD_REQUEST).entity("/userfunc/usernot found").build();
}
String entryPoint = req.getFunctionName();
logger.log(Level.INFO, "Entrypoint class:" + entryPoint);
if (entryPoint == null) {
return Response.status(Response.Status.BAD_REQUEST).entity("Entrypoint class is missing in the JAR or the name is incorrect")
.build();
}
JarFile jarFile = null;
ClassLoader cl = null;
try {
jarFile = new JarFile(file);
Enumeration<JarEntry> e = jarFile.entries();
URL[] urls = { new URL("jar:file:" + file + "!/") };
// TODO Check if the class loading can be improved for ex. use something like:
// Thread.currentThread().setContextClassLoader(cl);
if (this.getClass().getClassLoader() == null) {
cl = URLClassLoader.newInstance(urls);
} else {
cl = URLClassLoader.newInstance(urls, this.getClass().getClassLoader());
}
if (cl == null) {
return Response.status(Response.Status.BAD_REQUEST).entity("Failed to initialize the class loader")
.build();
}
// Load all dependent classes from libraries etc.
while (e.hasMoreElements()) {
JarEntry je = e.nextElement();
if (je.isDirectory() || !je.getName().endsWith(".class")) {
continue;
}
String className = je.getName().substring(0, je.getName().length() - CLASS_LENGTH);
className = className.replace('/', '.');
cl.loadClass(className);
}
// Instantiate the function class
fn = (Function) cl.loadClass(entryPoint).newInstance();
} catch (MalformedURLException e) {
e.printStackTrace();
return Response.status(Response.Status.BAD_REQUEST).entity("Entrypoint class is missing in the function")
.build();
} catch (ClassNotFoundException e) {
e.printStackTrace();
return Response.status(Response.Status.BAD_REQUEST).entity("Error loading Function or dependent class")
.build();
} catch (InstantiationException e) {
e.printStackTrace();
return Response.status(Response.Status.BAD_REQUEST)
.entity("Error creating a new instance of function class").build();
} catch (IllegalAccessException e) {
e.printStackTrace();
return Response.status(Response.Status.BAD_REQUEST)
.entity("Error creating a new instance of function class").build();
} catch (IOException e) {
e.printStackTrace();
return Response.status(Response.Status.BAD_REQUEST).entity("Error reading the JAR file").build();
} finally {
try {
jarFile.close();
} catch (IOException e) {
e.printStackTrace();
return Response.status(Response.Status.BAD_REQUEST)
.entity("Error closing the file while loading the class").build();
}
}
long elapsedTime = System.nanoTime() - startTime;
logger.log(Level.INFO, "Specialize call done in: " + elapsedTime / 1000000 + " ms");
return Response.status(Response.Status.OK).entity("Done").build();
}
private Response callUserFunction(ContainerRequestContext httpRequest) {
if (fn == null) {
return Response.status(Response.Status.BAD_REQUEST).entity("Container not specialized").build();
} else {
return fn.call(httpRequest, null);
}
}
}
@@ -1,50 +0,0 @@
package io.fission;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
public class Main {
private static final int DEFAULT_PORT = 8888;
private int serverPort;
public Main(int serverPort) throws Exception {
this.serverPort = serverPort;
Server server = configureServer();
server.start();
server.join();
}
private Server configureServer() {
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.packages(JerseyServer.class.getPackage().getName());
resourceConfig.register(JacksonFeature.class);
ServletContainer servletContainer = new ServletContainer(resourceConfig);
ServletHolder sh = new ServletHolder(servletContainer);
Server server = new Server(serverPort);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.addServlet(sh, "/*");
server.setHandler(context);
return server;
}
public static void main(String[] args) throws Exception {
int serverPort = DEFAULT_PORT;
if(args.length >= 1) {
try {
serverPort = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new Main(serverPort);
}
}
-7
View File
@@ -1,7 +0,0 @@
.springBeans
.project
.mvn
.settings
.classpath
target/
bin/
-16
View File
@@ -1,16 +0,0 @@
FROM maven:3.5-jdk-8 as BUILD
WORKDIR /usr/src/myapp/
# To reuse the build cache, here we split maven dependency
# download and package into two RUN commands to avoid cache invalidation.
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src /usr/src/myapp/src/
RUN mvn package
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY --from=BUILD /usr/src/myapp/target/env-java-0.0.1-SNAPSHOT.jar /app.jar
ENTRYPOINT java ${JVM_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.jar --server.port=8888
EXPOSE 8888
-70
View File
@@ -1,70 +0,0 @@
# Fission: Java and JVM Environment
This is the JVM environment for Fission.
It's a Docker image containing a OpenJDK8 runtime, along with a
dynamic loader. A few dependencies are included in the
pom.xml file.
Looking for ready-to-run examples? See the [JVM examples directory](../../examples/jvm).
## Customizing this image
To add package dependencies, edit pom.xml to add what you
need, and rebuild this image (instructions below).
## Rebuilding and pushing the image
You'll need access to a Docker registry to push the image: you can
sign up for Docker hub at hub.docker.com, or use registries from
gcr.io, quay.io, etc. Let's assume you're using a docker hub account
called USER. Build and push the image to the the registry:
```
docker build -t USER/jvm-env . && docker push USER/jvm-env
```
## Using the image in fission
You can add this customized image to fission with "fission env
create":
```
fission env create --name jvm --image USER/jvm-env
```
Or, if you already have an environment, you can update its image:
```
fission env update --name jvm --image USER/jvm-env
```
After this, fission functions that have the env parameter set to the
same environment name as this command will use this environment.
## Web Server Framework
JVM environment uses Tomcat HTTP server by default as it is included in spring web. You can choose to use jetty or undertow by changing the dependency in pom.xml file as shown below.
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
```
## Java and JVM builder
JVM environment builder is based on OpenJDK8 and Maven 3.5.4 version. The default build command runs `mvn clean package` and uses the target/*with-dependencies.jar file for function. The default build command can be overridden as long as the uber jar file is copied to ${DEPLOY_PKG}.
-48
View File
@@ -1,48 +0,0 @@
## Fission builder base image
ARG BUILDER_IMAGE=fission/builder:latest
FROM ${BUILDER_IMAGE}
## Section copied from the openjdk:8-jdk-alpine Dockerfile - (https://github.com/docker-library/openjdk/blob/47a6539cd18023dafb45db9013455136cc0bca07/8/jdk/alpine/Dockerfile)
ENV LANG C.UTF-8
RUN { \
echo '#!/bin/sh'; \
echo 'set -e'; \
echo; \
echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \
} > /usr/local/bin/docker-java-home \
&& chmod +x /usr/local/bin/docker-java-home
ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk
ENV PATH $PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin
ENV JAVA_VERSION 8u181
## Use "fuzzy" version matching to pin the version to a major/minor release
ENV JAVA_ALPINE_VERSION "~8"
RUN set -x \
&& apk add --no-cache \
openjdk8="$JAVA_ALPINE_VERSION" \
&& [ "$JAVA_HOME" = "$(docker-java-home)" ]
## Section copied from the Maven Dockerfile
RUN apk add --no-cache curl tar bash procps
ARG MAVEN_VERSION=3.5.4
ARG USER_HOME_DIR="/root"
ARG SHA=ce50b1c91364cb77efe3776f756a6d92b76d9038b0a0782f7d53acf1e997a14d
ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries
RUN mkdir -p /usr/share/maven /usr/share/maven/ref \
&& curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
&& echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \
&& tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \
&& rm -f /tmp/apache-maven.tar.gz \
&& ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
## Fission builder specific section
ADD build.sh /usr/local/bin/build
EXPOSE 8001
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
set -eou pipefail
mvn clean package
cp ${SRC_PKG}/target/*with-dependencies.jar ${DEPLOY_PKG}
-50
View File
@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.fission</groupId>
<artifactId>env-java</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.fission</groupId>
<artifactId>fission-java-core</artifactId>
<version>0.0.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- Adding Sonatype repository to pull snapshots -->
<repositories>
<repository>
<id>fission-java-core</id>
<name>fission-java-core-snapshot</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>
</project>
@@ -1,32 +0,0 @@
package io.fission;
public class FunctionLoadRequest {
private String filepath;
private String functionName;
private String url;
String getFilepath() {
return filepath;
}
void setFilepath(String filepath) {
this.filepath = filepath;
}
String getUrl() {
return url;
}
void setUrl(String url) {
this.url = url;
}
public String getFunctionName() {
return functionName;
}
public void setFunctionName(String functionName) {
this.functionName = functionName;
}
}
@@ -1,122 +0,0 @@
package io.fission;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import io.fission.Function;
@RestController
@EnableAutoConfiguration
public class Server {
private Function fn;
private static final int CLASS_LENGTH = 6;
private static Logger logger = Logger.getGlobal();
@RequestMapping(value = "/", method = { RequestMethod.GET, RequestMethod.POST, RequestMethod.DELETE,
RequestMethod.PUT })
ResponseEntity<Object> home(RequestEntity<?> req) {
if (fn == null) {
return ResponseEntity.badRequest().body("Container not specialized");
} else {
return ((ResponseEntity<Object>) ((Function) fn).call(req, null));
}
}
@PostMapping(path = "/v2/specialize", consumes = "application/json")
ResponseEntity<String> specialize(@RequestBody FunctionLoadRequest req) {
long startTime = System.nanoTime();
File file = new File(req.getFilepath());
if (!file.exists()) {
return ResponseEntity.badRequest().body("/userfunc/user not found");
}
String entryPoint = req.getFunctionName();
logger.log(Level.INFO, "Entrypoint class:" + entryPoint);
if (entryPoint == null) {
return ResponseEntity.badRequest().body("Entrypoint class is missing in the function");
}
JarFile jarFile = null;
ClassLoader cl = null;
try {
jarFile = new JarFile(file);
Enumeration<JarEntry> e = jarFile.entries();
URL[] urls = { new URL("jar:file:" + file + "!/") };
// TODO Check if the class loading can be improved for ex. use something like:
// Thread.currentThread().setContextClassLoader(cl);
if (this.getClass().getClassLoader() == null) {
cl = URLClassLoader.newInstance(urls);
} else {
cl = URLClassLoader.newInstance(urls, this.getClass().getClassLoader());
}
if (cl == null) {
return ResponseEntity.status(500).body("Failed to initialize the class loader");
}
// Load all dependent classes from libraries etc.
while (e.hasMoreElements()) {
JarEntry je = e.nextElement();
if (je.isDirectory() || !je.getName().endsWith(".class")) {
continue;
}
String className = je.getName().substring(0, je.getName().length() - CLASS_LENGTH);
className = className.replace('/', '.');
cl.loadClass(className);
}
// Instantiate the function class
fn = (Function) cl.loadClass(entryPoint).newInstance();
} catch (MalformedURLException e) {
e.printStackTrace();
return ResponseEntity.badRequest().body("Error loading the Function class file");
} catch (ClassNotFoundException e) {
e.printStackTrace();
return ResponseEntity.badRequest().body("Error loading Function or dependent class");
} catch (InstantiationException e) {
e.printStackTrace();
return ResponseEntity.badRequest().body("Error creating a new instance of function class");
} catch (IllegalAccessException e) {
e.printStackTrace();
return ResponseEntity.badRequest().body("Error creating a new instance of function class");
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.badRequest().body("Error reading the JAR file");
} finally {
try {
// cl.close();
jarFile.close();
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.badRequest().body("Error closing the file while loading the class");
}
}
long elapsedTime = System.nanoTime() - startTime;
logger.log(Level.INFO, "Specialize call done in: " + elapsedTime / 1000000 + " ms");
return ResponseEntity.ok("Done");
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Server.class, args);
}
}
-1
View File
@@ -1 +0,0 @@
node_modules/
-18
View File
@@ -1,18 +0,0 @@
# A docker image for the func container.
# default variant is the official alpine node image (much smaller than the standard image)
FROM node:8-alpine
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install && npm cache clean --force
COPY server.js /usr/src/app/server.js
CMD [ "npm", "start" ]
EXPOSE 8888
-15
View File
@@ -1,15 +0,0 @@
FROM node:12.16-alpine3.11
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install && npm cache clean --force
COPY server.js /usr/src/app/server.js
CMD [ "npm", "start" ]
EXPOSE 8888
-18
View File
@@ -1,18 +0,0 @@
# A docker image for the func container.
# debian variant is the official standard node image (larger than the alpine image)
FROM node:8
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install && npm cache clean --force
COPY server.js /usr/src/app/server.js
CMD [ "npm", "start" ]
EXPOSE 8888
-47
View File
@@ -1,47 +0,0 @@
# Fission: NodeJS Environment
This is the NodeJS environment for Fission.
It's a Docker image containing a NodeJS runtime, along with a dynamic
loader. A few common dependencies are included in the package.json
file.
Looking for ready-to-run examples? See the [NodeJS examples directory](../../examples/nodejs).
## Customizing this image
To add package dependencies, edit package.json to add what you need,
and rebuild this image (instructions below).
You also may want to customize what's available to the function in its
request context. You can do this by editing `server.js` (see the
comment in that file about customizing request context).
## Rebuilding and pushing the image
You'll need access to a Docker registry to push the image: you can
sign up for Docker hub at hub.docker.com, or use registries from
gcr.io, quay.io, etc. Let's assume you're using a docker hub account
called USER. Build and push the image to the the registry:
```
docker build -t USER/nodejs-env . && docker push USER/nodejs-env
```
## Using the image in fission
You can add this customized image to fission with "fission env
create":
```
fission env create --name nodejs --image USER/nodejs-env
```
Or, if you already have an environment, you can update its image:
```
fission env update --name nodejs --image USER/nodejs-env
```
After this, fission functions that have the env parameter set to the
same environment name as this command will use this environment.
-11
View File
@@ -1,11 +0,0 @@
ARG BUILDER_IMAGE=fission/builder
FROM ${BUILDER_IMAGE}
# default variant is the official alpine node image (much smaller than the standard image)
FROM node:8-alpine
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
COPY --from=0 /builder /builder
ADD build.sh /usr/local/bin/build
RUN chmod +x /usr/local/bin/build
@@ -1,11 +0,0 @@
ARG BUILDER_IMAGE=fission/builder
FROM ${BUILDER_IMAGE}
# default variant is the official alpine node image (much smaller than the standard image)
FROM node:12.16-alpine3.11
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
COPY --from=0 /builder /builder
ADD build.sh /usr/local/bin/build
RUN chmod +x /usr/local/bin/build
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
cd ${SRC_PKG}
npm install && cp -r ${SRC_PKG} ${DEPLOY_PKG}
-30
View File
@@ -1,30 +0,0 @@
{
"name": "fission-nodejs-runtime",
"version": "0.1.0",
"author": "Soam Vasani",
"contributors": [
{
"name": "Soam Vasani",
"email": "soamvasani+1@gmail.com"
},
{
"name": "Gary Yeap",
"email": "contact@garyyeap.com"
}
],
"description": "NodeJS run container for the fission framework",
"engines": {
"node": ">=7.6.0"
},
"dependencies": {
"body-parser": "*",
"co": "~4.6.0",
"express": "*",
"minimist": "*",
"morgan": "*",
"mz": "~2.7.0",
"request": "^2.81.0",
"underscore": ">=1.8.3",
"request-promise-native": "^1.0.3"
}
}
-168
View File
@@ -1,168 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const process = require('process');
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const morgan = require('morgan');
const argv = require('minimist')(process.argv.slice(1));// Command line opts
if (!argv.port) {
argv.port = 8888;
}
// To catch unhandled exceptions thrown by user code async callbacks,
// these exceptions cannot be catched by try-catch in user function invocation code below
process.on('uncaughtException', (err) => {
console.error(`Caught exception: ${err}`);
});
// User function. Starts out undefined.
let userFunction;
function loadFunction(modulepath, funcname) {
// Read and load the code. It's placed there securely by the fission runtime.
try {
let startTime = process.hrtime();
// support v1 codepath and v2 entrypoint like 'foo', '', 'index.hello'
let userFunction = funcname ? require(modulepath)[funcname] : require(modulepath);
let elapsed = process.hrtime(startTime);
console.log(`user code loaded in ${elapsed[0]}sec ${elapsed[1]/1000000}ms`);
return userFunction;
} catch(e) {
console.error(`user code load error: ${e}`);
return e;
}
}
function withEnsureGeneric(func) {
return function(req, res) {
// Make sure we're a generic container. (No reuse of containers.
// Once specialized, the container remains specialized.)
if (userFunction) {
res.status(400).send("Not a generic container");
return;
}
func(req, res);
}
}
function isFunction(func) {
return func && func.constructor && func.call && func.apply;
}
function specializeV2(req, res) {
// for V2 entrypoint, 'filename.funcname' => ['filename', 'funcname']
const entrypoint = req.body.functionName ? req.body.functionName.split('.') : [];
// for V2, filepath is dynamic path
const modulepath = path.join(req.body.filepath, entrypoint[0] || '');
const result = loadFunction(modulepath, entrypoint[1]);
if(isFunction(result)){
userFunction = result;
res.status(202).send();
} else {
res.status(500).send(JSON.stringify(result));
}
}
function specialize(req, res) {
// Specialize this server to a given user function. The user function
// is read from argv.codepath; it's expected to be placed there by the
// fission runtime.
//
const modulepath = argv.codepath || '/userfunc/user';
// Node resolves module paths according to a file's location. We load
// the file from argv.codepath, but tell users to put dependencies in
// the server's package.json; this means the function's dependencies
// are in /usr/src/app/node_modules. We could be smarter and have the
// function deps in the right place in argv.codepath; b ut for now we
// just symlink the function's node_modules to the server's
// node_modules.
fs.symlinkSync('/usr/src/app/node_modules', `${path.dirname(modulepath)}/node_modules`);
const result = loadFunction(modulepath);
if(isFunction(result)){
userFunction = result;
res.status(202).send();
} else {
res.status(500).send(JSON.stringify(result));
}
}
// Request logger
app.use(morgan('combined'));
let bodyParserLimit = process.env.BODY_PARSER_LIMIT || '1mb';
app.use(bodyParser.urlencoded({ extended: false, limit: bodyParserLimit}));
app.use(bodyParser.json({limit: bodyParserLimit}));
app.use(bodyParser.raw({limit: bodyParserLimit}));
app.use(bodyParser.text({ type : "text/*", limit: bodyParserLimit }));
app.post('/specialize', withEnsureGeneric(specialize));
app.post('/v2/specialize', withEnsureGeneric(specializeV2));
// Generic route -- all http requests go to the user function.
app.all('/', function (req, res) {
if (!userFunction) {
res.status(500).send("Generic container: no requests supported");
return;
}
const context = {
request: req,
response: res
// TODO: context should also have: URL template params, query string
};
function callback(status, body, headers) {
if (!status)
return;
if (headers) {
for (let name of Object.keys(headers)) {
res.set(name, headers[name]);
}
}
res.status(status).send(body);
}
//
// Customizing the request context
//
// If you want to modify the context to add anything to it,
// you can do that here by adding properties to the context.
//
if (userFunction.length <= 1) { // One or zero argument (context)
let result;
// Make sure their function returns a promise
if (userFunction.length === 0) {
result = Promise.resolve(userFunction())
} else {
result = Promise.resolve(userFunction(context))
}
result.then(function({status, body, headers}) {
callback(status, body, headers);
}).catch(function(err) {
console.log(`Function error: ${err}`);
callback(500, "Internal server error");
});
} else { // 2 arguments (context, callback)
try {
userFunction(context, callback);
} catch (err) {
console.log(`Function error: ${err}`);
callback(500, "Internal server error");
}
}
});
app.listen(argv.port);
-9
View File
@@ -1,9 +0,0 @@
module.exports = async function (context) {
console.log("headers=", JSON.stringify(context.request.headers));
console.log("body=", JSON.stringify(context.request.body));
return {
status: 200,
body: "Hello, world !\n"
};
}

Some files were not shown because too many files have changed in this diff Show More