Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b9fa5bc30 | ||
|
|
8d103fa35b | ||
|
|
9d1b096bac | ||
|
|
d83c89df69 | ||
|
|
c03f1a6d9c | ||
|
|
4fc55b3b3c | ||
|
|
6f017cf400 | ||
|
|
b19be5c0a3 | ||
|
|
9920008916 | ||
|
|
68ab7efcd4 | ||
|
|
3693082004 | ||
|
|
dd6bfd73e2 | ||
|
|
fe9f200606 | ||
|
|
3165fe7719 | ||
|
|
a4124abeb2 | ||
|
|
789a7d25ce | ||
|
|
31f94dbc12 | ||
|
|
6bca24a3f5 | ||
|
|
961ee069ed | ||
|
|
b5e3e4e625 | ||
|
|
00a8430a24 | ||
|
|
f79d44156b | ||
|
|
a52425a31d | ||
|
|
45f3095fb1 | ||
|
|
96ae2d3d86 | ||
|
|
6f8a2e4789 | ||
|
|
00c2883235 | ||
|
|
42261d8810 | ||
|
|
7d83f5713a | ||
|
|
e238776bf7 | ||
|
|
37aa266a4d | ||
|
|
85983c91cd | ||
|
|
a8fc7a7029 | ||
|
|
2036d80778 | ||
|
|
46ff55ab49 | ||
|
|
51c84f3cfc | ||
|
|
fe4015fab2 | ||
|
|
22e0c10d07 | ||
|
|
e4fe007838 | ||
|
|
0fd60eab3b | ||
|
|
ae87c0ea37 | ||
|
|
55651d49e0 | ||
|
|
1f0ca47550 | ||
|
|
e619a70661 | ||
|
|
d248bb89ea | ||
|
|
5fe4b276eb |
@@ -10,3 +10,4 @@ environments/php7/vendor/
|
||||
*~
|
||||
|
||||
vendor/
|
||||
local/
|
||||
+25
-11
@@ -1,20 +1,34 @@
|
||||
sudo: required
|
||||
|
||||
dist: trusty
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8
|
||||
- tip
|
||||
- 1.8
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/google-cloud-sdk/
|
||||
- $HOME/k8scli
|
||||
|
||||
install:
|
||||
- go get github.com/Masterminds/glide
|
||||
- curl -L https://github.com/coreos/etcd/releases/download/v3.0.15/etcd-v3.0.15-linux-amd64.tar.gz -o /tmp/etcd-v3.0.15-linux-amd64.tar.gz && mkdir -p /tmp/test-etcd && tar xzvf /tmp/etcd-v3.0.15-linux-amd64.tar.gz -C /tmp/test-etcd --strip-components=1
|
||||
- go get github.com/Masterminds/glide
|
||||
- hack/travis-kube-setup.sh
|
||||
|
||||
env:
|
||||
- KUBECONFIG=${HOME}/.kube/config PATH=$HOME/k8scli:${PATH}
|
||||
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- glide install
|
||||
- ./fission-bundle/build.sh
|
||||
- hack/verify-gofmt.sh
|
||||
- /tmp/test-etcd/etcd &
|
||||
- go test -v -i $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go')
|
||||
- go test -v $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go')
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- glide install
|
||||
- ./fission-bundle/build.sh
|
||||
- hack/verify-gofmt.sh
|
||||
- hack/runtests.sh
|
||||
- test/build_and_test.sh
|
||||
|
||||
notifications:
|
||||
slack:
|
||||
secure: YZ34vsfw1TtftJypg1MyP4+ihONI4gaxeS3FghBQli6+EezjzcxOXyj5VD+x0ucXfDeTaDrFfmVN0SAGObOoZUE+ea9KAoTo50tRLaD9kwOTACiekZalC4uuBguH0D1/A6vlbU8dchsr9mvIhbisG6mTncdPtqGYYHtyBQme6ngmmHbVAQFZcIBHmNuDb/HWhSr8KMEuyB6+mBXLYELHnXnf26cOhdGNaagqCOJTiemX85RGwIuOPxyBhKDFMLyHohDT7FJMH/qijveE6YgOYTQC5nYc2Np1KvC7hQkIu4nuyczyYlrNQl/TWv+SVI8PjIs0PYuCuD3gUqoVEi8d94HbrOPzFEpbwDS9P4qL39DGmco1Q56Vqxe6sRI0vDWPb5gCP1lSgs3PMECVn7Wor/pTvcL+C+U2jLwWJUl0vbyWCL7ngl/3iTssV7qBpUrI7Oclwp8LrQo9fPj0DL4gE9rNanpEWjQ6yPGaysIL1zLHtRghhm52A22NJGp71jkS2KEpLi6ZWFYjMeuXw5eOQFhqFlzyRJOmLYa3B607TLWuyo2L2CxAfMmq0FGfemvrkLZIWtlQKK4y9ImpsURwaGT2XCtThFtHl77wEss913nC+T2dX3O5Bl0UmxFd5S3mVM109I8c4lDosxnAjRfS9MheFlrG0gjSJSBCw57x7f0=
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at soamvasani@platform9.com. All
|
||||
reported by contacting the project team at codeofconduct@platform9.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
Compiling Fission
|
||||
=================
|
||||
|
||||
[You only need to do this if you're making Fission changes; if you're
|
||||
just deploying Fission, use fission.yaml which points to prebuilt
|
||||
images.]
|
||||
|
||||
You'll need the `go` compiler and tools installed, along with the
|
||||
[glide dependency management
|
||||
tool](https://github.com/Masterminds/glide#install). You'll also need
|
||||
docker for building images.
|
||||
|
||||
The server side is compiled as one binary ("fission-bundle") which
|
||||
contains controller, poolmgr and router; it invokes the right one
|
||||
based on command-line arguments.
|
||||
|
||||
To build fission-bundle: clone this repo to
|
||||
`$GOPATH/src/github.com/fission/fission`, then from the top level
|
||||
directory (if you want to build the image with the docker inside
|
||||
minikube, you'll need to set the proper environment variables with
|
||||
`eval $(minikube docker-env)`):
|
||||
|
||||
```
|
||||
# Get dependencies
|
||||
$ glide install
|
||||
|
||||
# Build fission server and an image
|
||||
$ pushd fission-bundle
|
||||
$ ./build.sh
|
||||
```
|
||||
|
||||
You now need to build the docker image for fission. You can use
|
||||
`push.sh` and push it to a docker hub account. But it's easiest to use
|
||||
minikube and its built-in docker daemon:
|
||||
|
||||
```
|
||||
$ eval $(minikube docker-env)
|
||||
$ docker build -t minikube/fission-bundle .
|
||||
```
|
||||
|
||||
Next, install fission on your kubernetes cluster:
|
||||
|
||||
```
|
||||
# To install, update fission.yaml to point to the image you just made: "minikube/fission-bundle"
|
||||
$ $EDITOR fission.yaml
|
||||
$ kubectl create -f fission.yaml
|
||||
$ kubectl create -f fission-nodeport.yaml
|
||||
```
|
||||
|
||||
And if you're changing the CLI too, you can build it with:
|
||||
|
||||
```
|
||||
# Build Fission CLI
|
||||
$ cd fission && go install
|
||||
```
|
||||
@@ -95,6 +95,19 @@ While a few simple retries are done, there isn't yet a reliable
|
||||
message bus between Kubewatcher and the function. Work for this is
|
||||
tracked in issue #64.
|
||||
|
||||
Message Queue Trigger
|
||||
---------------------
|
||||
|
||||
A message queue trigger binds a message queue topic to a function:
|
||||
events from that topic cause the function to be invoked with the
|
||||
message as the body of the request. The trigger may also contain a
|
||||
response topic: if specified, the function's output is sent to this
|
||||
response.
|
||||
|
||||
Here's a diagram of the components:
|
||||
|
||||

|
||||
|
||||
Environment Container
|
||||
---------------------
|
||||
|
||||
@@ -113,7 +126,7 @@ volume shared between fetcher and this environment container. Poolmgr
|
||||
then requests the container to load the function.
|
||||
|
||||
Logger
|
||||
-----------
|
||||
------
|
||||
|
||||
Logger helps to forward function logs to centralized db service for log
|
||||
persistence. Currently only influxdb is supported to store logs.
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# Environment V2 Fission-Environment API
|
||||
|
||||
Fission Environments are the language-specific component of fission.
|
||||
|
||||
They must satisfy the interface in this spec.
|
||||
|
||||
## Meta
|
||||
|
||||
This is version 2.0-alpha of the Fission-Environment API.
|
||||
|
||||
(It's unstable and may change without warning until 2.0-beta.)
|
||||
|
||||
## Overview
|
||||
|
||||
Fission V2 Environments consist of:
|
||||
|
||||
* Metadata
|
||||
* A runtime image
|
||||
* A builder image (optional)
|
||||
* Function Interface Specification
|
||||
* User Documentation
|
||||
* Examples
|
||||
|
||||
### Metadata
|
||||
|
||||
See EnvironmentSpec, Runtime, and Builder in types.go.
|
||||
|
||||
## Runtime Image
|
||||
|
||||
An environment runtime image is a docker container image. It must run
|
||||
a server that has two jobs:
|
||||
|
||||
(a) Loading a "function" from a file path on demand
|
||||
(b) Invoking that function on request
|
||||
|
||||
### Function Loading
|
||||
|
||||
The environment must expose a single HTTP endpoint (at the port and
|
||||
URL specified in the metadata) that loads a function. The function
|
||||
load request is a JSON-serialized `FunctionLoadRequest`.
|
||||
|
||||
The function load request contains a filepath to load the function
|
||||
from. Fission does not define in any way the contents of the path; it
|
||||
is completely environment-dependent. It may be a single file, or a
|
||||
directory (in case of deployment packages).
|
||||
|
||||
The load request may contain an EntryPoint. If it does, the loader
|
||||
must interpret this; usually it's the name of a function in a module
|
||||
or package containing multiple functions. If there is no entrypoint,
|
||||
the environment must use a default; again, the value of thsi default
|
||||
is environment-specific.
|
||||
|
||||
The load request may contain a URL. If it does, requests to that URL
|
||||
should be routed to the function. It defaults to "/".
|
||||
|
||||
### Function Invocation
|
||||
|
||||
Functions are invoked on HTTP request to the server. The port for the
|
||||
request on the runtime container is defined in the Runtime metadata,
|
||||
and the URL for the request is specified in the FunctionLoadRequest.
|
||||
|
||||
The interface of the function is environment specific; the environment
|
||||
must come with a spec for this interface.
|
||||
|
||||
## Builder
|
||||
|
||||
The builder is a container image that contains tools to build a
|
||||
function from source. The source may be a single file or a directory
|
||||
of files.
|
||||
|
||||
The builder container is invoked with the specified command, with the
|
||||
following params:
|
||||
|
||||
1. File path of the source
|
||||
2. File path where the output should go
|
||||
3. Other env or function-specific params passed by the user
|
||||
|
||||
The first two parameters are file paths, and all remaining params are
|
||||
environment-specific.
|
||||
|
||||
The output of the builder should be something that the runtime can
|
||||
load and run -- there should be no intermediate steps that need user
|
||||
intervention.
|
||||
|
||||
### Errors
|
||||
|
||||
## Function Interface Spec
|
||||
|
||||
The function interface spec is a document that specifies the interface
|
||||
of functions and their semantics. It must specify:
|
||||
|
||||
* How functions are invoked (sync, async)
|
||||
* How the request context is provided to the function (URL, headers, request type, request body)
|
||||
* Function logging
|
||||
* Semantics of function errors and exceptions
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
The docs should contain everything necessary to use the environment:
|
||||
|
||||
* How to add it to a fission cluster
|
||||
* How to write and build functions for this environment (link to the interface spec)
|
||||
* How to modify and rebuild the environment itself
|
||||
|
||||
## Examples
|
||||
|
||||
Suggested examples to provide:
|
||||
|
||||
* A simple "Hello world"
|
||||
* A function that demonstrates use of the request context: url params,
|
||||
request headers, request body
|
||||
* A function that does logging
|
||||
* A multi-file function package
|
||||
* A function with dependencies
|
||||
* Functions with shared code
|
||||
|
||||
## Compatibility with v1
|
||||
|
||||
V1 environment images can be used as v2 environment runtime images.
|
||||
+75
-22
@@ -1,14 +1,18 @@
|
||||
* [Running Fission on your Cluster](#running-fission-on-your-cluster)
|
||||
* [Setup Kubernetes](#setup-kubernetes)
|
||||
* [Mac](#install-and-start-kubernetes-on-osx)
|
||||
* [Linux](#or-install-and-start-kubernetes-on-linux)
|
||||
* [Verify access to the cluster](#verify-access-to-the-cluster)
|
||||
* [Get and Run Fission: Minikube or Local cluster](#get-and-run-fission-minikube-or-local-cluster)
|
||||
* [Get and Run Fission: GKE or other Cloud](#get-and-run-fission-gke-or-other-cloud)
|
||||
* [Install the client CLI](#install-the-client-cli)
|
||||
* [Run an example](#run-an-example)
|
||||
* [Enable Persistent Function Logs (Optional)](#enable-persistent-function-logs-optional)
|
||||
* [Use the web based Fission-ui (Optional)](#use-the-web-based-fission-ui-optional)
|
||||
|
||||
- [Running Fission on your Cluster](#running-fission-on-your-cluster)
|
||||
* [Setup Kubernetes](#setup-kubernetes)
|
||||
+ [Install and start Kubernetes on OSX:](#install-and-start-kubernetes-on-osx)
|
||||
+ [Or, install and start Kubernetes on Linux:](#or-install-and-start-kubernetes-on-linux)
|
||||
* [Verify access to the cluster](#verify-access-to-the-cluster)
|
||||
* [Get and Run Fission: Minikube or Local cluster](#get-and-run-fission-minikube-or-local-cluster)
|
||||
* [Get and Run Fission: GKE or other Cloud](#get-and-run-fission-gke-or-other-cloud)
|
||||
* [Get and Run Fission: OpenShift](#get-and-run-fission-openshift)
|
||||
+ [Using Minishift or Local Cluster](#using-minishift-or-local-cluster)
|
||||
+ [Using other clouds](#using-other-clouds)
|
||||
* [Install the client CLI](#install-the-client-cli)
|
||||
* [Run an example](#run-an-example)
|
||||
* [Enable Persistent Function Logs (Optional)](#enable-persistent-function-logs-optional)
|
||||
* [Use the web based Fission-ui (Optional)](#use-the-web-based-fission-ui-optional)
|
||||
|
||||
## Running Fission on your Cluster
|
||||
|
||||
@@ -45,8 +49,8 @@ set up services with NodePort. This exposes fission on ports 31313
|
||||
and 31314.
|
||||
|
||||
```
|
||||
$ kubectl create -f http://fission.io/fission.yaml
|
||||
$ kubectl create -f http://fission.io/fission-nodeport.yaml
|
||||
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-rbac.yaml
|
||||
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-nodeport.yaml
|
||||
```
|
||||
|
||||
Set the FISSION_URL and FISSION_ROUTER environment variables.
|
||||
@@ -68,8 +72,8 @@ If you're using GKE or any other cloud provider that supports the
|
||||
LoadBalancer service type, use these commands:
|
||||
|
||||
```
|
||||
$ kubectl create -f http://fission.io/fission.yaml
|
||||
$ kubectl create -f http://fission.io/fission-cloud.yaml
|
||||
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-rbac.yaml
|
||||
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-cloud.yaml
|
||||
```
|
||||
|
||||
Save the external IP addresses of controller and router services in
|
||||
@@ -84,8 +88,11 @@ svc```). Then:
|
||||
|
||||
### Get and Run Fission: OpenShift
|
||||
|
||||
If you're using OpenShift, it's possible to run Fission on it! The deployment
|
||||
template needs to be deployed as a user with cluster-admin permissions (like `system:admin`), as it needs to create a `ClusterRole` for deploying function containers from the `fission` namespace/project.
|
||||
If you're using OpenShift, it's possible to run Fission on it! The
|
||||
deployment template needs to be deployed as a user with cluster-admin
|
||||
permissions (like `system:admin`), as it needs to create a
|
||||
`ClusterRole` for deploying function containers from the `fission`
|
||||
namespace/project.
|
||||
|
||||
Identically as with Kubernetes, you need to set the FISSION_URL and FISSION_ROUTER environment variables. If you're using minishift, use these commands:
|
||||
|
||||
@@ -93,21 +100,48 @@ Identically as with Kubernetes, you need to set the FISSION_URL and FISSION_ROUT
|
||||
$ export FISSION_URL=http://$(minishift ip):31313¬
|
||||
$ export FISSION_ROUTER=$(minishift ip):31314¬
|
||||
```
|
||||
#### Using Minishift or Local Cluster
|
||||
|
||||
If you're using minishift or no cloud provider, use these commands to set up services with NodePort. This exposes fission on ports 31313 and 31314.
|
||||
|
||||
```
|
||||
$ oc login -u system:admin
|
||||
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-openshift.yaml
|
||||
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-nodeport.yaml
|
||||
```
|
||||
|
||||
#### Using other clouds
|
||||
|
||||
If you're using any cloud provider that supports the LoadBalancer service type, use these commands:
|
||||
|
||||
```
|
||||
$ oc login -u system:admin
|
||||
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-openshift.yaml
|
||||
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-cloud.yaml
|
||||
```
|
||||
After these steps, you should be able to run fission client as with kubernetes.
|
||||
|
||||
### Install the client CLI
|
||||
|
||||
#### Mac OS
|
||||
|
||||
Get the CLI binary for Mac:
|
||||
|
||||
```
|
||||
$ curl http://fission.io/mac/fission > fission && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
```
|
||||
|
||||
Or Linux:
|
||||
#### Linux
|
||||
|
||||
```
|
||||
$ curl http://fission.io/linux/fission > fission && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
For Windows, you can use the linux binary on WSL. Or you can download
|
||||
this windows executable: [fission.exe](https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-windows.exe)
|
||||
|
||||
### Run an example
|
||||
|
||||
Finally, you're ready to use Fission!
|
||||
@@ -132,7 +166,7 @@ Fission uses InfluxDB to store logs and fluentd to forward them from
|
||||
function pods into InfluxDB.
|
||||
|
||||
```
|
||||
$ kubectl create -f fission-logger.yaml
|
||||
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-logger.yaml
|
||||
```
|
||||
|
||||
That's it for the basic setup. You can now use following command to view function logs:
|
||||
@@ -159,10 +193,29 @@ It allows users to observe and manage fission. It also provides a simple online
|
||||
To setup Fission-ui with fission in k8s is simple:
|
||||
|
||||
```bash
|
||||
# After Fission deployed
|
||||
# Run this after fission is deployed
|
||||
$ kubectl create -f https://raw.githubusercontent.com/fission/fission-ui/master/docker/fission-ui.yaml
|
||||
```
|
||||
|
||||
Then open `http://node-ip:31319` to use Fission-ui.
|
||||
|
||||
For more infomation, please check out [Fission-ui Readme](https://github.com/fission/fission-ui/blob/master/README.md).
|
||||
|
||||
### Install NATS for message-queue based triggers (Optional)
|
||||
|
||||
Fission supports message queue triggers that allow you to invoke
|
||||
functions based on events in a queue. For now, NATS-Streaming is the
|
||||
only supported message queue.
|
||||
|
||||
You can install NATS Streaming on your Kubernetes cluster with:
|
||||
|
||||
```
|
||||
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-nats.yaml
|
||||
```
|
||||
|
||||
You can subscribe to a NATS Streaming queue with a command like this:
|
||||
(See `fission mqtrigger --help` for details)
|
||||
|
||||
```
|
||||
$ fission mqtrigger create --name myQueueTrigger --function processEvent --topic "myQueue.request"
|
||||
```
|
||||
|
||||
@@ -15,8 +15,8 @@ extend Fission if you want to.
|
||||
|
||||
Fission is extensible to any language; the core is written in Go, and
|
||||
language-specific parts are isolated in something called
|
||||
_environments_ (more below). Fission currently supports NodeJS and
|
||||
Python, with more languages coming soon.
|
||||
_environments_ (more below). Fission currently supports NodeJS, Python, Ruby, Go,
|
||||
PHP, Bash, and any Linux executable, with more languages coming soon.
|
||||
|
||||
### Performance: 100msec cold start
|
||||
|
||||
@@ -81,187 +81,12 @@ See the [examples](examples) directory for more.
|
||||
Running Fission on your Cluster
|
||||
===============================
|
||||
|
||||
### Setup Kubernetes
|
||||
|
||||
You can install Kubernetes on your laptop with [minikube](https://github.com/kubernetes/minikube):
|
||||
|
||||
#### Install and start Kubernetes on OSX:
|
||||
```bash
|
||||
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin
|
||||
$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.16.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
|
||||
$ minikube start
|
||||
```
|
||||
|
||||
#### Or, install and start Kubernetes on Linux:
|
||||
```bash
|
||||
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin
|
||||
$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.16.0/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
|
||||
$ minikube start
|
||||
```
|
||||
|
||||
Or, you can use [Google Container Engine's](https://cloud.google.com/container-engine/) free trial to get a 3 node cluster.
|
||||
|
||||
### Verify access to the cluster
|
||||
|
||||
```
|
||||
$ kubectl version
|
||||
```
|
||||
|
||||
### Get and Run Fission: Minikube or Local cluster
|
||||
|
||||
If you're using minikube or no cloud provider, use these commands to
|
||||
set up services with NodePort. This exposes fission on ports 31313
|
||||
and 31314.
|
||||
|
||||
```
|
||||
$ kubectl create -f http://fission.io/fission.yaml
|
||||
$ kubectl create -f http://fission.io/fission-nodeport.yaml
|
||||
```
|
||||
|
||||
Set the FISSION_URL and FISSION_ROUTER environment variables.
|
||||
FISSION_URL is used by the fission CLI to find the server.
|
||||
FISSION_URL should be prefixed with a `http://`. (FISSION_ROUTER is
|
||||
only needed for the examples below to work.)
|
||||
|
||||
If you're using minikube, use these commands:
|
||||
|
||||
```
|
||||
$ export FISSION_URL=http://$(minikube ip):31313
|
||||
$ export FISSION_ROUTER=$(minikube ip):31314
|
||||
```
|
||||
|
||||
|
||||
### Get and Run Fission: GKE or other Cloud
|
||||
|
||||
If you're using GKE or any other cloud provider that supports the
|
||||
LoadBalancer service type, use these commands:
|
||||
|
||||
```
|
||||
$ kubectl create -f http://fission.io/fission.yaml
|
||||
$ kubectl create -f http://fission.io/fission-cloud.yaml
|
||||
```
|
||||
|
||||
Save the external IP addresses of controller and router services in
|
||||
FISSION_URL and FISSION_ROUTER, respectively. Wait for services to
|
||||
get IP addresses (check this with ```kubectl --namespace fission get
|
||||
svc```). Then:
|
||||
|
||||
```
|
||||
$ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..ip}')
|
||||
$ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}')
|
||||
```
|
||||
### Get and run fission: OpenShift
|
||||
|
||||
If you're using OpenShift, it's possible to run Fission on it! The deployment
|
||||
template needs to be deployed as a user with cluster-admin permissions (like `system:admin`), as it needs to create a `ClusterRole` for deploying function containers from the `fission` namespace/project.
|
||||
|
||||
Identically as with Kubernetes, you need to set the FISSION_URL and FISSION_ROUTER environment variables. If you're using minishift, use these commands:
|
||||
|
||||
```
|
||||
$ export FISSION_URL=http://$(minishift ip):31313¬
|
||||
$ export FISSION_ROUTER=$(minishift ip):31314¬
|
||||
```
|
||||
|
||||
#### Using Minishift or Local Cluster
|
||||
|
||||
If you're using minishift or no cloud provider, use these commands to set up services with NodePort. This exposes fission on ports 31313 and 31314.
|
||||
|
||||
```
|
||||
$ oc login -u system:admin
|
||||
$ oc create -f http://fission.io/fission-openshift.yaml
|
||||
$ oc create -f http://fission.io/fission-nodeport.yaml
|
||||
```
|
||||
|
||||
#### Using other clouds
|
||||
If you're using any cloud provider that supports the LoadBalancer service type, use these commands:
|
||||
|
||||
```
|
||||
$ oc login -u system:admin
|
||||
$ oc create -f http://fission.io/fission-openshift.yaml
|
||||
$ oc create -f http://fission.io/fission-cloud.yaml
|
||||
```
|
||||
After these steps, you should be able to run fission client as with kubernetes.
|
||||
|
||||
### Install the client CLI
|
||||
|
||||
Get the CLI binary for Mac:
|
||||
|
||||
```
|
||||
$ curl http://fission.io/mac/fission > fission && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
```
|
||||
|
||||
Or Linux:
|
||||
|
||||
```
|
||||
$ curl http://fission.io/linux/fission > fission && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
```
|
||||
|
||||
### Run an example
|
||||
|
||||
Finally, you're ready to use Fission!
|
||||
|
||||
```
|
||||
$ fission env create --name nodejs --image fission/node-env
|
||||
|
||||
$ curl https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js > hello.js
|
||||
|
||||
$ fission function create --name hello --env nodejs --code hello.js
|
||||
|
||||
$ fission route create --method GET --url /hello --function hello
|
||||
|
||||
$ curl http://$FISSION_ROUTER/hello
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
You can also set up persistence for logs: [instructions here](INSTALL.md).
|
||||
|
||||
See the [installation guide](INSTALL.md).
|
||||
|
||||
Compiling Fission
|
||||
=================
|
||||
|
||||
[You only need to do this if you're making Fission changes; if you're
|
||||
just deploying Fission, use fission.yaml which points to prebuilt
|
||||
images.]
|
||||
|
||||
You'll need go installed, along with the [glide dependency management
|
||||
tool](https://github.com/Masterminds/glide#install).
|
||||
You'll also need docker for building images.
|
||||
|
||||
The server side is compiled as one binary ("fission-bundle") which
|
||||
contains controller, poolmgr and router; it invokes the right one
|
||||
based on command-line arguments.
|
||||
|
||||
To build fission-bundle: clone this repo to
|
||||
`$GOPATH/src/github.com/fission/fission`, then from the top level
|
||||
directory (if you want to build the image with the docker inside
|
||||
minikube, you'll need to set the proper environment variables with
|
||||
`eval $(minikube docker-env)`):
|
||||
|
||||
```
|
||||
# Get dependencies
|
||||
$ glide install
|
||||
|
||||
# Build fission server and an image
|
||||
$ pushd fission-bundle
|
||||
$ ./build.sh
|
||||
|
||||
# Edit push.sh to point to your registry, or comment out the `docker push`
|
||||
# line if building into your local minikube for dev purposes
|
||||
$ $EDITOR push.sh
|
||||
$ ./push.sh
|
||||
$ popd
|
||||
|
||||
# To install, update fission.yaml to point to your compiled image
|
||||
$ $EDITOR fission.yaml
|
||||
$ kubectl create -f fission.yaml
|
||||
```
|
||||
|
||||
If you're changing the CLI:
|
||||
|
||||
```
|
||||
# Build Fission CLI
|
||||
$ cd fission && go install
|
||||
```
|
||||
See the [compilation guide](Compiling.md).
|
||||
|
||||
Status
|
||||
======
|
||||
@@ -269,9 +94,8 @@ Status
|
||||
Fission is in early alpha. It's not suitable for production use just
|
||||
yet.
|
||||
|
||||
We're looking for early developer feedback -- if you do use Fission,
|
||||
we'd love to hear how it's working for you, what parts in particular
|
||||
you'd like to see improved, and so on. Talk to us on
|
||||
[slack](http://slack.fission.io) or
|
||||
Reach us on [slack](http://slack.fission.io) or
|
||||
[twitter](https://twitter.com/fissionio).
|
||||
|
||||
Fission is a project by [Platform9 Systems](https://platform9.com/fission/)
|
||||
and [many contributors](https://github.com/fission/fission/graphs/contributors).
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
)
|
||||
|
||||
const (
|
||||
// supported environment variables
|
||||
envSrcPkg = "SRC_PKG"
|
||||
envDeployPkg = "DEPLOY_PKG"
|
||||
)
|
||||
|
||||
type (
|
||||
PackageBuildRequest struct {
|
||||
SrcPkgFilename string `json:"srcPkgFilename"`
|
||||
// Command for builder to run with.
|
||||
// A build command consists of commands, parameters and environment variables.
|
||||
// For now, two environment variables are supported:
|
||||
// 1. SRC_PKG: path to source package directory
|
||||
// 2. DEPLOY_PKG: path to deployment package directory
|
||||
BuildCommand string `json:"command"`
|
||||
}
|
||||
|
||||
PackageBuildResponse struct {
|
||||
ArtifactFilename string `json:"artifactFilename"`
|
||||
BuildLogs string `json:"buildLogs"`
|
||||
}
|
||||
|
||||
Builder struct {
|
||||
sharedVolumePath string
|
||||
}
|
||||
)
|
||||
|
||||
func MakeBuilder(sharedVolumePath string) *Builder {
|
||||
return &Builder{
|
||||
sharedVolumePath: sharedVolumePath,
|
||||
}
|
||||
}
|
||||
|
||||
func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "", 405)
|
||||
return
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
elapsed := time.Now().Sub(startTime)
|
||||
log.Printf("elapsed time in build request = %v", elapsed)
|
||||
}()
|
||||
|
||||
// parse request
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Printf("Error reading request body")
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
var req PackageBuildRequest
|
||||
err = json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing json body: %v", err)
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
log.Printf("Builder received request: %v", req)
|
||||
|
||||
log.Println("Starting build...")
|
||||
srcPkgPath := filepath.Join(builder.sharedVolumePath, req.SrcPkgFilename)
|
||||
deployPkgFilename := fmt.Sprintf("%v-%v", req.SrcPkgFilename, strings.ToLower(uniuri.NewLen(6)))
|
||||
deployPkgPath := filepath.Join(builder.sharedVolumePath, deployPkgFilename)
|
||||
buildLogs, err := builder.build(req.BuildCommand, srcPkgPath, deployPkgPath)
|
||||
if err != nil {
|
||||
e := errors.New(fmt.Sprintf("Error building source package: %v", err))
|
||||
http.Error(w, e.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
resp := PackageBuildResponse{
|
||||
ArtifactFilename: deployPkgFilename,
|
||||
BuildLogs: buildLogs,
|
||||
}
|
||||
|
||||
rBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
e := errors.New(fmt.Sprintf("Error encoding response body: %v", err))
|
||||
http.Error(w, e.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write(rBody)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (builder *Builder) build(command string, srcPkgPath string, deployPkgPath string) (string, error) {
|
||||
cmd := exec.Command(command)
|
||||
cmd.Dir = srcPkgPath
|
||||
// set env variables for build command
|
||||
cmd.Env = append(os.Environ(),
|
||||
fmt.Sprintf("%v=%v", envSrcPkg, srcPkgPath),
|
||||
fmt.Sprintf("%v=%v", envDeployPkg, deployPkgPath),
|
||||
)
|
||||
|
||||
cmdReader, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return "", errors.New(fmt.Sprintf("Error creating stdout pipe for cmd: %v", err.Error()))
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(cmdReader)
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return "", errors.New(fmt.Sprintf("Error starting cmd: %v", err.Error()))
|
||||
}
|
||||
|
||||
var buildLogs string
|
||||
|
||||
fmt.Println("\n=== Build Logs ===")
|
||||
for scanner.Scan() {
|
||||
output := scanner.Text()
|
||||
fmt.Println(output)
|
||||
buildLogs += fmt.Sprintf("%v\n", output)
|
||||
}
|
||||
fmt.Println("==================\n")
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", errors.New(fmt.Sprintf("Error reading cmd output: %v", err.Error()))
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return "", errors.New(fmt.Sprintf("Error waiting for cmd: %v", err.Error()))
|
||||
}
|
||||
|
||||
return buildLogs, nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/fission/fission"
|
||||
builder "github.com/fission/fission/builder"
|
||||
)
|
||||
|
||||
type (
|
||||
Client struct {
|
||||
url string
|
||||
}
|
||||
)
|
||||
|
||||
func MakeClient(serverUrl string) *Client {
|
||||
return &Client{
|
||||
url: strings.TrimSuffix(serverUrl, "/"),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Build(req *builder.PackageBuildRequest) error {
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := http.Post(c.url, "application/json", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fission.MakeErrorFromHTTP(resp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
FROM alpine:3.4
|
||||
|
||||
ADD builder /
|
||||
|
||||
EXPOSE 8000
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
GOOS=linux GOARCH=386 go build -o builder .
|
||||
@@ -14,24 +14,29 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fission
|
||||
package main
|
||||
|
||||
func (f Function) Key() string {
|
||||
return f.Metadata.Name
|
||||
}
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
func (e Environment) Key() string {
|
||||
return e.Metadata.Name
|
||||
}
|
||||
builder "github.com/fission/fission/builder"
|
||||
)
|
||||
|
||||
func (ht HTTPTrigger) Key() string {
|
||||
return ht.Metadata.Name
|
||||
}
|
||||
|
||||
func (tt TimeTrigger) Key() string {
|
||||
return tt.Metadata.Name
|
||||
}
|
||||
|
||||
func (w Watch) Key() string {
|
||||
return w.Metadata.Name
|
||||
// Usage: builder <shared volume path>
|
||||
func main() {
|
||||
dir := os.Args[1]
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, os.ModeDir|0700)
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating directory: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
builder := builder.MakeBuilder(dir)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", builder.Handler)
|
||||
http.ListenAndServe(":8000", mux)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
# Fission
|
||||
|
||||
[Fission](http://fission.io/) is a framework for serverless functions on Kubernetes.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.6+ with Beta APIs enabled
|
||||
|
||||
|
||||
## Helm charts
|
||||
|
||||
The following table lists two helm charts for Fission.
|
||||
|
||||
| Parameter | Description |
|
||||
| ---------------| ---------------------------------------------------------------------------------------|
|
||||
| `fission-core` | FaaS essentials, and triggers for HTTP, Timers and Kubernetes Watches |
|
||||
| `fission-all` | Log aggregation with fluentd and InfluxDB; NATS for message queue triggers; Fission-UI |
|
||||
|
||||
## Installing the chart
|
||||
|
||||
To install the chart with the release name `my-release`,
|
||||
|
||||
```bash
|
||||
$ helm install --name my-release fission-all
|
||||
```
|
||||
|
||||
## Uninstalling the chart
|
||||
|
||||
To uninstall/delete chart,
|
||||
|
||||
```bash
|
||||
$ helm delete my-release
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The following table lists the configurable parameters of the Fission chart and their default values.
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| ------------------- | ------------------------------------------ | ------------------------ |
|
||||
| `serviceType` | Type of service to use | `LoadBalancer`. |
|
||||
| `image` | Fission image | `fission/fission-bundle` |
|
||||
| `imageTag` | Fission image tag | `alpha20170124` |
|
||||
| `fetcherImage` | Fission fetcher image | `fission/fetcher` |
|
||||
| `fetcherImageTag` | Fission fetcher image tag | `latest` |
|
||||
| `controllerPort` | Fission Controller Service Port | `31313` |
|
||||
| `routerPort` | Fission Router Service Port | `31314` |
|
||||
| `functionNamespace` | Namespace for Fission functions | `fission-function` |
|
||||
| `openshift` | RBAC for openshift | `false` |
|
||||
|
||||
|
||||
* Extra configuration for `fission-all`
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| ---------------------- | --------------------------- | -------------------------- |
|
||||
| `logger.influxdbAdmin` | Log database admin username | `admin`. |
|
||||
| `logger.fluentdImage` | Logger fluentd image | `fission/fluentd` |
|
||||
| `fissionUiImage` | Fission ui image | `fission/fission-ui:0.1.0` |
|
||||
| `nats.authToken` | Nats streaming auth token | `defaultFissionAuthToken` |
|
||||
| `nats.clusterID` | Nats streaming clusterID | `fissionMQTrigger` |
|
||||
|
||||
|
||||
|
||||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||
|
||||
```bash
|
||||
$ helm install --name my-release --set image=custom/fission-bundle,imageTag=v1 fission-all
|
||||
```
|
||||
|
||||
If you're using minikube, set serviceType to NodePort:
|
||||
|
||||
```bash
|
||||
$ helm install --name my-release --set serviceType=NodePort fission-all
|
||||
```
|
||||
|
||||
You can also set parameters with a yaml file (see values.yaml for
|
||||
what it should look like):
|
||||
|
||||
```bash
|
||||
$ helm install --name my-release -f values.yaml fission-all
|
||||
```
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
name: fission-all
|
||||
version: v0.2.0-20170822
|
||||
description: Fission is a fast serverless framework for Kubernetes.
|
||||
keywords:
|
||||
- fission
|
||||
- serverless
|
||||
home: http://fission.io/
|
||||
maintainers:
|
||||
- name: Sanket Sudake
|
||||
email: sanketsudake@gmail.com
|
||||
- name: Soam Vasani
|
||||
email: soamvasani@platform9.com
|
||||
- name: Ta Ching Chen
|
||||
email: contact@tachingchen.com
|
||||
engine: gotpl
|
||||
@@ -1,10 +1,13 @@
|
||||
1. Install the client CLI.
|
||||
|
||||
Mac:
|
||||
$ curl http://fission.io/mac/fission > fission && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
|
||||
Linux:
|
||||
$ curl http://fission.io/linux/fission > fission && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
|
||||
Windows:
|
||||
For Windows, you can use the linux binary on WSL. Or you can download this windows executable: https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-windows.exe
|
||||
|
||||
2. Set the FISSION_URL and FISSION_ROUTER environment variables.
|
||||
FISSION_URL is used by the fission CLI to find the server.
|
||||
@@ -0,0 +1,486 @@
|
||||
{{ if .Values.openshift }}
|
||||
|
||||
# For openshift
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ProjectRequest
|
||||
metadata:
|
||||
name: fission
|
||||
labels:
|
||||
name: fission
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ProjectRequest
|
||||
metadata:
|
||||
name: {{ .Values.functionNamespace }}
|
||||
labels:
|
||||
name: fission-function
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: fission-admin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: fission:fission-admin
|
||||
rules:
|
||||
- apiGroups:
|
||||
- extensions
|
||||
attributeRestrictions: null
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
attributeRestrictions: null
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
groupNames: null
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: fission:fission-admin
|
||||
namespace: {{ .Values.functionNamespace }}
|
||||
roleRef:
|
||||
name: fission:fission-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-admin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
userNames:
|
||||
- system:serviceaccount:fission:fission-admin
|
||||
|
||||
{{ else }}
|
||||
|
||||
# For all environments except openshift
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: {{ .Values.functionNamespace }}
|
||||
labels:
|
||||
name: fission-function
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: fission-svc
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
---
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: fission-admin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-svc
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: fission-function-admin
|
||||
namespace: {{ .Values.functionNamespace }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-svc
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: fission-tpr
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-svc
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: fission-fetcher
|
||||
namespace: {{ .Values.functionNamespace }}
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: fission-fetcher-tpr
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-fetcher
|
||||
namespace: {{ .Values.functionNamespace }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
{{ end }}
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: controller
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: controller
|
||||
spec:
|
||||
containers:
|
||||
- name: controller
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--controllerPort", "8888"]
|
||||
serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: router
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: router
|
||||
spec:
|
||||
containers:
|
||||
- name: router
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--routerPort", "8888", "--poolmgrUrl", "http://poolmgr.{{ .Release.Namespace }}"]
|
||||
serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: poolmgr
|
||||
labels:
|
||||
svc: poolmgr
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8888
|
||||
selector:
|
||||
svc: poolmgr
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: poolmgr
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: poolmgr
|
||||
spec:
|
||||
containers:
|
||||
- name: poolmgr
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--poolmgrPort", "8888", "--namespace", "{{ .Values.functionNamespace }}", "--fission-namespace", "{{ .Release.Namespace }}"]
|
||||
env:
|
||||
- name: FETCHER_IMAGE
|
||||
value: "{{ .Values.fetcherImage }}:{{ .Values.fetcherImageTag }}"
|
||||
- name: FETCHER_IMAGE_PULL_POLICY
|
||||
value: "{{ .Values.pullPolicy }}"
|
||||
serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kubewatcher
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: kubewatcher
|
||||
spec:
|
||||
containers:
|
||||
- name: kubewatcher
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--kubewatcher"]
|
||||
serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: influxdb
|
||||
labels:
|
||||
svc: influxdb
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8086
|
||||
targetPort: 8086
|
||||
selector:
|
||||
svc: influxdb
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: influxdb
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: influxdb
|
||||
spec:
|
||||
containers:
|
||||
- name: influxdb
|
||||
image: tutum/influxdb
|
||||
env:
|
||||
- name: PRE_CREATE_DB
|
||||
value: fissionFunctionLog
|
||||
- name: ADMIN_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: influxdb
|
||||
key: username
|
||||
- name: INFLUXDB_INIT_PWD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: influxdb
|
||||
key: password
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: logger
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: logger
|
||||
spec:
|
||||
containers:
|
||||
- name: logger
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--logger"]
|
||||
volumeMounts:
|
||||
- name: container-log
|
||||
mountPath: /var/log/containers
|
||||
readOnly: true
|
||||
- name: docker-log
|
||||
mountPath: /var/lib/docker/containers
|
||||
readOnly: true
|
||||
- name: fission-log
|
||||
mountPath: /var/log/fission
|
||||
readOnly: false
|
||||
ports:
|
||||
- containerPort: 1234
|
||||
hostPort: 1234
|
||||
protocol: TCP
|
||||
- name: fluentd
|
||||
image: {{ .Values.logger.fluentdImage }}
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
env:
|
||||
- name: INFLUXDB_ADDRESS
|
||||
value: influxdb
|
||||
- name: INFLUXDB_PORT
|
||||
value: "8086"
|
||||
- name: INFLUXDB_DBNAME
|
||||
value: "fissionFunctionLog"
|
||||
- name: INFLUXDB_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: influxdb
|
||||
key: username
|
||||
- name: INFLUXDB_PASSWD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: influxdb
|
||||
key: password
|
||||
volumeMounts:
|
||||
- name: container-log
|
||||
mountPath: /var/log/containers
|
||||
readOnly: true
|
||||
- name: docker-log
|
||||
mountPath: /var/lib/docker/containers
|
||||
readOnly: true
|
||||
- name: fission-log
|
||||
mountPath: /var/log/fission
|
||||
readOnly: false
|
||||
serviceAccount: fission-svc
|
||||
volumes:
|
||||
- name: container-log
|
||||
hostPath:
|
||||
path: /var/log/containers
|
||||
- name: docker-log
|
||||
hostPath:
|
||||
path: /var/lib/docker/containers
|
||||
- name: fission-log
|
||||
hostPath:
|
||||
path: /var/log/fission
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: timer
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: timer
|
||||
spec:
|
||||
containers:
|
||||
- name: timer
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--timer"]
|
||||
serviceAccount: fission-svc
|
||||
|
||||
#
|
||||
# This is commented out until fission-ui allows configuring the
|
||||
# namespace. Right now it just crashes if Release.Namespace !=
|
||||
# "fission".
|
||||
#
|
||||
#---
|
||||
#apiVersion: extensions/v1beta1
|
||||
#kind: Deployment
|
||||
#metadata:
|
||||
# name: fission-ui
|
||||
# labels:
|
||||
# chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
#spec:
|
||||
# replicas: 1
|
||||
# template:
|
||||
# metadata:
|
||||
# labels:
|
||||
# svc: fission-ui
|
||||
# spec:
|
||||
# containers:
|
||||
# - name: nginx
|
||||
# image: {{ .Values.fissionUiImage }}
|
||||
# - name: kubectl-proxy
|
||||
# image: lachlanevenson/k8s-kubectl
|
||||
# args: ["proxy", "--port", "8001", "--address", "127.0.0.1"]
|
||||
# serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
svc: nats-streaming
|
||||
name: nats-streaming
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: nats-streaming
|
||||
spec:
|
||||
containers:
|
||||
- name: nats-streaming
|
||||
image: nats-streaming
|
||||
args: ["--cluster_id", "{{ .Values.nats.clusterID }}", "--auth", "{{ .Values.nats.authToken }}"]
|
||||
ports:
|
||||
- containerPort: 4222
|
||||
hostPort: 4222
|
||||
protocol: TCP
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mqtrigger
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: mqtrigger
|
||||
spec:
|
||||
containers:
|
||||
- name: mqtrigger
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--mqt"]
|
||||
env:
|
||||
- name: MESSAGE_QUEUE_TYPE
|
||||
value: nats-streaming
|
||||
- name: MESSAGE_QUEUE_URL
|
||||
value: nats://{{ .Values.nats.authToken }}@nats-streaming:4222
|
||||
serviceAccount: fission-svc
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: influxdb
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
type: Opaque
|
||||
data:
|
||||
username: {{ .Values.logger.influxdbAdmin | b64enc | quote }}
|
||||
password: {{ randAlphaNum 20 | b64enc | quote }}
|
||||
@@ -0,0 +1,67 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: router
|
||||
labels:
|
||||
svc: router
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
type: {{ .Values.serviceType }}
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8888
|
||||
nodePort: {{ .Values.routerPort }}
|
||||
selector:
|
||||
svc: router
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: controller
|
||||
labels:
|
||||
svc: controller
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
type: {{ .Values.serviceType }}
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8888
|
||||
nodePort: {{ .Values.controllerPort }}
|
||||
selector:
|
||||
svc: controller
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: fission-ui
|
||||
labels:
|
||||
svc: fission-ui
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
type: {{ .Values.serviceType }}
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
nodePort: 31319
|
||||
selector:
|
||||
svc: fission-ui
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nats-streaming
|
||||
labels:
|
||||
svc: nats-streaming
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
type: {{ .Values.serviceType }}
|
||||
ports:
|
||||
- port: 4222
|
||||
targetPort: 4222
|
||||
nodePort: 31316
|
||||
selector:
|
||||
svc: nats-streaming
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# Fission chart configuration
|
||||
#
|
||||
|
||||
## Kubernetes configuration
|
||||
## For minikube, set this to NodePort, elsewhere use LoadBalancer.
|
||||
serviceType: LoadBalancer
|
||||
|
||||
## Fission image repository
|
||||
image: fission/fission-bundle
|
||||
|
||||
## Image pull policy
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## Fission image version
|
||||
imageTag: v0.2.0-20170822
|
||||
|
||||
## Fission fetcher repository
|
||||
fetcherImage: fission/fetcher
|
||||
|
||||
## Fission fetcher image version
|
||||
fetcherImageTag: v0.2.0-20170822
|
||||
|
||||
## Port at which Fission controller service should be exposed
|
||||
controllerPort: 31313
|
||||
|
||||
## Port at which Fission router service should be exposed
|
||||
routerPort: 31314
|
||||
|
||||
## Namespace in which to run fission functions (this is different from
|
||||
## the release namespace)
|
||||
functionNamespace: fission-function
|
||||
|
||||
## Set up openshift RBAC rule
|
||||
openshift: false
|
||||
|
||||
## Logger config
|
||||
logger:
|
||||
influxdbAdmin: "admin"
|
||||
fluentdImage: fission/fluentd
|
||||
|
||||
## Fission ui config
|
||||
fissionUiImage: fission/fission-ui:0.1.0
|
||||
|
||||
## Message queue trigger config
|
||||
### NATS Streaming
|
||||
nats:
|
||||
authToken: "defaultFissionAuthToken"
|
||||
clusterID: "fissionMQTrigger"
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
name: fission-core
|
||||
version: v0.2.0-20170822
|
||||
description: Fission is a fast serverless framework for Kubernetes.
|
||||
keywords:
|
||||
- fission
|
||||
- serverless
|
||||
home: http://fission.io/
|
||||
maintainers:
|
||||
- name: Sanket Sudake
|
||||
email: sanketsudake@gmail.com
|
||||
- name: Soam Vasani
|
||||
email: soamvasani@platform9.com
|
||||
- name: Ta Ching Chen
|
||||
email: contact@tachingchen.com
|
||||
engine: gotpl
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,40 @@
|
||||
1. Install the client CLI.
|
||||
|
||||
Mac:
|
||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
|
||||
Linux:
|
||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||
|
||||
Windows:
|
||||
For Windows, you can use the linux binary on WSL. Or you can download this windows executable: https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-windows.exe
|
||||
|
||||
2. Set the FISSION_URL and FISSION_ROUTER environment variables.
|
||||
FISSION_URL is used by the fission CLI to find the server.
|
||||
FISSION_URL should be prefixed with a http://. (FISSION_ROUTER is only needed for the examples below to work.)
|
||||
|
||||
{{- if contains "NodePort" .Values.serviceType }}
|
||||
$ export FISSION_URL=http://$(minikube ip):{{ .Values.controllerPort }}
|
||||
$ export FISSION_ROUTER=$(minikube ip):{{ .Values.routerPort }}
|
||||
|
||||
{{- else if contains "LoadBalancer" .Values.serviceType }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl --namespace fission get -w svc' and 'kubectl --namespace fission get svc -w router'
|
||||
|
||||
$ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..ip}')
|
||||
$ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}')
|
||||
|
||||
{{- end }}
|
||||
|
||||
3. Finally, you're ready to use Fission!
|
||||
|
||||
$ fission env create --name nodejs --image fission/node-env
|
||||
|
||||
$ curl https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js > hello.js
|
||||
|
||||
$ fission function create --name hello --env nodejs --code hello.js
|
||||
|
||||
$ fission route create --method GET --url /hello --function hello
|
||||
|
||||
$ curl http://$FISSION_ROUTER/hello
|
||||
Hello, world!
|
||||
@@ -0,0 +1,16 @@
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 24 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
*/}}
|
||||
{{- define "fullname" -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 24 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,287 @@
|
||||
{{ if .Values.openshift }}
|
||||
|
||||
# For openshift
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ProjectRequest
|
||||
metadata:
|
||||
name: fission
|
||||
labels:
|
||||
name: fission
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ProjectRequest
|
||||
metadata:
|
||||
name: {{ .Values.functionNamespace }}
|
||||
labels:
|
||||
name: fission-function
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: fission-admin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: fission:fission-admin
|
||||
rules:
|
||||
- apiGroups:
|
||||
- extensions
|
||||
attributeRestrictions: null
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
attributeRestrictions: null
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
groupNames: null
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: fission:fission-admin
|
||||
namespace: {{ .Values.functionNamespace }}
|
||||
roleRef:
|
||||
name: fission:fission-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-admin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
userNames:
|
||||
- system:serviceaccount:fission:fission-admin
|
||||
|
||||
{{ else }}
|
||||
|
||||
# For all environments except openshift
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: {{ .Values.functionNamespace }}
|
||||
labels:
|
||||
name: fission-function
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: fission-svc
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
---
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: fission-admin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-svc
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: fission-function-admin
|
||||
namespace: {{ .Values.functionNamespace }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-svc
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: fission-tpr
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-svc
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: fission-fetcher
|
||||
namespace: {{ .Values.functionNamespace }}
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: fission-fetcher-tpr
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-fetcher
|
||||
namespace: {{ .Values.functionNamespace }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
{{ end }}
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: controller
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: controller
|
||||
spec:
|
||||
containers:
|
||||
- name: controller
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--controllerPort", "8888"]
|
||||
serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: router
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: router
|
||||
spec:
|
||||
containers:
|
||||
- name: router
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--routerPort", "8888", "--poolmgrUrl", "http://poolmgr.{{ .Release.Namespace }}"]
|
||||
serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: poolmgr
|
||||
labels:
|
||||
svc: poolmgr
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8888
|
||||
selector:
|
||||
svc: poolmgr
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: poolmgr
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: poolmgr
|
||||
spec:
|
||||
containers:
|
||||
- name: poolmgr
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--poolmgrPort", "8888", "--namespace", "{{ .Values.functionNamespace }}", "--fission-namespace", "{{ .Release.Namespace }}"]
|
||||
env:
|
||||
- name: FETCHER_IMAGE
|
||||
value: "{{ .Values.fetcherImage }}:{{ .Values.fetcherImageTag }}"
|
||||
- name: FETCHER_IMAGE_PULL_POLICY
|
||||
value: "{{ .Values.pullPolicy }}"
|
||||
serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kubewatcher
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: kubewatcher
|
||||
spec:
|
||||
containers:
|
||||
- name: kubewatcher
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--kubewatcher"]
|
||||
serviceAccount: fission-svc
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: timer
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: timer
|
||||
spec:
|
||||
containers:
|
||||
- name: timer
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--timer"]
|
||||
serviceAccount: fission-svc
|
||||
@@ -29,4 +29,4 @@ spec:
|
||||
targetPort: 8888
|
||||
nodePort: {{ .Values.controllerPort }}
|
||||
selector:
|
||||
svc: controller
|
||||
svc: controller
|
||||
@@ -6,11 +6,20 @@
|
||||
## For minikube, set this to NodePort, elsewhere use LoadBalancer.
|
||||
serviceType: LoadBalancer
|
||||
|
||||
## Fission image repostiroy
|
||||
## Fission image repository
|
||||
image: fission/fission-bundle
|
||||
|
||||
## Fission image version
|
||||
imageTag: alpha20170124
|
||||
imageTag: v0.2.0-20170822
|
||||
|
||||
## Image pull policy
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## Fission fetcher repository
|
||||
fetcherImage: fission/fetcher
|
||||
|
||||
## Fission fetcher image version
|
||||
fetcherImageTag: v0.2.0-20170822
|
||||
|
||||
## Port at which Fission controller service should be exposed
|
||||
controllerPort: 31313
|
||||
@@ -21,3 +30,6 @@ routerPort: 31314
|
||||
## Namespace in which to run fission functions (this is different from
|
||||
## the release namespace)
|
||||
functionNamespace: fission-function
|
||||
|
||||
## Set up openshift RBAC rule
|
||||
openshift: false
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
name: fission
|
||||
version: 0.1.0
|
||||
description: A Helm chart for Fission. Fission is a fast serverless framework for Kubernetes.
|
||||
keywords:
|
||||
- fission
|
||||
- serverless
|
||||
home: http://fission.io/
|
||||
maintainers:
|
||||
- name: Sanket Sudake
|
||||
email: sanketsudake@gmail.com
|
||||
engine: gotpl
|
||||
@@ -1,58 +0,0 @@
|
||||
# Fission
|
||||
|
||||
[Fission](http://fission.io/) is a framework for serverless functions on Kubernetes.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.4+ with Beta APIs enabled
|
||||
|
||||
|
||||
## Installing the chart
|
||||
|
||||
To install the chart with the release name `my-release`,
|
||||
|
||||
```bash
|
||||
$ helm install --name my-release ./fission
|
||||
```
|
||||
|
||||
## Uninstalling the chart
|
||||
|
||||
To uninstall/delete chart,
|
||||
|
||||
```bash
|
||||
$ helm delete my-release
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The following table lists the configurable parameters of the Fission chart and their default values.
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| ------------------- | ------------------------------------------ | ------------------------ |
|
||||
| `serviceType` | Type of service to use | `LoadBalancer`. |
|
||||
| `image` | Fission image | `fission/fission-bundle` |
|
||||
| `imageTag` | Fission image tag | `alpha20170124` |
|
||||
| `controllerPort` | Fission Controller Service Port | `31313` |
|
||||
| `routerPort` | Fission Router Service Port | `31314` |
|
||||
| `functionNamespace` | Namespace for Fission functions | `fission-function` |
|
||||
|
||||
|
||||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||
|
||||
```bash
|
||||
$ helm install --name my-release --set image=custom/fission-bundle,imageTag=v1 fission
|
||||
```
|
||||
|
||||
If you're using minikube, set serviceType to NodePort:
|
||||
|
||||
```bash
|
||||
$ helm install --name my-release --set serviceType=NodePort fission
|
||||
```
|
||||
|
||||
You can also set parameters with a yaml file (see values.yaml for
|
||||
what it should look like):
|
||||
|
||||
```bash
|
||||
$ helm install --name my-release -f values.yaml fission
|
||||
```
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: {{ .Values.functionNamespace }}
|
||||
labels:
|
||||
name: fission-function
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: controller
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: controller
|
||||
spec:
|
||||
containers:
|
||||
- name: controller
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--controllerPort", "8888", "--filepath", "/filestore"]
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: router
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: router
|
||||
spec:
|
||||
containers:
|
||||
- name: router
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--routerPort", "8888"]
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: poolmgr
|
||||
labels:
|
||||
svc: poolmgr
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8888
|
||||
selector:
|
||||
svc: poolmgr
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: poolmgr
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: poolmgr
|
||||
spec:
|
||||
containers:
|
||||
- name: poolmgr
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--poolmgrPort", "8888", "--namespace", "{{ .Values.functionNamespace }}"]
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kubewatcher
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: kubewatcher
|
||||
spec:
|
||||
containers:
|
||||
- name: kubewatcher
|
||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||
command: ["/fission-bundle"]
|
||||
args: ["--kubewatcher"]
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: etcd
|
||||
labels:
|
||||
svc: etcd
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
ports:
|
||||
- port: 2379
|
||||
targetPort: 2379
|
||||
selector:
|
||||
svc: etcd
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: etcd
|
||||
labels:
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
svc: etcd
|
||||
spec:
|
||||
containers:
|
||||
- name: etcd
|
||||
image: quay.io/coreos/etcd
|
||||
env:
|
||||
- name: ETCD_LISTEN_CLIENT_URLS
|
||||
value: http://0.0.0.0:2379
|
||||
- name: ETCD_ADVERTISE_CLIENT_URLS
|
||||
value: http://etcd:2379
|
||||
@@ -20,11 +20,7 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func UrlForFunction(m *Metadata) string {
|
||||
func UrlForFunction(name string) string {
|
||||
prefix := "/fission-function"
|
||||
if len(m.Uid) > 0 {
|
||||
return fmt.Sprintf("%v/%v/%v", prefix, m.Name, m.Uid)
|
||||
} else {
|
||||
return fmt.Sprintf("%v/%v", prefix, m.Name)
|
||||
}
|
||||
return fmt.Sprintf("%v/%v", prefix, name)
|
||||
}
|
||||
|
||||
+67
-38
@@ -26,18 +26,17 @@ import (
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
kerrors "k8s.io/client-go/1.5/pkg/api/errors"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/fission/logdb"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
type (
|
||||
API struct {
|
||||
FunctionStore
|
||||
HTTPTriggerStore
|
||||
TimeTriggerStore
|
||||
EnvironmentStore
|
||||
WatchStore
|
||||
fissionClient *tpr.FissionClient
|
||||
storageServiceUrl string
|
||||
}
|
||||
|
||||
logDBConfig struct {
|
||||
@@ -47,15 +46,17 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func MakeAPI(rs *ResourceStore) *API {
|
||||
api := &API{
|
||||
FunctionStore: FunctionStore{ResourceStore: *rs},
|
||||
HTTPTriggerStore: HTTPTriggerStore{ResourceStore: *rs},
|
||||
TimeTriggerStore: TimeTriggerStore{ResourceStore: *rs},
|
||||
EnvironmentStore: EnvironmentStore{ResourceStore: *rs},
|
||||
WatchStore: WatchStore{ResourceStore: *rs},
|
||||
func MakeAPI() (*API, error) {
|
||||
api, err := makeTPRBackedAPI()
|
||||
|
||||
u := os.Getenv("STORAGE_SERVICE_URL")
|
||||
if len(u) > 0 {
|
||||
api.storageServiceUrl = strings.TrimSuffix(u, "/")
|
||||
} else {
|
||||
api.storageServiceUrl = "http://storagesvc"
|
||||
}
|
||||
return api
|
||||
|
||||
return api, err
|
||||
}
|
||||
|
||||
func (api *API) respondWithSuccess(w http.ResponseWriter, resp []byte) {
|
||||
@@ -69,6 +70,14 @@ func (api *API) respondWithSuccess(w http.ResponseWriter, resp []byte) {
|
||||
|
||||
func (api *API) respondWithError(w http.ResponseWriter, err error) {
|
||||
debug.PrintStack()
|
||||
|
||||
// this error type comes with an HTTP code, so just use that
|
||||
se, ok := err.(*kerrors.StatusError)
|
||||
if ok {
|
||||
http.Error(w, string(se.ErrStatus.Reason), int(se.ErrStatus.Code))
|
||||
return
|
||||
}
|
||||
|
||||
code, msg := fission.GetHTTPError(err)
|
||||
log.Errorf("Error: %v: %v", code, msg)
|
||||
http.Error(w, msg, code)
|
||||
@@ -96,41 +105,61 @@ func (api *API) HomeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "{\"message\": \"Fission API\", \"version\": \"0.1.0\"}\n")
|
||||
}
|
||||
|
||||
func (api *API) ApiVersionMismatchHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := fission.MakeError(fission.ErrorNotFound, "Fission server supports API v2 only - v1 is not supported. Please upgrade your Fission client / CLI")
|
||||
api.respondWithError(w, err)
|
||||
}
|
||||
|
||||
func (api *API) Serve(port int) {
|
||||
r := mux.NewRouter()
|
||||
// Give a useful error message if an older CLI attempts to make a request
|
||||
r.HandleFunc(`/v1/{rest:[a-zA-Z0-9=\-\/]+}`, api.ApiVersionMismatchHandler)
|
||||
r.HandleFunc("/", api.HomeHandler)
|
||||
|
||||
r.HandleFunc("/v1/functions", api.FunctionApiList).Methods("GET")
|
||||
r.HandleFunc("/v1/functions", api.FunctionApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v1/functions/{function}", api.FunctionApiGet).Methods("GET")
|
||||
r.HandleFunc("/v1/functions/{function}", api.FunctionApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v1/functions/{function}", api.FunctionApiDelete).Methods("DELETE")
|
||||
r.HandleFunc("/v2/packages", api.PackageApiList).Methods("GET")
|
||||
r.HandleFunc("/v2/packages", api.PackageApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v2/packages/{package}", api.PackageApiGet).Methods("GET")
|
||||
r.HandleFunc("/v2/packages/{package}", api.PackageApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v2/packages/{package}", api.PackageApiDelete).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/v1/triggers/http", api.HTTPTriggerApiList).Methods("GET")
|
||||
r.HandleFunc("/v1/triggers/http", api.HTTPTriggerApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiGet).Methods("GET")
|
||||
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiDelete).Methods("DELETE")
|
||||
r.HandleFunc("/v2/functions", api.FunctionApiList).Methods("GET")
|
||||
r.HandleFunc("/v2/functions", api.FunctionApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v2/functions/{function}", api.FunctionApiGet).Methods("GET")
|
||||
r.HandleFunc("/v2/functions/{function}", api.FunctionApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v2/functions/{function}", api.FunctionApiDelete).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/v1/environments", api.EnvironmentApiList).Methods("GET")
|
||||
r.HandleFunc("/v1/environments", api.EnvironmentApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiGet).Methods("GET")
|
||||
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE")
|
||||
r.HandleFunc("/v2/triggers/http", api.HTTPTriggerApiList).Methods("GET")
|
||||
r.HandleFunc("/v2/triggers/http", api.HTTPTriggerApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiGet).Methods("GET")
|
||||
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiDelete).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/v1/watches", api.WatchApiList).Methods("GET")
|
||||
r.HandleFunc("/v1/watches", api.WatchApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v1/watches/{watch}", api.WatchApiGet).Methods("GET")
|
||||
r.HandleFunc("/v1/watches/{watch}", api.WatchApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v1/watches/{watch}", api.WatchApiDelete).Methods("DELETE")
|
||||
r.HandleFunc("/v2/environments", api.EnvironmentApiList).Methods("GET")
|
||||
r.HandleFunc("/v2/environments", api.EnvironmentApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiGet).Methods("GET")
|
||||
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/v1/triggers/time", api.TimeTriggerApiList).Methods("GET")
|
||||
r.HandleFunc("/v1/triggers/time", api.TimeTriggerApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiGet).Methods("GET")
|
||||
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiDelete).Methods("DELETE")
|
||||
r.HandleFunc("/v2/watches", api.WatchApiList).Methods("GET")
|
||||
r.HandleFunc("/v2/watches", api.WatchApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v2/watches/{watch}", api.WatchApiGet).Methods("GET")
|
||||
r.HandleFunc("/v2/watches/{watch}", api.WatchApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v2/watches/{watch}", api.WatchApiDelete).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/v2/triggers/time", api.TimeTriggerApiList).Methods("GET")
|
||||
r.HandleFunc("/v2/triggers/time", api.TimeTriggerApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiGet).Methods("GET")
|
||||
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiDelete).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/v2/triggers/messagequeue", api.MessageQueueTriggerApiList).Methods("GET")
|
||||
r.HandleFunc("/v2/triggers/messagequeue", api.MessageQueueTriggerApiCreate).Methods("POST")
|
||||
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiGet).Methods("GET")
|
||||
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiUpdate).Methods("PUT")
|
||||
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiDelete).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/proxy/{dbType}", api.FunctionLogsApiPost).Methods("POST")
|
||||
r.HandleFunc("/proxy/storage/v1/archive", api.StorageServiceProxy)
|
||||
|
||||
address := fmt.Sprintf(":%v", port)
|
||||
|
||||
|
||||
+137
-215
@@ -19,35 +19,49 @@ package controller
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
etcdClient "github.com/coreos/etcd/client"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/controller/client"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
var g struct {
|
||||
client *client.Client
|
||||
}
|
||||
|
||||
func assertNameReuseFails(err error, name string) {
|
||||
func panicIf(err error) {
|
||||
if err != nil {
|
||||
log.Panicf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func assert(c bool, msg string) {
|
||||
if !c {
|
||||
log.Fatalf("assert failed: %v", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func assertNameReuseFailure(err error, name string) {
|
||||
assert(err != nil, "recreating "+name+" with same name must fail")
|
||||
fe, ok := err.(fission.Error)
|
||||
assert(ok, "error must be a fission Error")
|
||||
assert(fe.Code == fission.ErrorNameExists, "error must be a name exists error")
|
||||
}
|
||||
|
||||
func assertNotFoundFails(err error, name string) {
|
||||
func assertNotFoundFailure(err error, name string) {
|
||||
assert(err != nil, "requesting a non-existent "+name+" must fail")
|
||||
fe, ok := err.(fission.Error)
|
||||
assert(ok, "error must be a fission Error")
|
||||
assert(fe.Code == fission.ErrorNotFound, "error must be a not found error")
|
||||
if fe.Code != fission.ErrorNotFound {
|
||||
log.Fatalf("error must be a not found error: %v", fe)
|
||||
}
|
||||
}
|
||||
|
||||
func assertCronSpecFails(err error) {
|
||||
@@ -58,68 +72,39 @@ func assertCronSpecFails(err error) {
|
||||
}
|
||||
|
||||
func TestFunctionApi(t *testing.T) {
|
||||
log.SetFormatter(&log.TextFormatter{DisableColors: true})
|
||||
name1 := "foo"
|
||||
name2 := "bar"
|
||||
|
||||
testFunc := &fission.Function{
|
||||
Metadata: fission.Metadata{
|
||||
Name: "foo",
|
||||
Uid: "",
|
||||
testFunc := &tpr.Function{
|
||||
Metadata: api.ObjectMeta{
|
||||
Name: name1,
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Environment: fission.Metadata{
|
||||
Name: "nodejs",
|
||||
Uid: "xxx",
|
||||
Spec: fission.FunctionSpec{
|
||||
EnvironmentName: "nodejs",
|
||||
Deployment: fission.FunctionPackageRef{
|
||||
FunctionName: "xxx",
|
||||
},
|
||||
},
|
||||
Code: "code1",
|
||||
}
|
||||
_, err := g.client.FunctionGet(&fission.Metadata{Name: "foo"})
|
||||
assertNotFoundFails(err, "function")
|
||||
|
||||
m, err := g.client.FunctionCreate(testFunc)
|
||||
panicIf(err)
|
||||
uid1 := m.Uid
|
||||
//log.Printf("Created function %v: %v", m.Name, m.Uid)
|
||||
_, err := g.client.FunctionGet(&api.ObjectMeta{
|
||||
Name: testFunc.Metadata.Name,
|
||||
Namespace: api.NamespaceDefault,
|
||||
})
|
||||
assertNotFoundFailure(err, "function")
|
||||
|
||||
_, err = g.client.FunctionCreate(testFunc)
|
||||
assertNameReuseFails(err, "function")
|
||||
|
||||
code, err := g.client.FunctionGetRaw(m)
|
||||
panicIf(err)
|
||||
assert(string(code) == testFunc.Code, "code from FunctionGetRaw must match created function")
|
||||
|
||||
testFunc.Code = "code2"
|
||||
m, err = g.client.FunctionUpdate(testFunc)
|
||||
panicIf(err)
|
||||
uid2 := m.Uid
|
||||
//log.Printf("Updated function %v: %v", m.Name, m.Uid)
|
||||
|
||||
m.Uid = uid1
|
||||
testFunc.Code = "code1"
|
||||
f, err := g.client.FunctionGet(m)
|
||||
panicIf(err)
|
||||
|
||||
testFunc.Metadata.Uid = m.Uid
|
||||
//log.Printf("f = %#v", f)
|
||||
//log.Printf("testFunc = %#v", testFunc)
|
||||
assert(*f == *testFunc, "first version should match when read by uid")
|
||||
_, err = g.client.FunctionCreate(testFunc)
|
||||
assertNameReuseFailure(err, "function")
|
||||
|
||||
m.Uid = uid2
|
||||
testFunc.Metadata.Uid = m.Uid
|
||||
testFunc.Code = "code2"
|
||||
f, err = g.client.FunctionGet(m)
|
||||
testFunc.Spec.Deployment.FunctionName = "yyy"
|
||||
_, err = g.client.FunctionUpdate(testFunc)
|
||||
panicIf(err)
|
||||
|
||||
assert(*f == *testFunc, "second version should match when read by uid")
|
||||
|
||||
m.Uid = ""
|
||||
testFunc.Metadata.Uid = uid2
|
||||
testFunc.Code = "code2"
|
||||
f, err = g.client.FunctionGet(m)
|
||||
panicIf(err)
|
||||
|
||||
assert(*f == *testFunc, "second version should match when read as latest")
|
||||
|
||||
testFunc.Metadata.Name = "bar"
|
||||
m, err = g.client.FunctionCreate(testFunc)
|
||||
testFunc.Metadata.Name = name2
|
||||
_, err = g.client.FunctionCreate(testFunc)
|
||||
panicIf(err)
|
||||
|
||||
funcs, err := g.client.FunctionList()
|
||||
@@ -127,7 +112,7 @@ func TestFunctionApi(t *testing.T) {
|
||||
assert(len(funcs) == 2,
|
||||
"created two functions, but didn't find them")
|
||||
|
||||
funcs_url := g.client.Url + "/v1/functions"
|
||||
funcs_url := g.client.Url + "/v2/functions"
|
||||
resp, err := http.Get(funcs_url)
|
||||
panicIf(err)
|
||||
defer resp.Body.Close()
|
||||
@@ -141,112 +126,55 @@ func TestFunctionApi(t *testing.T) {
|
||||
}
|
||||
assert(found, "incorrect response content type")
|
||||
|
||||
err = g.client.FunctionDelete(&fission.Metadata{Name: "foo"})
|
||||
err = g.client.FunctionDelete(&api.ObjectMeta{Name: name1, Namespace: api.NamespaceDefault})
|
||||
panicIf(err)
|
||||
err = g.client.FunctionDelete(&fission.Metadata{Name: "bar"})
|
||||
err = g.client.FunctionDelete(&api.ObjectMeta{Name: name2, Namespace: api.NamespaceDefault})
|
||||
panicIf(err)
|
||||
}
|
||||
|
||||
func TestFunctionVersionApi(t *testing.T) {
|
||||
testFunc := &fission.Function{
|
||||
Metadata: fission.Metadata{
|
||||
Name: "foo",
|
||||
Uid: "",
|
||||
},
|
||||
Environment: fission.Metadata{
|
||||
Name: "nodejs",
|
||||
Uid: "xxx",
|
||||
},
|
||||
Code: "code1",
|
||||
}
|
||||
|
||||
testFunc.Code = "code1"
|
||||
m, err := g.client.FunctionCreate(testFunc)
|
||||
panicIf(err)
|
||||
uid1 := m.Uid
|
||||
|
||||
testFunc.Code = "code2"
|
||||
m, err = g.client.FunctionUpdate(testFunc)
|
||||
panicIf(err)
|
||||
uid2 := m.Uid
|
||||
|
||||
err = g.client.FunctionDelete(&fission.Metadata{Name: "foo", Uid: uid1})
|
||||
panicIf(err)
|
||||
|
||||
f, err := g.client.FunctionGet(&fission.Metadata{Name: "foo"})
|
||||
panicIf(err)
|
||||
assert(f.Metadata.Uid == uid2, "deleted version1, but version2 does not exist")
|
||||
|
||||
testFunc.Code = "code3"
|
||||
m, err = g.client.FunctionUpdate(testFunc)
|
||||
panicIf(err)
|
||||
uid3 := m.Uid
|
||||
|
||||
err = g.client.FunctionDelete(&fission.Metadata{Name: "foo", Uid: uid3})
|
||||
panicIf(err)
|
||||
|
||||
f, err = g.client.FunctionGet(&fission.Metadata{Name: "foo"})
|
||||
panicIf(err)
|
||||
assert(f.Metadata.Uid == uid2, "deleted version3, but version2 does not exist")
|
||||
|
||||
testFunc.Code = "code4"
|
||||
m, err = g.client.FunctionUpdate(testFunc)
|
||||
panicIf(err)
|
||||
|
||||
err = g.client.FunctionDelete(&fission.Metadata{Name: "foo"})
|
||||
panicIf(err)
|
||||
|
||||
funcs, err := g.client.FunctionList()
|
||||
panicIf(err)
|
||||
assert(len(funcs) == 0,
|
||||
"created one function with two versions(2 and 4), delete without uid but cannot delete them all")
|
||||
}
|
||||
|
||||
func TestHTTPTriggerApi(t *testing.T) {
|
||||
testTrigger := &fission.HTTPTrigger{
|
||||
Metadata: fission.Metadata{
|
||||
Name: "xxx",
|
||||
Uid: "yyy",
|
||||
testTrigger := &tpr.Httptrigger{
|
||||
Metadata: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
UrlPattern: "/hello",
|
||||
Function: fission.Metadata{
|
||||
Name: "foo",
|
||||
Uid: "",
|
||||
Spec: fission.HTTPTriggerSpec{
|
||||
RelativeURL: "/hello",
|
||||
FunctionReference: fission.FunctionReference{
|
||||
Type: fission.FunctionReferenceTypeFunctionName,
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := g.client.HTTPTriggerGet(&fission.Metadata{Name: "foo"})
|
||||
assertNotFoundFails(err, "trigger")
|
||||
_, err := g.client.HTTPTriggerGet(&api.ObjectMeta{
|
||||
Name: testTrigger.Metadata.Name,
|
||||
Namespace: api.NamespaceDefault,
|
||||
})
|
||||
assertNotFoundFailure(err, "httptrigger")
|
||||
|
||||
m, err := g.client.HTTPTriggerCreate(testTrigger)
|
||||
panicIf(err)
|
||||
defer g.client.HTTPTriggerDelete(m)
|
||||
|
||||
_, err = g.client.HTTPTriggerCreate(testTrigger)
|
||||
assertNameReuseFails(err, "trigger")
|
||||
assertNameReuseFailure(err, "httptrigger")
|
||||
|
||||
tr, err := g.client.HTTPTriggerGet(m)
|
||||
panicIf(err)
|
||||
testTrigger.Metadata.Uid = m.Uid
|
||||
assert(*testTrigger == *tr, "trigger should match after reading")
|
||||
assert(testTrigger.Spec == tr.Spec, "trigger should match after reading")
|
||||
|
||||
testTrigger.UrlPattern = "/hi"
|
||||
m2, err := g.client.HTTPTriggerUpdate(testTrigger)
|
||||
testTrigger.Spec.RelativeURL = "/hi"
|
||||
_, err = g.client.HTTPTriggerUpdate(testTrigger)
|
||||
panicIf(err)
|
||||
|
||||
m.Uid = m2.Uid
|
||||
tr, err = g.client.HTTPTriggerGet(m)
|
||||
panicIf(err)
|
||||
testTrigger.Metadata.Uid = m.Uid
|
||||
assert(*testTrigger == *tr, "trigger should match after reading")
|
||||
|
||||
testTrigger.Metadata.Name = "yyy"
|
||||
m, err = g.client.HTTPTriggerCreate(testTrigger)
|
||||
_, err = g.client.HTTPTriggerCreate(testTrigger)
|
||||
assert(err != nil, "duplicate trigger should not be allowed")
|
||||
|
||||
testTrigger.UrlPattern = "/hi2"
|
||||
m, err = g.client.HTTPTriggerCreate(testTrigger)
|
||||
testTrigger.Spec.RelativeURL = "/hi2"
|
||||
m2, err := g.client.HTTPTriggerCreate(testTrigger)
|
||||
panicIf(err)
|
||||
defer g.client.HTTPTriggerDelete(m)
|
||||
defer g.client.HTTPTriggerDelete(m2)
|
||||
|
||||
ts, err := g.client.HTTPTriggerList()
|
||||
panicIf(err)
|
||||
@@ -254,42 +182,42 @@ func TestHTTPTriggerApi(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnvironmentApi(t *testing.T) {
|
||||
testEnv := &fission.Environment{
|
||||
Metadata: fission.Metadata{
|
||||
Name: "xxx",
|
||||
Uid: "yyy",
|
||||
testEnv := &tpr.Environment{
|
||||
Metadata: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: fission.EnvironmentSpec{
|
||||
Runtime: fission.Runtime{
|
||||
Image: "gcr.io/xyz",
|
||||
},
|
||||
},
|
||||
RunContainerImageUrl: "gcr.io/xyz",
|
||||
}
|
||||
_, err := g.client.EnvironmentGet(&fission.Metadata{Name: "foo"})
|
||||
assertNotFoundFails(err, "environment")
|
||||
_, err := g.client.EnvironmentGet(&api.ObjectMeta{
|
||||
Name: testEnv.Metadata.Name,
|
||||
Namespace: api.NamespaceDefault,
|
||||
})
|
||||
assertNotFoundFailure(err, "environment")
|
||||
|
||||
m, err := g.client.EnvironmentCreate(testEnv)
|
||||
panicIf(err)
|
||||
defer g.client.EnvironmentDelete(m)
|
||||
|
||||
_, err = g.client.EnvironmentCreate(testEnv)
|
||||
assertNameReuseFails(err, "environment")
|
||||
assertNameReuseFailure(err, "environment")
|
||||
|
||||
tr, err := g.client.EnvironmentGet(m)
|
||||
e, err := g.client.EnvironmentGet(m)
|
||||
panicIf(err)
|
||||
testEnv.Metadata.Uid = m.Uid
|
||||
assert(*testEnv == *tr, "env should match after reading")
|
||||
assert(testEnv.Spec == e.Spec, "env should match after reading")
|
||||
|
||||
testEnv.RunContainerImageUrl = "/hi"
|
||||
m2, err := g.client.EnvironmentUpdate(testEnv)
|
||||
testEnv.Spec.Runtime.Image = "another-img"
|
||||
_, err = g.client.EnvironmentUpdate(testEnv)
|
||||
panicIf(err)
|
||||
|
||||
m.Uid = m2.Uid
|
||||
tr, err = g.client.EnvironmentGet(m)
|
||||
testEnv.Metadata.Name = "bar"
|
||||
m2, err := g.client.EnvironmentCreate(testEnv)
|
||||
panicIf(err)
|
||||
testEnv.Metadata.Uid = m.Uid
|
||||
assert(*testEnv == *tr, "env should match after reading")
|
||||
|
||||
testEnv.Metadata.Name = "yyy"
|
||||
m, err = g.client.EnvironmentCreate(testEnv)
|
||||
panicIf(err)
|
||||
defer g.client.EnvironmentDelete(m)
|
||||
defer g.client.EnvironmentDelete(m2)
|
||||
|
||||
ts, err := g.client.EnvironmentList()
|
||||
panicIf(err)
|
||||
@@ -297,36 +225,38 @@ func TestEnvironmentApi(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWatchApi(t *testing.T) {
|
||||
testWatch := &fission.Watch{
|
||||
Metadata: fission.Metadata{
|
||||
Name: "xxx",
|
||||
Uid: "yyy",
|
||||
testWatch := &tpr.Kuberneteswatchtrigger{
|
||||
Metadata: api.ObjectMeta{
|
||||
Name: "xxx",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Namespace: "default",
|
||||
ObjType: "pod",
|
||||
LabelSelector: "",
|
||||
FieldSelector: "",
|
||||
Function: fission.Metadata{
|
||||
Name: "foo",
|
||||
Uid: "",
|
||||
Spec: fission.KubernetesWatchTriggerSpec{
|
||||
Namespace: "default",
|
||||
Type: "pod",
|
||||
FunctionReference: fission.FunctionReference{
|
||||
Type: fission.FunctionReferenceTypeFunctionName,
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
Target: "",
|
||||
}
|
||||
_, err := g.client.WatchGet(&fission.Metadata{Name: "foo"})
|
||||
assertNotFoundFails(err, "watch")
|
||||
_, err := g.client.WatchGet(&api.ObjectMeta{
|
||||
Name: testWatch.Metadata.Name,
|
||||
Namespace: api.NamespaceDefault,
|
||||
})
|
||||
assertNotFoundFailure(err, "watch")
|
||||
|
||||
m, err := g.client.WatchCreate(testWatch)
|
||||
panicIf(err)
|
||||
defer g.client.WatchDelete(m)
|
||||
|
||||
_, err = g.client.WatchCreate(testWatch)
|
||||
assertNameReuseFails(err, "watch")
|
||||
assertNameReuseFailure(err, "watch")
|
||||
|
||||
w, err := g.client.WatchGet(m)
|
||||
panicIf(err)
|
||||
testWatch.Metadata.Uid = m.Uid
|
||||
w.Target = ""
|
||||
assert(*testWatch == *w, "watch should match after reading")
|
||||
assert((testWatch.Spec.Namespace == w.Spec.Namespace &&
|
||||
testWatch.Spec.Type == w.Spec.Type &&
|
||||
testWatch.Spec.FunctionReference == w.Spec.FunctionReference), "watch should match after reading")
|
||||
|
||||
testWatch.Metadata.Name = "yyy"
|
||||
m2, err := g.client.WatchCreate(testWatch)
|
||||
@@ -339,45 +269,40 @@ func TestWatchApi(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTimeTriggerApi(t *testing.T) {
|
||||
testTrigger := &fission.TimeTrigger{
|
||||
Metadata: fission.Metadata{
|
||||
Name: "xxx",
|
||||
Uid: "yyy",
|
||||
testTrigger := &tpr.Timetrigger{
|
||||
Metadata: api.ObjectMeta{
|
||||
Name: "xxx",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Cron: "0 30 * * * *",
|
||||
Function: fission.Metadata{
|
||||
Name: "foo",
|
||||
Uid: "",
|
||||
Spec: fission.TimeTriggerSpec{
|
||||
Cron: "0 30 * * * *",
|
||||
FunctionReference: fission.FunctionReference{
|
||||
Type: fission.FunctionReferenceTypeFunctionName,
|
||||
Name: "asdf",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := g.client.TimeTriggerGet(&fission.Metadata{Name: "foo"})
|
||||
assertNotFoundFails(err, "trigger")
|
||||
_, err := g.client.TimeTriggerGet(&api.ObjectMeta{Name: testTrigger.Metadata.Name})
|
||||
assertNotFoundFailure(err, "trigger")
|
||||
|
||||
m, err := g.client.TimeTriggerCreate(testTrigger)
|
||||
panicIf(err)
|
||||
defer g.client.TimeTriggerDelete(m)
|
||||
|
||||
_, err = g.client.TimeTriggerCreate(testTrigger)
|
||||
assertNameReuseFails(err, "trigger")
|
||||
assertNameReuseFailure(err, "trigger")
|
||||
|
||||
tr, err := g.client.TimeTriggerGet(m)
|
||||
panicIf(err)
|
||||
testTrigger.Metadata.Uid = m.Uid
|
||||
assert(*testTrigger == *tr, "trigger should match after reading")
|
||||
assert(testTrigger.Spec == tr.Spec, "trigger should match after reading")
|
||||
|
||||
testTrigger.Cron = "@hourly"
|
||||
m2, err := g.client.TimeTriggerUpdate(testTrigger)
|
||||
testTrigger.Spec.Cron = "@hourly"
|
||||
_, err = g.client.TimeTriggerUpdate(testTrigger)
|
||||
panicIf(err)
|
||||
|
||||
m.Uid = m2.Uid
|
||||
tr, err = g.client.TimeTriggerGet(m)
|
||||
panicIf(err)
|
||||
testTrigger.Metadata.Uid = m.Uid
|
||||
assert(*testTrigger == *tr, "trigger should match after reading")
|
||||
|
||||
testTrigger.Metadata.Name = "yyy"
|
||||
testTrigger.Cron = "Not valid cron spec"
|
||||
m, err = g.client.TimeTriggerCreate(testTrigger)
|
||||
testTrigger.Spec.Cron = "Not valid cron spec"
|
||||
_, err = g.client.TimeTriggerCreate(testTrigger)
|
||||
assertCronSpecFails(err)
|
||||
|
||||
ts, err := g.client.TimeTriggerList()
|
||||
@@ -388,21 +313,18 @@ func TestTimeTriggerApi(t *testing.T) {
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
|
||||
fileStore, ks, rs := getTestResourceStore()
|
||||
defer os.RemoveAll(fileStore.root)
|
||||
// skip test if no cluster available for testing
|
||||
kubeconfig := os.Getenv("KUBECONFIG")
|
||||
if len(kubeconfig) == 0 {
|
||||
log.Println("Skipping test, no kubernetes cluster")
|
||||
return
|
||||
}
|
||||
|
||||
api := MakeAPI(rs)
|
||||
go Start(8888)
|
||||
|
||||
time.Sleep(time.Second)
|
||||
g.client = client.MakeClient("http://localhost:8888")
|
||||
|
||||
ks.Delete(context.Background(), "Function", &etcdClient.DeleteOptions{Recursive: true})
|
||||
ks.Delete(context.Background(), "HTTPTrigger", &etcdClient.DeleteOptions{Recursive: true})
|
||||
ks.Delete(context.Background(), "TimeTrigger", &etcdClient.DeleteOptions{Recursive: true})
|
||||
ks.Delete(context.Background(), "Environment", &etcdClient.DeleteOptions{Recursive: true})
|
||||
ks.Delete(context.Background(), "Watch", &etcdClient.DeleteOptions{Recursive: true})
|
||||
|
||||
go api.Serve(8888)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
resp, err := http.Get("http://localhost:8888/")
|
||||
panicIf(err)
|
||||
assert(resp.StatusCode == 200, "http get status code on root")
|
||||
|
||||
+1
-553
@@ -18,10 +18,7 @@ package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -73,7 +70,7 @@ func (c *Client) put(relativeUrl string, contentType string, body []byte) (*http
|
||||
}
|
||||
|
||||
func (c *Client) url(relativeUrl string) string {
|
||||
return c.Url + "/v1/" + relativeUrl
|
||||
return c.Url + "/v2/" + relativeUrl
|
||||
}
|
||||
|
||||
func (c *Client) handleResponse(resp *http.Response) ([]byte, error) {
|
||||
@@ -91,552 +88,3 @@ func (c *Client) handleCreateResponse(resp *http.Response) ([]byte, error) {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
func (c *Client) FunctionCreate(f *fission.Function) (*fission.Metadata, error) {
|
||||
orig := f.Code
|
||||
f.Code = base64.StdEncoding.EncodeToString([]byte(f.Code))
|
||||
defer func() { f.Code = orig }()
|
||||
|
||||
reqbody, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("functions"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) FunctionGet(m *fission.Metadata) (*fission.Function, error) {
|
||||
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var f fission.Function
|
||||
err = json.Unmarshal(body, &f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec, err := base64.StdEncoding.DecodeString(f.Code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Code = string(dec)
|
||||
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
func (c *Client) FunctionGetRaw(m *fission.Metadata) ([]byte, error) {
|
||||
relativeUrl := fmt.Sprintf("functions/%v?raw=1", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("&uid=%v", m.Uid)
|
||||
}
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return c.handleResponse(resp)
|
||||
}
|
||||
|
||||
func (c *Client) FunctionUpdate(f *fission.Function) (*fission.Metadata, error) {
|
||||
orig := f.Code
|
||||
f.Code = base64.StdEncoding.EncodeToString([]byte(f.Code))
|
||||
defer func() { f.Code = orig }()
|
||||
|
||||
reqbody, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("functions/%v", f.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) FunctionDelete(m *fission.Metadata) error {
|
||||
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
err := c.delete(relativeUrl)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) FunctionList() ([]fission.Function, error) {
|
||||
resp, err := http.Get(c.url("functions"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funcs := make([]fission.Function, 0)
|
||||
err = json.Unmarshal(body, &funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerCreate(t *fission.HTTPTrigger) (*fission.Metadata, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("triggers/http"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerGet(m *fission.Metadata) (*fission.HTTPTrigger, error) {
|
||||
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var t fission.HTTPTrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerUpdate(t *fission.HTTPTrigger) (*fission.Metadata, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("triggers/http/%v", t.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerDelete(m *fission.Metadata) error {
|
||||
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
err := c.delete(relativeUrl)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerList() ([]fission.HTTPTrigger, error) {
|
||||
resp, err := http.Get(c.url("triggers/http"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggers := make([]fission.HTTPTrigger, 0)
|
||||
err = json.Unmarshal(body, &triggers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return triggers, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentCreate(env *fission.Environment) (*fission.Metadata, error) {
|
||||
reqbody, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("environments"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentGet(m *fission.Metadata) (*fission.Environment, error) {
|
||||
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var env fission.Environment
|
||||
err = json.Unmarshal(body, &env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &env, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentUpdate(env *fission.Environment) (*fission.Metadata, error) {
|
||||
reqbody, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("environments/%v", env.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentDelete(m *fission.Metadata) error {
|
||||
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
err := c.delete(relativeUrl)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentList() ([]fission.Environment, error) {
|
||||
resp, err := http.Get(c.url("environments"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs := make([]fission.Environment, 0)
|
||||
err = json.Unmarshal(body, &envs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func (c *Client) WatchCreate(w *fission.Watch) (*fission.Metadata, error) {
|
||||
reqbody, err := json.Marshal(w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("watches"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) WatchGet(m *fission.Metadata) (*fission.Watch, error) {
|
||||
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var w fission.Watch
|
||||
err = json.Unmarshal(body, &w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &w, nil
|
||||
}
|
||||
|
||||
func (c *Client) WatchUpdate(w *fission.Watch) (*fission.Metadata, error) {
|
||||
return nil, fission.MakeError(fission.ErrorNotImplmented,
|
||||
"watch update not implemented")
|
||||
}
|
||||
|
||||
func (c *Client) WatchDelete(m *fission.Metadata) error {
|
||||
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
err := c.delete(relativeUrl)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) WatchList() ([]fission.Watch, error) {
|
||||
resp, err := http.Get(c.url("watches"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
watches := make([]fission.Watch, 0)
|
||||
err = json.Unmarshal(body, &watches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return watches, err
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerCreate(t *fission.TimeTrigger) (*fission.Metadata, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("triggers/time"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerGet(m *fission.Metadata) (*fission.TimeTrigger, error) {
|
||||
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var t fission.TimeTrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerUpdate(t *fission.TimeTrigger) (*fission.Metadata, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("triggers/time/%v", t.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m fission.Metadata
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerDelete(m *fission.Metadata) error {
|
||||
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
|
||||
if len(m.Uid) > 0 {
|
||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
||||
}
|
||||
err := c.delete(relativeUrl)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerList() ([]fission.TimeTrigger, error) {
|
||||
resp, err := http.Get(c.url("triggers/time"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggers := make([]fission.TimeTrigger, 0)
|
||||
err = json.Unmarshal(body, &triggers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return triggers, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (c *Client) EnvironmentCreate(env *tpr.Environment) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("environments"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentGet(m *api.ObjectMeta) (*tpr.Environment, error) {
|
||||
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var env tpr.Environment
|
||||
err = json.Unmarshal(body, &env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &env, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentUpdate(env *tpr.Environment) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("environments/%v", env.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentDelete(m *api.ObjectMeta) error {
|
||||
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
return c.delete(relativeUrl)
|
||||
}
|
||||
|
||||
func (c *Client) EnvironmentList() ([]tpr.Environment, error) {
|
||||
resp, err := http.Get(c.url("environments"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs := make([]tpr.Environment, 0)
|
||||
err = json.Unmarshal(body, &envs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return envs, nil
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (c *Client) FunctionCreate(f *tpr.Function) (*api.ObjectMeta, error) {
|
||||
|
||||
reqbody, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("functions"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) FunctionGet(m *api.ObjectMeta) (*tpr.Function, error) {
|
||||
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var f tpr.Function
|
||||
err = json.Unmarshal(body, &f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
func (c *Client) FunctionGetRawDeployment(m *api.ObjectMeta) ([]byte, error) {
|
||||
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
relativeUrl += fmt.Sprintf("&deploymentraw=1")
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return c.handleResponse(resp)
|
||||
}
|
||||
|
||||
func (c *Client) FunctionUpdate(f *tpr.Function) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("functions/%v", f.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) FunctionDelete(m *api.ObjectMeta) error {
|
||||
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
return c.delete(relativeUrl)
|
||||
}
|
||||
|
||||
func (c *Client) FunctionList() ([]tpr.Function, error) {
|
||||
resp, err := http.Get(c.url("functions"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funcs := make([]tpr.Function, 0)
|
||||
err = json.Unmarshal(body, &funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return funcs, nil
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (c *Client) HTTPTriggerCreate(t *tpr.Httptrigger) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("triggers/http"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerGet(m *api.ObjectMeta) (*tpr.Httptrigger, error) {
|
||||
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var t tpr.Httptrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerUpdate(t *tpr.Httptrigger) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("triggers/http/%v", t.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerDelete(m *api.ObjectMeta) error {
|
||||
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
return c.delete(relativeUrl)
|
||||
}
|
||||
|
||||
func (c *Client) HTTPTriggerList() ([]tpr.Httptrigger, error) {
|
||||
resp, err := http.Get(c.url("triggers/http"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggers := make([]tpr.Httptrigger, 0)
|
||||
err = json.Unmarshal(body, &triggers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return triggers, nil
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (c *Client) WatchCreate(w *tpr.Kuberneteswatchtrigger) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("watches"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) WatchGet(m *api.ObjectMeta) (*tpr.Kuberneteswatchtrigger, error) {
|
||||
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var w tpr.Kuberneteswatchtrigger
|
||||
err = json.Unmarshal(body, &w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &w, nil
|
||||
}
|
||||
|
||||
func (c *Client) WatchUpdate(w *tpr.Kuberneteswatchtrigger) (*api.ObjectMeta, error) {
|
||||
return nil, fission.MakeError(fission.ErrorNotImplmented,
|
||||
"watch update not implemented")
|
||||
}
|
||||
|
||||
func (c *Client) WatchDelete(m *api.ObjectMeta) error {
|
||||
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
return c.delete(relativeUrl)
|
||||
}
|
||||
|
||||
func (c *Client) WatchList() ([]tpr.Kuberneteswatchtrigger, error) {
|
||||
resp, err := http.Get(c.url("watches"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
watches := make([]tpr.Kuberneteswatchtrigger, 0)
|
||||
err = json.Unmarshal(body, &watches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return watches, err
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (c *Client) MessageQueueTriggerCreate(t *tpr.Messagequeuetrigger) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("triggers/messagequeue"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) MessageQueueTriggerGet(m *api.ObjectMeta) (*tpr.Messagequeuetrigger, error) {
|
||||
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var t tpr.Messagequeuetrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (c *Client) MessageQueueTriggerUpdate(mqTrigger *tpr.Messagequeuetrigger) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(mqTrigger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", mqTrigger.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) MessageQueueTriggerDelete(m *api.ObjectMeta) error {
|
||||
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
return c.delete(relativeUrl)
|
||||
}
|
||||
|
||||
func (c *Client) MessageQueueTriggerList(mqType string) ([]tpr.Messagequeuetrigger, error) {
|
||||
relativeUrl := "triggers/messagequeue"
|
||||
if len(mqType) > 0 {
|
||||
// TODO remove this, replace with field selector
|
||||
relativeUrl += fmt.Sprintf("?mqtype=%v", mqType)
|
||||
}
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggers := make([]tpr.Messagequeuetrigger, 0)
|
||||
err = json.Unmarshal(body, &triggers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return triggers, nil
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (c *Client) PackageCreate(f *tpr.Package) (*api.ObjectMeta, error) {
|
||||
|
||||
reqbody, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("packages"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) PackageGet(m *api.ObjectMeta) (*tpr.Package, error) {
|
||||
relativeUrl := fmt.Sprintf("packages/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var f tpr.Package
|
||||
err = json.Unmarshal(body, &f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
func (c *Client) PackageUpdate(f *tpr.Package) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("packages/%v", f.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) PackageDelete(m *api.ObjectMeta) error {
|
||||
relativeUrl := fmt.Sprintf("packages/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
return c.delete(relativeUrl)
|
||||
}
|
||||
|
||||
func (c *Client) PackageList() ([]tpr.Package, error) {
|
||||
resp, err := http.Get(c.url("packages"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funcs := make([]tpr.Package, 0)
|
||||
err = json.Unmarshal(body, &funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return funcs, nil
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (c *Client) TimeTriggerCreate(t *tpr.Timetrigger) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(c.url("triggers/time"), "application/json", bytes.NewReader(reqbody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleCreateResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerGet(m *api.ObjectMeta) (*tpr.Timetrigger, error) {
|
||||
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
resp, err := http.Get(c.url(relativeUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var t tpr.Timetrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerUpdate(t *tpr.Timetrigger) (*api.ObjectMeta, error) {
|
||||
reqbody, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relativeUrl := fmt.Sprintf("triggers/time/%v", t.Metadata.Name)
|
||||
|
||||
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m api.ObjectMeta
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerDelete(m *api.ObjectMeta) error {
|
||||
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
return c.delete(relativeUrl)
|
||||
}
|
||||
|
||||
func (c *Client) TimeTriggerList() ([]tpr.Timetrigger, error) {
|
||||
resp, err := http.Get(c.url("triggers/time"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := c.handleResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggers := make([]tpr.Timetrigger, 0)
|
||||
err = json.Unmarshal(body, &triggers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return triggers, nil
|
||||
}
|
||||
@@ -17,16 +17,27 @@ limitations under the License.
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
type JsonSerializer struct {
|
||||
}
|
||||
func Start(port int) {
|
||||
fc, kc, err := tpr.MakeFissionClient()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to K8s API: %v", err)
|
||||
}
|
||||
|
||||
func (j JsonSerializer) serialize(r resource) ([]byte, error) {
|
||||
return json.Marshal(r)
|
||||
}
|
||||
err = tpr.EnsureFissionTPRs(kc)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create fission TPRs: %v", err)
|
||||
}
|
||||
|
||||
func (j JsonSerializer) deserialize(buf []byte, r resource) error {
|
||||
return json.Unmarshal(buf, r)
|
||||
fc.WaitForTPRs()
|
||||
|
||||
api, err := MakeAPI()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start controller: %v", err)
|
||||
}
|
||||
api.Serve(port)
|
||||
}
|
||||
@@ -23,133 +23,138 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (api *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) {
|
||||
envs, err := api.EnvironmentStore.List()
|
||||
func (a *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) {
|
||||
envs, err := a.fissionClient.Environments(api.NamespaceAll).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(envs)
|
||||
resp, err := json.Marshal(envs.Items)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var env fission.Environment
|
||||
var env tpr.Environment
|
||||
err = json.Unmarshal(body, &env)
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshal request body: [%v]", body)
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
uid, err := api.EnvironmentStore.Create(&env)
|
||||
err = validateResourceName(env.Metadata.Name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: env.Metadata.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Create(&env)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(enew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
var m fission.Metadata
|
||||
|
||||
func (a *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
m.Name = vars["environment"]
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
name := vars["environment"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
env, err := api.EnvironmentStore.Get(&m)
|
||||
env, err := a.fissionClient.Environments(ns).Get(name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) EnvironmentApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) EnvironmentApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["environment"]
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var env fission.Environment
|
||||
var env tpr.Environment
|
||||
err = json.Unmarshal(body, &env)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if name != env.Metadata.Name {
|
||||
err = fission.MakeError(fission.ErrorInvalidArgument, "Environment name doesn't match URL")
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
uid, err := api.EnvironmentStore.Update(&env)
|
||||
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Update(&env)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: env.Metadata.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
resp, err := json.Marshal(enew.Metadata)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) EnvironmentApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) EnvironmentApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
var m fission.Metadata
|
||||
m.Name = vars["environment"]
|
||||
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
if len(m.Uid) == 0 {
|
||||
log.WithFields(log.Fields{"httpTrigger": m.Name}).Info("Deleting all versions")
|
||||
name := vars["environment"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
err := api.EnvironmentStore.Delete(m)
|
||||
err := a.fissionClient.Environments(ns).Delete(name, &api.DeleteOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, []byte(""))
|
||||
a.respondWithSuccess(w, []byte(""))
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/satori/go.uuid"
|
||||
|
||||
"github.com/fission/fission"
|
||||
)
|
||||
|
||||
type EnvironmentStore struct {
|
||||
ResourceStore
|
||||
}
|
||||
|
||||
func (es *EnvironmentStore) Create(e *fission.Environment) (string, error) {
|
||||
e.Metadata.Uid = uuid.NewV4().String()
|
||||
return e.Metadata.Uid, es.ResourceStore.create(e)
|
||||
}
|
||||
|
||||
func (es *EnvironmentStore) Get(m *fission.Metadata) (*fission.Environment, error) {
|
||||
var e fission.Environment
|
||||
err := es.ResourceStore.read(m.Name, &e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &e, nil
|
||||
}
|
||||
|
||||
func (es *EnvironmentStore) Update(e *fission.Environment) (string, error) {
|
||||
e.Metadata.Uid = uuid.NewV4().String()
|
||||
return e.Metadata.Uid, es.ResourceStore.update(e)
|
||||
}
|
||||
|
||||
func (es *EnvironmentStore) Delete(m fission.Metadata) error {
|
||||
typeName, err := getTypeName(fission.Environment{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return es.ResourceStore.delete(typeName, m.Name)
|
||||
}
|
||||
|
||||
func (es *EnvironmentStore) List() ([]fission.Environment, error) {
|
||||
typeName, err := getTypeName(fission.Environment{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bufs, err := es.ResourceStore.getAll(typeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs := make([]fission.Environment, 0, len(bufs))
|
||||
js := JsonSerializer{}
|
||||
for _, buf := range bufs {
|
||||
var e fission.Environment
|
||||
err = js.deserialize([]byte(buf), &e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envs = append(envs, e)
|
||||
}
|
||||
|
||||
return envs, nil
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type requestType int
|
||||
|
||||
const (
|
||||
READ requestType = iota
|
||||
WRITE
|
||||
DELETE
|
||||
)
|
||||
|
||||
type (
|
||||
FileStore struct {
|
||||
root string // abs path of root of filestore
|
||||
requestChannel chan fileStoreRequest
|
||||
}
|
||||
|
||||
fileStoreRequest struct {
|
||||
requestType
|
||||
fileName string // relative path
|
||||
fileContents []byte
|
||||
responseChannel chan fileStoreResponse
|
||||
}
|
||||
|
||||
fileStoreResponse struct {
|
||||
error
|
||||
fileContents []byte
|
||||
}
|
||||
)
|
||||
|
||||
func MakeFileStore(path string) *FileStore {
|
||||
|
||||
// create directory if necessary
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(path, os.ModeDir|0700)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create directory %v", path)
|
||||
return nil
|
||||
}
|
||||
log.Printf("Created directory %v", path)
|
||||
} else {
|
||||
log.Printf("Unknown error initializing filestore: %v", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
fileStore := &FileStore{
|
||||
root: path,
|
||||
requestChannel: make(chan fileStoreRequest),
|
||||
}
|
||||
go fileStore.fileStoreService()
|
||||
return fileStore
|
||||
}
|
||||
|
||||
func (fs *FileStore) fileStoreService() {
|
||||
for {
|
||||
req := <-fs.requestChannel
|
||||
response := &fileStoreResponse{}
|
||||
|
||||
log.WithFields(log.Fields{"file": req.fileName, "type": req.requestType}).Debug("fileStore request")
|
||||
switch req.requestType {
|
||||
case READ:
|
||||
response.fileContents, response.error = ioutil.ReadFile(path.Join(fs.root, req.fileName))
|
||||
case WRITE:
|
||||
response.error = ioutil.WriteFile(path.Join(fs.root, req.fileName), req.fileContents, 0600)
|
||||
case DELETE:
|
||||
response.error = os.Remove(path.Join(fs.root, req.fileName))
|
||||
if os.IsNotExist(response.error) {
|
||||
response.error = nil
|
||||
}
|
||||
default:
|
||||
log.Panic("bad request")
|
||||
}
|
||||
req.responseChannel <- *response
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FileStore) read(fileName string) ([]byte, error) {
|
||||
req := fileStoreRequest{
|
||||
requestType: READ,
|
||||
fileName: fileName,
|
||||
responseChannel: make(chan fileStoreResponse),
|
||||
}
|
||||
fs.requestChannel <- req
|
||||
response := <-req.responseChannel
|
||||
return response.fileContents, response.error
|
||||
}
|
||||
|
||||
func (fs *FileStore) write(fileName string, contents []byte) error {
|
||||
req := fileStoreRequest{
|
||||
requestType: WRITE,
|
||||
fileName: fileName,
|
||||
fileContents: contents,
|
||||
responseChannel: make(chan fileStoreResponse),
|
||||
}
|
||||
fs.requestChannel <- req
|
||||
response := <-req.responseChannel
|
||||
return response.error
|
||||
}
|
||||
|
||||
func (fs *FileStore) delete(fileName string) error {
|
||||
req := fileStoreRequest{
|
||||
requestType: DELETE,
|
||||
fileName: fileName,
|
||||
responseChannel: make(chan fileStoreResponse),
|
||||
}
|
||||
fs.requestChannel <- req
|
||||
response := <-req.responseChannel
|
||||
return response.error
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFileStore(t *testing.T) {
|
||||
// tmp dir
|
||||
dir, err := ioutil.TempDir("", "testFileStore")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
log.Printf("temp dir at %v", dir)
|
||||
|
||||
// file store
|
||||
fs := MakeFileStore(dir)
|
||||
|
||||
_, err = fs.read("nonexistent")
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
|
||||
path := "fileStoreTest"
|
||||
contents := []byte("bar")
|
||||
err = fs.write(path, contents)
|
||||
if err != nil {
|
||||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
|
||||
observedContents, err := fs.read(path)
|
||||
if err != nil {
|
||||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
|
||||
if string(observedContents) != string(contents) {
|
||||
t.Fatalf("contents don't match")
|
||||
}
|
||||
|
||||
err = fs.delete(path)
|
||||
if err != nil {
|
||||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
}
|
||||
+58
-75
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -26,165 +25,149 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (api *API) FunctionApiList(w http.ResponseWriter, r *http.Request) {
|
||||
funcs, err := api.FunctionStore.List()
|
||||
func (a *API) FunctionApiList(w http.ResponseWriter, r *http.Request) {
|
||||
funcs, err := a.fissionClient.Functions(api.NamespaceAll).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(funcs)
|
||||
resp, err := json.Marshal(funcs.Items)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var f fission.Function
|
||||
var f tpr.Function
|
||||
err = json.Unmarshal(body, &f)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
dec, err := base64.StdEncoding.DecodeString(f.Code)
|
||||
err = validateResourceName(f.Metadata.Name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
f.Code = string(dec)
|
||||
|
||||
uid, err := api.FunctionStore.Create(&f)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: f.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Create(&f)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(fnew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
var m fission.Metadata
|
||||
|
||||
func (a *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
m.Name = vars["function"]
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
raw := r.FormValue("raw") // just the code
|
||||
name := vars["function"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
f, err := api.FunctionStore.Get(&m)
|
||||
f, err := a.fissionClient.Functions(ns).Get(name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var resp []byte
|
||||
if raw != "" {
|
||||
resp = []byte(f.Code)
|
||||
} else {
|
||||
f.Code = base64.StdEncoding.EncodeToString([]byte(f.Code))
|
||||
resp, err = json.Marshal(f)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
resp, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) FunctionApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) FunctionApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
funcName := vars["function"]
|
||||
name := vars["function"]
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var f fission.Function
|
||||
var f tpr.Function
|
||||
err = json.Unmarshal(body, &f)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if funcName != f.Metadata.Name {
|
||||
if name != f.Metadata.Name {
|
||||
err = fission.MakeError(fission.ErrorInvalidArgument, "Function name doesn't match URL")
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
dec, err := base64.StdEncoding.DecodeString(f.Code)
|
||||
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Update(&f)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
f.Code = string(dec)
|
||||
|
||||
uid, err := api.FunctionStore.Update(&f)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: f.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
resp, err := json.Marshal(fnew.Metadata)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
var m fission.Metadata
|
||||
m.Name = vars["function"]
|
||||
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
if len(m.Uid) == 0 {
|
||||
log.WithFields(log.Fields{"function": m.Name}).Info("Deleting all versions")
|
||||
name := vars["function"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
err := api.FunctionStore.Delete(m)
|
||||
err := a.fissionClient.Functions(ns).Delete(name, &api.DeleteOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, []byte(""))
|
||||
a.respondWithSuccess(w, []byte(""))
|
||||
}
|
||||
|
||||
// FunctionLogsApiPost establishes a proxy server to log database, and redirect
|
||||
// query command send from client to database then proxy back the db response.
|
||||
func (api *API) FunctionLogsApiPost(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) FunctionLogsApiPost(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
// get dbType from url
|
||||
dbType := vars["dbType"]
|
||||
|
||||
// find correspond db http url
|
||||
dbCnf := api.getLogDBConfig(dbType)
|
||||
dbCnf := a.getLogDBConfig(dbType)
|
||||
|
||||
svcUrl, err := url.Parse(dbCnf.httpURL)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fission/fission"
|
||||
)
|
||||
|
||||
type FunctionStore struct {
|
||||
ResourceStore
|
||||
}
|
||||
|
||||
func (fs *FunctionStore) Create(f *fission.Function) (string, error) {
|
||||
code := []byte(f.Code)
|
||||
_, uid, err := fs.ResourceStore.writeFile(f.Key(), code)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
f.Metadata.Uid = uid
|
||||
f.Code = ""
|
||||
|
||||
err = fs.ResourceStore.create(f)
|
||||
if err != nil {
|
||||
fs.ResourceStore.deleteFile(f.Key(), uid) // ignore errors
|
||||
return "", err
|
||||
}
|
||||
return f.Metadata.Uid, nil
|
||||
}
|
||||
|
||||
func (fs *FunctionStore) Get(m *fission.Metadata) (*fission.Function, error) {
|
||||
var f fission.Function
|
||||
err := fs.ResourceStore.read(m.Name, &f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var code []byte
|
||||
if len(m.Uid) > 0 {
|
||||
log.WithFields(log.Fields{"Uid": m.Uid}).Info("fetching by uid")
|
||||
code, err = fs.ResourceStore.readFile(m.Name, &m.Uid)
|
||||
f.Metadata = *m
|
||||
} else {
|
||||
code, err = fs.ResourceStore.readFile(m.Name, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f.Code = string(code)
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
func (fs *FunctionStore) Update(f *fission.Function) (string, error) {
|
||||
code := []byte(f.Code)
|
||||
_, uid, err := fs.ResourceStore.writeFile(f.Key(), code)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var fnew fission.Function
|
||||
err = fs.ResourceStore.read(f.Metadata.Name, &fnew)
|
||||
if err != nil {
|
||||
fs.ResourceStore.deleteFile(f.Key(), uid) // ignore err
|
||||
return "", err
|
||||
}
|
||||
|
||||
fnew.Metadata.Uid = uid
|
||||
fnew.Environment = f.Environment
|
||||
|
||||
err = fs.ResourceStore.update(fnew)
|
||||
if err != nil {
|
||||
fs.ResourceStore.deleteFile(f.Key(), uid) // ignore err
|
||||
return "", err
|
||||
}
|
||||
return uid, err
|
||||
}
|
||||
|
||||
func (fs *FunctionStore) Delete(m fission.Metadata) error {
|
||||
if len(m.Uid) == 0 {
|
||||
err := fs.ResourceStore.deleteAllFiles(m.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := fs.ResourceStore.deleteFile(m.Name, m.Uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
typeName, err := getTypeName(fission.Function{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bufs, err := fs.ResourceStore.getAll("file/" + m.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(bufs) == 0 {
|
||||
return fs.ResourceStore.delete(typeName, m.Name)
|
||||
}
|
||||
|
||||
fnew, err := fs.Get(&fission.Metadata{Name: m.Name})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
latestUid := bufs[len(bufs)-1] // function always tracks the latest version of code
|
||||
if latestUid == fnew.Uid {
|
||||
return nil
|
||||
}
|
||||
fnew.Uid = latestUid
|
||||
return fs.ResourceStore.update(fnew)
|
||||
}
|
||||
|
||||
func (fs *FunctionStore) List() ([]fission.Function, error) {
|
||||
typeName, err := getTypeName(fission.Function{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bufs, err := fs.ResourceStore.getAll(typeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
js := JsonSerializer{}
|
||||
functions := make([]fission.Function, 0, len(bufs))
|
||||
for _, buf := range bufs {
|
||||
var f fission.Function
|
||||
err = js.deserialize([]byte(buf), &f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
functions = append(functions, f)
|
||||
}
|
||||
|
||||
return functions, nil
|
||||
}
|
||||
@@ -18,151 +18,170 @@ package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (api *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
||||
triggers, err := api.HTTPTriggerStore.List()
|
||||
func (a *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
||||
triggers, err := a.fissionClient.Httptriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(triggers)
|
||||
resp, err := json.Marshal(triggers.Items)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
func (a *API) checkHttpTriggerDuplicates(t *tpr.Httptrigger) error {
|
||||
triggers, err := a.fissionClient.Httptriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
var t fission.HTTPTrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
triggers, err := api.HTTPTriggerStore.List()
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
for _, url := range triggers {
|
||||
if url.UrlPattern == t.UrlPattern && url.Method == t.Method {
|
||||
err = fission.MakeError(fission.ErrorNameExists,
|
||||
"HTTPTrigger with same URL & method already exists")
|
||||
api.respondWithError(w, err)
|
||||
return
|
||||
for _, ht := range triggers.Items {
|
||||
if ht.Spec.RelativeURL == t.Spec.RelativeURL && ht.Spec.Method == t.Spec.Method {
|
||||
return fission.MakeError(fission.ErrorNameExists,
|
||||
fmt.Sprintf("HTTPTrigger with same URL & method already exists (%v)",
|
||||
ht.Metadata.Name))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
uid, err := api.HTTPTriggerStore.Create(&t)
|
||||
func (a *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
var t tpr.Httptrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = validateResourceName(t.Metadata.Name)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure we don't have a duplicate HTTP route defined (same URL and method)
|
||||
err = a.checkHttpTriggerDuplicates(&t)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
tnew, err := a.fissionClient.Httptriggers(t.Metadata.Namespace).Create(&t)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(tnew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
var m fission.Metadata
|
||||
|
||||
func (a *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
m.Name = vars["httpTrigger"]
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
name := vars["httpTrigger"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
t, err := api.HTTPTriggerStore.Get(&m)
|
||||
t, err := a.fissionClient.Httptriggers(ns).Get(name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["httpTrigger"]
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var t fission.HTTPTrigger
|
||||
var t tpr.Httptrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if name != t.Metadata.Name {
|
||||
err = fission.MakeError(fission.ErrorInvalidArgument, "HTTPTrigger name doesn't match URL")
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
uid, err := api.HTTPTriggerStore.Update(&t)
|
||||
err = a.checkHttpTriggerDuplicates(&t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
tnew, err := a.fissionClient.Httptriggers(t.Metadata.Namespace).Update(&t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
api.respondWithSuccess(w, resp)
|
||||
|
||||
resp, err := json.Marshal(tnew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) HTTPTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) HTTPTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
var m fission.Metadata
|
||||
m.Name = vars["httpTrigger"]
|
||||
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
if len(m.Uid) == 0 {
|
||||
log.WithFields(log.Fields{"httpTrigger": m.Name}).Info("Deleting all versions")
|
||||
name := vars["httpTrigger"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
err := api.HTTPTriggerStore.Delete(m)
|
||||
err := a.fissionClient.Httptriggers(ns).Delete(name, &api.DeleteOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, []byte(""))
|
||||
a.respondWithSuccess(w, []byte(""))
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/satori/go.uuid"
|
||||
|
||||
"github.com/fission/fission"
|
||||
)
|
||||
|
||||
type HTTPTriggerStore struct {
|
||||
ResourceStore
|
||||
}
|
||||
|
||||
func (hts *HTTPTriggerStore) Create(ht *fission.HTTPTrigger) (string, error) {
|
||||
ht.Metadata.Uid = uuid.NewV4().String()
|
||||
return ht.Metadata.Uid, hts.ResourceStore.create(ht)
|
||||
}
|
||||
|
||||
func (hts *HTTPTriggerStore) Get(m *fission.Metadata) (*fission.HTTPTrigger, error) {
|
||||
var ht fission.HTTPTrigger
|
||||
err := hts.ResourceStore.read(m.Name, &ht)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ht, nil
|
||||
}
|
||||
|
||||
func (hts *HTTPTriggerStore) Update(ht *fission.HTTPTrigger) (string, error) {
|
||||
ht.Metadata.Uid = uuid.NewV4().String()
|
||||
return ht.Metadata.Uid, hts.ResourceStore.update(ht)
|
||||
}
|
||||
|
||||
func (hts *HTTPTriggerStore) Delete(m fission.Metadata) error {
|
||||
typeName, err := getTypeName(fission.HTTPTrigger{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return hts.ResourceStore.delete(typeName, m.Name)
|
||||
}
|
||||
|
||||
func (hts *HTTPTriggerStore) List() ([]fission.HTTPTrigger, error) {
|
||||
typeName, err := getTypeName(fission.HTTPTrigger{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bufs, err := hts.ResourceStore.getAll(typeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggers := make([]fission.HTTPTrigger, 0, len(bufs))
|
||||
js := JsonSerializer{}
|
||||
for _, buf := range bufs {
|
||||
var ht fission.HTTPTrigger
|
||||
err = js.deserialize([]byte(buf), &ht)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
triggers = append(triggers, ht)
|
||||
}
|
||||
|
||||
return triggers, nil
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
Copyright 2017 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (a *API) MessageQueueTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
||||
//mqType := r.FormValue("mqtype") // ignored for now
|
||||
triggers, err := a.fissionClient.Messagequeuetriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
resp, err := json.Marshal(triggers.Items)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var mqTrigger tpr.Messagequeuetrigger
|
||||
err = json.Unmarshal(body, &mqTrigger)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = validateResourceName(mqTrigger.Metadata.Name)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
tnew, err := a.fissionClient.Messagequeuetriggers(mqTrigger.Metadata.Namespace).Create(&mqTrigger)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(tnew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (a *API) MessageQueueTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["mqTrigger"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
mqTrigger, err := a.fissionClient.Messagequeuetriggers(ns).Get(name)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
resp, err := json.Marshal(mqTrigger)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (a *API) MessageQueueTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["mqTrigger"]
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var mqTrigger tpr.Messagequeuetrigger
|
||||
err = json.Unmarshal(body, &mqTrigger)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if name != mqTrigger.Metadata.Name {
|
||||
err = fission.MakeError(fission.ErrorInvalidArgument, "Message queue trigger name doesn't match URL")
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
tnew, err := a.fissionClient.Messagequeuetriggers(mqTrigger.Metadata.Namespace).Update(&mqTrigger)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(tnew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (a *API) MessageQueueTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["mqTrigger"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
err := a.fissionClient.Messagequeuetriggers(ns).Delete(name, &api.DeleteOptions{})
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
a.respondWithSuccess(w, []byte(""))
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (a *API) PackageApiList(w http.ResponseWriter, r *http.Request) {
|
||||
funcs, err := a.fissionClient.Packages(api.NamespaceAll).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(funcs.Items)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (a *API) PackageApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var f tpr.Package
|
||||
err = json.Unmarshal(body, &f)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = validateResourceName(f.Metadata.Name)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure size limits
|
||||
if len(f.Spec.Literal) > 256*1024 {
|
||||
err := fission.MakeError(fission.ErrorInvalidArgument, "Package literal larger than 256K")
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
fnew, err := a.fissionClient.Packages(f.Metadata.Namespace).Create(&f)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(fnew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (a *API) PackageApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["package"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
raw := r.FormValue("raw") // just the deployment pkg
|
||||
|
||||
f, err := a.fissionClient.Packages(ns).Get(name)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var resp []byte
|
||||
if raw != "" {
|
||||
resp = []byte(f.Spec.Literal)
|
||||
} else {
|
||||
resp, err = json.Marshal(f)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (a *API) PackageApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["package"]
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var f tpr.Package
|
||||
err = json.Unmarshal(body, &f)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if name != f.Metadata.Name {
|
||||
err = fission.MakeError(fission.ErrorInvalidArgument, "Package name doesn't match URL")
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
fnew, err := a.fissionClient.Packages(f.Metadata.Namespace).Update(&f)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(fnew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (a *API) PackageApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["package"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
err := a.fissionClient.Packages(ns).Delete(name, &api.DeleteOptions{})
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.respondWithSuccess(w, []byte(""))
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/satori/go.uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/fission/fission"
|
||||
)
|
||||
|
||||
type (
|
||||
ResourceStore struct {
|
||||
*FileStore
|
||||
client.KeysAPI
|
||||
serializer
|
||||
}
|
||||
)
|
||||
|
||||
func MakeResourceStore(fs *FileStore, etcdUrls []string) (*ResourceStore, error) {
|
||||
ks, err := getEtcdKeyAPI(etcdUrls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := JsonSerializer{}
|
||||
return &ResourceStore{FileStore: fs, KeysAPI: ks, serializer: s}, nil
|
||||
}
|
||||
|
||||
func getEtcdKeyAPI(etcdUrls []string) (client.KeysAPI, error) {
|
||||
cfg := client.Config{
|
||||
Endpoints: etcdUrls,
|
||||
Transport: client.DefaultTransport,
|
||||
// set timeout per request to fail fast when the target endpoint is unavailable
|
||||
HeaderTimeoutPerRequest: time.Second,
|
||||
}
|
||||
c, err := client.New(cfg)
|
||||
if err != nil {
|
||||
log.Printf("failed to connect to etcd: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return client.NewKeysAPI(c), nil
|
||||
}
|
||||
|
||||
func getTypeName(r resource) (string, error) {
|
||||
typ := reflect.TypeOf(r)
|
||||
if typ.Kind().String() == "ptr" {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
typName := typ.Name()
|
||||
if len(typName) == 0 {
|
||||
return "", errors.New("Failed to get type")
|
||||
}
|
||||
return typName, nil
|
||||
}
|
||||
|
||||
func getKey(r resource) (string, error) {
|
||||
typName, err := getTypeName(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rkey := r.Key()
|
||||
return (typName + "/" + rkey), nil
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) create(r resource) error {
|
||||
key, err := getKey(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serialized, err := rs.serializer.serialize(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = rs.KeysAPI.Set(context.Background(), key, string(serialized),
|
||||
&client.SetOptions{PrevExist: client.PrevNoExist})
|
||||
return handleEtcdErrorForResource(err, r)
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) read(rkey string, res resource) error {
|
||||
typName, err := getTypeName(res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := typName + "/" + rkey
|
||||
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, nil)
|
||||
if err != nil {
|
||||
return handleEtcdError(err, typName, rkey)
|
||||
}
|
||||
return rs.serializer.deserialize([]byte(resp.Node.Value), res)
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) update(r resource) error {
|
||||
key, err := getKey(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serialized, err := rs.serializer.serialize(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = rs.KeysAPI.Set(context.Background(), key, string(serialized),
|
||||
&client.SetOptions{PrevExist: client.PrevExist})
|
||||
return handleEtcdErrorForResource(err, r)
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) delete(typename, rkey string) error {
|
||||
key := typename + "/" + rkey
|
||||
_, err := rs.KeysAPI.Delete(context.Background(), key, nil) // ignore response
|
||||
return handleEtcdError(err, typename, rkey)
|
||||
}
|
||||
|
||||
// getAll finds all entries under key. If none or found or key
|
||||
// doesn't exist, returns an empty slice.
|
||||
func (rs *ResourceStore) getAll(key string) ([]string, error) {
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Recursive: true, Sort: true})
|
||||
if err != nil {
|
||||
if client.IsKeyNotFound(err) {
|
||||
return []string{}, nil
|
||||
}
|
||||
return nil, handleEtcdError(err, "", key)
|
||||
}
|
||||
|
||||
res := make([]string, 0, len(resp.Node.Nodes))
|
||||
for _, n := range resp.Node.Nodes {
|
||||
res = append(res, n.Value)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) writeFile(parentKey string, contents []byte) (string, string, error) {
|
||||
uid := uuid.NewV4().String()
|
||||
|
||||
err := rs.FileStore.write(uid, contents)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
parentKey = "file/" + parentKey
|
||||
resp, err := rs.KeysAPI.CreateInOrder(context.Background(), parentKey, uid, nil)
|
||||
if err != nil {
|
||||
_ = rs.FileStore.delete(uid)
|
||||
return "", "", handleEtcdError(err, "file", parentKey)
|
||||
}
|
||||
|
||||
return resp.Node.Key, uid, nil
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) readFile(key string, uid *string) ([]byte, error) {
|
||||
key = "file/" + key
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
|
||||
if err != nil {
|
||||
return nil, handleEtcdError(err, "file", key)
|
||||
}
|
||||
|
||||
if uid == nil {
|
||||
// get latest
|
||||
n := resp.Node.Nodes
|
||||
uid = &n[len(n)-1].Value
|
||||
} else {
|
||||
// validate uid is in the list
|
||||
found := false
|
||||
for _, u := range resp.Node.Nodes {
|
||||
if *uid == u.Value {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, errors.New("Invalid UID " + *uid)
|
||||
}
|
||||
}
|
||||
|
||||
contents, err := rs.FileStore.read(*uid)
|
||||
return contents, err
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) deleteFile(key string, uid string) error {
|
||||
key = "file/" + key
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
|
||||
if err != nil {
|
||||
return handleEtcdError(err, "file", key)
|
||||
}
|
||||
|
||||
var node *client.Node
|
||||
for _, u := range resp.Node.Nodes {
|
||||
if u.Value == uid {
|
||||
node = u
|
||||
}
|
||||
}
|
||||
if node == nil {
|
||||
log.WithFields(log.Fields{"key": key, "uid": uid}).Error("unreferenced file")
|
||||
return errors.New("won't delete unreferenced file")
|
||||
}
|
||||
|
||||
err = rs.FileStore.delete(node.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = rs.KeysAPI.Delete(context.Background(), node.Key, nil)
|
||||
if err != nil {
|
||||
return handleEtcdError(err, "", node.Key)
|
||||
}
|
||||
|
||||
if len(resp.Node.Nodes) == 1 {
|
||||
_, err = rs.KeysAPI.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
|
||||
return handleEtcdError(err, "file", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) deleteAllFiles(key string) error {
|
||||
key = "file/" + key
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
|
||||
if err != nil {
|
||||
return handleEtcdError(err, "file", key)
|
||||
}
|
||||
for _, u := range resp.Node.Nodes {
|
||||
err = rs.FileStore.delete(u.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = rs.KeysAPI.Delete(context.Background(), u.Key, nil)
|
||||
if err != nil {
|
||||
return handleEtcdError(err, "", u.Key)
|
||||
}
|
||||
}
|
||||
_, err = rs.KeysAPI.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
|
||||
return handleEtcdError(err, "file", key)
|
||||
}
|
||||
|
||||
func handleEtcdErrorForResource(e error, r resource) error {
|
||||
resourceType, _ := getTypeName(r)
|
||||
return handleEtcdError(e, resourceType, r.Key())
|
||||
}
|
||||
|
||||
func handleEtcdError(e error, resourceType string, resourceKey string) error {
|
||||
ee, ok := e.(client.Error)
|
||||
if !ok {
|
||||
return e
|
||||
}
|
||||
code := fission.ErrorInternal
|
||||
msg := ee.Error()
|
||||
|
||||
if len(resourceType) > 0 {
|
||||
resourceType = strings.ToLower(resourceType) + " "
|
||||
}
|
||||
|
||||
//TODO: handle any other etcd error codes we care about
|
||||
switch ee.Code {
|
||||
case client.ErrorCodeNodeExist:
|
||||
code = fission.ErrorNameExists
|
||||
msg = fmt.Sprintf("%s'%s' already exists", resourceType, resourceKey)
|
||||
case client.ErrorCodeKeyNotFound:
|
||||
code = fission.ErrorNotFound
|
||||
msg = fmt.Sprintf("%s'%s' does not exist", resourceType, resourceKey)
|
||||
}
|
||||
return fission.MakeError(code, msg)
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type TestResource struct {
|
||||
A string
|
||||
B int
|
||||
}
|
||||
|
||||
func (tr TestResource) Key() string {
|
||||
return tr.A
|
||||
}
|
||||
|
||||
func panicIf(err error) {
|
||||
if err != nil {
|
||||
log.Panicf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func assert(b bool, msg string) {
|
||||
if !b {
|
||||
log.Panic("assertion failed: " + msg)
|
||||
}
|
||||
}
|
||||
|
||||
func getTestResourceStore() (*FileStore, client.KeysAPI, *ResourceStore) {
|
||||
// make a tmp dir
|
||||
dir, err := ioutil.TempDir("", "testFileStore")
|
||||
panicIf(err)
|
||||
fs := MakeFileStore(dir)
|
||||
|
||||
rs, err := MakeResourceStore(fs, []string{"http://localhost:2379"})
|
||||
panicIf(err)
|
||||
|
||||
return fs, rs.KeysAPI, rs
|
||||
}
|
||||
|
||||
func TestResourceStore(t *testing.T) {
|
||||
fs, ks, rs := getTestResourceStore()
|
||||
defer os.RemoveAll(fs.root)
|
||||
|
||||
s := JsonSerializer{}
|
||||
|
||||
tr := TestResource{A: "hello", B: 1}
|
||||
|
||||
// Delete the key first, in case of a panic'd previous test run; ignore errors
|
||||
_ = rs.delete("TestResource", tr.Key())
|
||||
ks.Delete(context.Background(), "/TestResource", &client.DeleteOptions{Dir: true})
|
||||
|
||||
// Verify getAll for empty db
|
||||
trs, err := rs.getAll("TestResource")
|
||||
panicIf(err)
|
||||
if len(trs) != 0 {
|
||||
log.Fatalf("Expected zero length slice, got %v", trs)
|
||||
}
|
||||
|
||||
// Create
|
||||
err = rs.create(tr)
|
||||
panicIf(err)
|
||||
defer ks.Delete(context.Background(), "/TestResource", &client.DeleteOptions{Dir: true})
|
||||
defer rs.delete("TestResource", tr.Key())
|
||||
|
||||
// Etcd key /TestResource/hello should exist
|
||||
_, err = ks.Get(context.Background(), "TestResource/hello", nil)
|
||||
panicIf(err)
|
||||
|
||||
// Read
|
||||
tr1 := TestResource{}
|
||||
err = rs.read(tr.Key(), &tr1)
|
||||
panicIf(err)
|
||||
assert(tr1 == tr, "retrieved value must equal created value")
|
||||
|
||||
// Update and Read
|
||||
tr.B += 1
|
||||
err = rs.update(tr)
|
||||
panicIf(err)
|
||||
err = rs.read(tr.Key(), &tr1)
|
||||
panicIf(err)
|
||||
assert(tr1 == tr, "retrieved value must equal updated value")
|
||||
|
||||
// Get list
|
||||
results, err := rs.getAll("TestResource")
|
||||
panicIf(err)
|
||||
res := make([]TestResource, 0, 0)
|
||||
for _, r := range results {
|
||||
tmp := TestResource{}
|
||||
err = s.deserialize([]byte(r), &tmp)
|
||||
panicIf(err)
|
||||
res = append(res, tmp)
|
||||
}
|
||||
assert(res[0] == tr, "value from retrieved list must equal updated value")
|
||||
|
||||
// file tests
|
||||
fileKey := "ResourceStoreTest"
|
||||
fileContents1 := []byte("hello")
|
||||
fileContents2 := []byte("world")
|
||||
key, uid1, err := rs.writeFile(fileKey, fileContents1)
|
||||
panicIf(err)
|
||||
defer rs.deleteFile(fileKey, uid1)
|
||||
log.Printf("key = %v, uid = %v", key, uid1)
|
||||
|
||||
// read latest
|
||||
contents, err := rs.readFile(fileKey, nil)
|
||||
panicIf(err)
|
||||
assert(string(contents) == string(fileContents1), "retrieved file contents must match written value")
|
||||
|
||||
// update-- same key new contents
|
||||
_, uid2, err := rs.writeFile(fileKey, fileContents2)
|
||||
panicIf(err)
|
||||
defer rs.deleteFile(fileKey, uid2)
|
||||
|
||||
// read latest
|
||||
contents, err = rs.readFile(fileKey, nil)
|
||||
panicIf(err)
|
||||
assert(string(contents) == string(fileContents2), "retrieved file contents must match updated value")
|
||||
|
||||
// read by uid
|
||||
// 1
|
||||
contents, err = rs.readFile(fileKey, &uid1)
|
||||
panicIf(err)
|
||||
assert(string(contents) == string(fileContents1), "retrieved file contents must match updated value")
|
||||
|
||||
// 2
|
||||
contents, err = rs.readFile(fileKey, &uid2)
|
||||
panicIf(err)
|
||||
assert(string(contents) == string(fileContents2), "retrieved file contents must match updated value")
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
Copyright 2017 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,13 +16,23 @@ limitations under the License.
|
||||
|
||||
package controller
|
||||
|
||||
type (
|
||||
resource interface {
|
||||
Key() string
|
||||
}
|
||||
|
||||
serializer interface {
|
||||
serialize(r resource) ([]byte, error)
|
||||
deserialize(buf []byte, r resource) error
|
||||
}
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func (api *API) StorageServiceProxy(w http.ResponseWriter, r *http.Request) {
|
||||
u := api.storageServiceUrl + "/v1/archive"
|
||||
ssUrl, err := url.Parse(u)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Error parsing url %v: %v", u, err)
|
||||
log.Println(msg)
|
||||
http.Error(w, msg, 500)
|
||||
return
|
||||
}
|
||||
proxy := httputil.NewSingleHostReverseProxy(ssUrl)
|
||||
proxy.ServeHTTP(w, r)
|
||||
}
|
||||
@@ -23,161 +23,152 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/robfig/cron"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (api *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
||||
triggers, err := api.TimeTriggerStore.List()
|
||||
func (a *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
||||
triggers, err := a.fissionClient.Timetriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(triggers)
|
||||
resp, err := json.Marshal(triggers.Items)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var t fission.TimeTrigger
|
||||
var t tpr.Timetrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
triggers, err := api.TimeTriggerStore.List()
|
||||
err = validateResourceName(t.Metadata.Name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
for _, trigger := range triggers {
|
||||
if trigger.Name == t.Name {
|
||||
err = fission.MakeError(fission.ErrorNameExists,
|
||||
"TimeTrigger with same name already exists")
|
||||
api.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = cron.Parse(t.Cron)
|
||||
// validate
|
||||
_, err = cron.Parse(t.Spec.Cron)
|
||||
if err != nil {
|
||||
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
uid, err := api.TimeTriggerStore.Create(&t)
|
||||
tnew, err := a.fissionClient.Timetriggers(t.Metadata.Namespace).Create(&t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
resp, err := json.Marshal(tnew.Metadata)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
var m fission.Metadata
|
||||
|
||||
func (a *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
m.Name = vars["timeTrigger"]
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
name := vars["timeTrigger"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
t, err := api.TimeTriggerStore.Get(&m)
|
||||
t, err := a.fissionClient.Timetriggers(ns).Get(name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) TimeTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) TimeTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["timeTrigger"]
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var t fission.TimeTrigger
|
||||
var t tpr.Timetrigger
|
||||
err = json.Unmarshal(body, &t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if name != t.Metadata.Name {
|
||||
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger name doesn't match URL")
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = cron.Parse(t.Cron)
|
||||
_, err = cron.Parse(t.Spec.Cron)
|
||||
if err != nil {
|
||||
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
uid, err := api.TimeTriggerStore.Update(&t)
|
||||
tnew, err := a.fissionClient.Timetriggers(t.Metadata.Namespace).Update(&t)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
resp, err := json.Marshal(tnew.Metadata)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) TimeTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) TimeTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
var m fission.Metadata
|
||||
m.Name = vars["timeTrigger"]
|
||||
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
if len(m.Uid) == 0 {
|
||||
log.WithFields(log.Fields{"timeTrigger": m.Name}).Info("Deleting all versions")
|
||||
name := vars["timeTrigger"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
err := api.TimeTriggerStore.Delete(m)
|
||||
err := a.fissionClient.Timetriggers(ns).Delete(name, &api.DeleteOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, []byte(""))
|
||||
a.respondWithSuccess(w, []byte(""))
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
Copyrigtt 2017 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
tttp://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/satori/go.uuid"
|
||||
|
||||
"github.com/fission/fission"
|
||||
)
|
||||
|
||||
type TimeTriggerStore struct {
|
||||
ResourceStore
|
||||
}
|
||||
|
||||
func (tts *TimeTriggerStore) Create(tt *fission.TimeTrigger) (string, error) {
|
||||
tt.Metadata.Uid = uuid.NewV4().String()
|
||||
return tt.Metadata.Uid, tts.ResourceStore.create(tt)
|
||||
}
|
||||
|
||||
func (tts *TimeTriggerStore) Get(m *fission.Metadata) (*fission.TimeTrigger, error) {
|
||||
var tt fission.TimeTrigger
|
||||
err := tts.ResourceStore.read(m.Name, &tt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tt, nil
|
||||
}
|
||||
|
||||
func (tts *TimeTriggerStore) Update(tt *fission.TimeTrigger) (string, error) {
|
||||
tt.Metadata.Uid = uuid.NewV4().String()
|
||||
return tt.Metadata.Uid, tts.ResourceStore.update(tt)
|
||||
}
|
||||
|
||||
func (tts *TimeTriggerStore) Delete(m fission.Metadata) error {
|
||||
typeName, err := getTypeName(fission.TimeTrigger{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tts.ResourceStore.delete(typeName, m.Name)
|
||||
}
|
||||
|
||||
func (tts *TimeTriggerStore) List() ([]fission.TimeTrigger, error) {
|
||||
typeName, err := getTypeName(fission.TimeTrigger{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bufs, err := tts.ResourceStore.getAll(typeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggers := make([]fission.TimeTrigger, 0, len(bufs))
|
||||
js := JsonSerializer{}
|
||||
for _, buf := range bufs {
|
||||
var tt fission.TimeTrigger
|
||||
err = js.deserialize([]byte(buf), &tt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
triggers = append(triggers, tt)
|
||||
}
|
||||
|
||||
return triggers, nil
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func makeTPRBackedAPI() (*API, error) {
|
||||
fissionClient, _, err := tpr.MakeFissionClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &API{fissionClient: fissionClient}, nil
|
||||
}
|
||||
|
||||
func validateResourceName(name string) error {
|
||||
re := regexp.MustCompile(`[a-z0-9]([-a-z0-9]*[a-z0-9])?`)
|
||||
if len(re.FindString(name)) != len(name) {
|
||||
return errors.New("Name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?'")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+45
-40
@@ -22,102 +22,107 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
func (api *API) WatchApiList(w http.ResponseWriter, r *http.Request) {
|
||||
watches, err := api.WatchStore.List()
|
||||
func (a *API) WatchApiList(w http.ResponseWriter, r *http.Request) {
|
||||
watches, err := a.fissionClient.Kuberneteswatchtriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(watches)
|
||||
resp, err := json.Marshal(watches.Items)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var watch fission.Watch
|
||||
var watch tpr.Kuberneteswatchtrigger
|
||||
err = json.Unmarshal(body, &watch)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
watch.Target = fission.UrlForFunction(&watch.Function)
|
||||
|
||||
uid, err := api.WatchStore.Create(&watch)
|
||||
err = validateResourceName(watch.Metadata.Name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := &fission.Metadata{Name: watch.Metadata.Name, Uid: uid}
|
||||
resp, err := json.Marshal(m)
|
||||
// TODO check for duplicate watches
|
||||
|
||||
wnew, err := a.fissionClient.Kuberneteswatchtriggers(watch.Metadata.Namespace).Create(&watch)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(wnew.Metadata)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
var m fission.Metadata
|
||||
|
||||
func (a *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
m.Name = vars["watch"]
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
name := vars["watch"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
watch, err := api.WatchStore.Get(&m)
|
||||
watch, err := a.fissionClient.Kuberneteswatchtriggers(ns).Get(name)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := json.Marshal(watch)
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, resp)
|
||||
a.respondWithSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (api *API) WatchApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
api.respondWithError(w, fission.MakeError(fission.ErrorNotImplmented,
|
||||
func (a *API) WatchApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
a.respondWithError(w, fission.MakeError(fission.ErrorNotImplmented,
|
||||
"Not implemented"))
|
||||
}
|
||||
|
||||
func (api *API) WatchApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) WatchApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
var m fission.Metadata
|
||||
m.Name = vars["watch"]
|
||||
|
||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
||||
if len(m.Uid) == 0 {
|
||||
log.WithFields(log.Fields{"watch": m.Name}).Info("Deleting all versions")
|
||||
name := vars["watch"]
|
||||
ns := vars["namespace"]
|
||||
if len(ns) == 0 {
|
||||
ns = api.NamespaceDefault
|
||||
}
|
||||
|
||||
err := api.WatchStore.Delete(m)
|
||||
err := a.fissionClient.Kuberneteswatchtriggers(ns).Delete(name, &api.DeleteOptions{})
|
||||
if err != nil {
|
||||
api.respondWithError(w, err)
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.respondWithSuccess(w, []byte(""))
|
||||
a.respondWithSuccess(w, []byte(""))
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/satori/go.uuid"
|
||||
|
||||
"github.com/fission/fission"
|
||||
)
|
||||
|
||||
type WatchStore struct {
|
||||
ResourceStore
|
||||
}
|
||||
|
||||
func (ws *WatchStore) Create(w *fission.Watch) (string, error) {
|
||||
w.Metadata.Uid = uuid.NewV4().String()
|
||||
return w.Metadata.Uid, ws.ResourceStore.create(w)
|
||||
}
|
||||
|
||||
func (ws *WatchStore) Get(m *fission.Metadata) (*fission.Watch, error) {
|
||||
var w fission.Watch
|
||||
err := ws.ResourceStore.read(m.Name, &w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &w, err
|
||||
}
|
||||
|
||||
func (ws *WatchStore) Update(w *fission.Watch) (string, error) {
|
||||
w.Metadata.Uid = uuid.NewV4().String()
|
||||
return w.Metadata.Uid, ws.ResourceStore.update(w)
|
||||
}
|
||||
|
||||
func (ws *WatchStore) Delete(m fission.Metadata) error {
|
||||
typeName, err := getTypeName(fission.Watch{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ws.ResourceStore.delete(typeName, m.Name)
|
||||
}
|
||||
|
||||
func (ws *WatchStore) List() ([]fission.Watch, error) {
|
||||
typeName, err := getTypeName(fission.Watch{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bufs, err := ws.ResourceStore.getAll(typeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
watches := make([]fission.Watch, 0, len(bufs))
|
||||
js := JsonSerializer{}
|
||||
for _, buf := range bufs {
|
||||
var w fission.Watch
|
||||
err = js.deserialize([]byte(buf), &w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
watches = append(watches, w)
|
||||
}
|
||||
|
||||
return watches, nil
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# Handles POST /guestbook -- adds item to guestbook
|
||||
#
|
||||
|
||||
from flask import request, redirect
|
||||
import redis
|
||||
|
||||
# Connect to redis.
|
||||
redisConnection = redis.StrictRedis(host='redis.guestbook', port=6379, db=0)
|
||||
|
||||
def main():
|
||||
# Read the item from POST params, add it to redis, and redirect
|
||||
# back to the list
|
||||
item = request.form['text']
|
||||
redisConnection.rpush('guestbook', item)
|
||||
return redirect('/guestbook', code=303)
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
kubectl delete ns guestbook
|
||||
|
||||
fission fn delete --name guestbook-add
|
||||
fission fn delete --name guestbook-get
|
||||
|
||||
fission route list|grep guestbook|cut -f1 -d' '|xargs -n1 fission route delete --name
|
||||
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Handles GET /guestbook -- returns a list of items in the guestbook
|
||||
# with a form to add more.
|
||||
#
|
||||
|
||||
from flask import current_app, escape
|
||||
import redis
|
||||
|
||||
# Connect to redis. This is run only when this file is loaded; as
|
||||
# long as the pod is alive, the connection is reused.
|
||||
redisConnection = redis.StrictRedis(host='redis.guestbook', port=6379, db=0)
|
||||
|
||||
def main():
|
||||
messages = redisConnection.lrange('guestbook', 0, -1)
|
||||
|
||||
items = [("<li>%s</li>" % escape(m.decode('utf-8'))) for m in messages]
|
||||
ul = "<ul>%s</ul>" % "\n".join(items)
|
||||
return """
|
||||
<html><body style="font-family:sans-serif;font-size:2rem;padding:40px">
|
||||
<h1>Guestbook</h1>
|
||||
<form action="/guestbook" method="POST">
|
||||
<input type="text" name="text">
|
||||
<button type="submit">Add</button>
|
||||
</form>
|
||||
<hr/>
|
||||
%s
|
||||
</body></html>
|
||||
""" % ul
|
||||
@@ -0,0 +1,45 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: guestbook
|
||||
labels:
|
||||
name: guestbook
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
run: redis
|
||||
name: redis
|
||||
namespace: guestbook
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
run: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
run: redis
|
||||
spec:
|
||||
containers:
|
||||
- image: redis
|
||||
name: redis
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
run: redis
|
||||
name: redis
|
||||
namespace: guestbook
|
||||
spec:
|
||||
selector:
|
||||
run: redis
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 6379
|
||||
protocol: TCP
|
||||
targetPort: 6379
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEMO_RUN_FAST=1
|
||||
ROOT_DIR=$(dirname $0)/..
|
||||
. $ROOT_DIR/util.sh
|
||||
|
||||
desc "Deploy redis"
|
||||
run "kubectl create -f redis.yaml"
|
||||
|
||||
desc "Ensure fission has a python environment"
|
||||
run "fission env create --name python --image fission/python-env"
|
||||
|
||||
desc "Register fission functions"
|
||||
run "fission function create --name guestbook-get --env python --code get.py --url /guestbook --method GET"
|
||||
run "fission function create --name guestbook-add --env python --code add.py --url /guestbook --method POST"
|
||||
|
||||
echo "http://$FISSION_ROUTER/guestbook"
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
fission fn delete --name hello
|
||||
fission route delete --name $(fission route list|grep hello|cut -f1 -d' ')
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
module.exports = async function(context) {
|
||||
return {
|
||||
status: 200,
|
||||
body: "Hello, world!\n"
|
||||
};
|
||||
}
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEMO_RUN_FAST=1
|
||||
ROOT_DIR=$(dirname $0)/..
|
||||
. $ROOT_DIR/util.sh
|
||||
|
||||
desc "Kubernetes cluster"
|
||||
run "kubectl get nodes"
|
||||
|
||||
desc "Fission installed"
|
||||
run "kubectl --namespace fission get deployment"
|
||||
|
||||
clear
|
||||
|
||||
desc "Hello world function"
|
||||
run "cat hello.js"
|
||||
|
||||
desc "Add NodeJS environment to fission"
|
||||
run "fission env create --name nodejs --image fission/node-env"
|
||||
|
||||
desc "NodeJS environment pods"
|
||||
run "kubectl --namespace fission-function get pod -l environmentName=nodejs"
|
||||
|
||||
desc "Upload a function to fission"
|
||||
run "fission function create --name hello --env nodejs --code hello.js"
|
||||
|
||||
desc "Set up a route \(HTTP trigger\) for the function"
|
||||
run "fission route create --method GET --url /hello --function hello"
|
||||
|
||||
sleep 2
|
||||
|
||||
desc "Finally, run the function"
|
||||
run "time -p curl http://$FISSION_ROUTER/hello"
|
||||
|
||||
desc "Run the function again"
|
||||
run "time -p curl http://$FISSION_ROUTER/hello"
|
||||
run "time -p curl http://$FISSION_ROUTER/hello"
|
||||
|
||||
desc "The pod is now labeled by the function name"
|
||||
run "kubectl --namespace fission-function get pod -l functionName=hello"
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2016 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
readonly reset=$(tput sgr0)
|
||||
readonly green=$(tput bold; tput setaf 2)
|
||||
readonly yellow=$(tput bold; tput setaf 3)
|
||||
readonly blue=$(tput bold; tput setaf 6)
|
||||
readonly timeout=$(if [ "$(uname)" == "Darwin" ]; then echo "1"; else echo "0.1"; fi)
|
||||
|
||||
function desc() {
|
||||
maybe_first_prompt
|
||||
echo "$blue# $@$reset"
|
||||
prompt
|
||||
}
|
||||
|
||||
function prompt() {
|
||||
echo -n "$yellow\$ $reset"
|
||||
}
|
||||
|
||||
started=""
|
||||
function maybe_first_prompt() {
|
||||
if [ -z "$started" ]; then
|
||||
prompt
|
||||
started=true
|
||||
fi
|
||||
}
|
||||
|
||||
# After a `run` this variable will hold the stdout of the command that was run.
|
||||
# If the command was interactive, this will likely be garbage.
|
||||
#DEMO_RUN_STDOUT=""
|
||||
|
||||
function run() {
|
||||
maybe_first_prompt
|
||||
rate=25
|
||||
if [ -n "$DEMO_RUN_FAST" ]; then
|
||||
rate=1000
|
||||
fi
|
||||
echo "$green$1$reset" | pv -qL $rate
|
||||
if [ -n "$DEMO_RUN_FAST" ]; then
|
||||
sleep 0.5
|
||||
fi
|
||||
$1
|
||||
echo; echo -n ">"
|
||||
read -s
|
||||
echo -e "\b "
|
||||
# OFILE="$(mktemp -t $(basename $0).XXXXXX)"
|
||||
# script -q -t0 "$OFILE" $1 # [removed -e,-c options -soam]
|
||||
# r=$?
|
||||
# read -d '' -t "${timeout}" -n 10000 # clear stdin
|
||||
# prompt
|
||||
# if [ -z "$DEMO_AUTO_RUN" ]; then
|
||||
# read -s
|
||||
# fi
|
||||
# DEMO_RUN_STDOUT="$(tail -n +2 $OFILE | sed 's/\r//g')"
|
||||
# return $r
|
||||
}
|
||||
|
||||
function relative() {
|
||||
for arg; do
|
||||
echo "$(realpath $(dirname $(which $0)))/$arg" | sed "s|$(realpath $(pwd))|.|"
|
||||
done
|
||||
}
|
||||
|
||||
SSH_NODE=$(kubectl get nodes | tail -1 | cut -f1 -d' ')
|
||||
|
||||
#trap "echo" EXIT
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM alpine:3.5
|
||||
|
||||
RUN apk update
|
||||
RUN apk add coreutils binutils findutils grep
|
||||
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
|
||||
EXPOSE 8888
|
||||
ENTRYPOINT ["./server"]
|
||||
@@ -0,0 +1,50 @@
|
||||
# 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
|
||||
- Run executables of languages that have no dedicated environment yet.
|
||||
|
||||
## Words of Caution
|
||||
The environment runs on an alpine image with some additional utilty commandline tools installed, such as 'grep'.
|
||||
However, in case you want to make use of more ecsoteric commandline 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.
|
||||
|
||||
## Usage
|
||||
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:
|
||||
|
||||
```
|
||||
# Request Metadata
|
||||
CONTENT_LENGTH
|
||||
REQUEST_URI
|
||||
REQUEST_METHOD
|
||||
|
||||
# HTTP Headers
|
||||
HTTP_ACCEPT
|
||||
HTTP_USER-AGENT
|
||||
...
|
||||
```
|
||||
|
||||
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
|
||||
In order to build the Dockerfile, the server needs to be compiled to the right architecture.
|
||||
|
||||
```bash
|
||||
sh ./build.sh
|
||||
```
|
||||
|
||||
Build the Dockerfile:
|
||||
```bash
|
||||
docker build --tag=${USER}/binary-env .
|
||||
```
|
||||
|
||||
See the [README](../../examples/binary/README.md) in the binary examples directory for usage instructions.
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
GOOS=linux GOARCH=386 go build -o server .
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
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{}
|
||||
if stringEnv != nil {
|
||||
for _, rawEnvVar := range stringEnv {
|
||||
env.SetEnv(FromString(rawEnvVar))
|
||||
}
|
||||
}
|
||||
return env
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"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
|
||||
}
|
||||
|
||||
func (bs *BinaryServer) SpecializeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if specialized {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("Not a generic container"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err := os.Stat(bs.fetchedCodePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(bs.fetchedCodePath + ": 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(bs.fetchedCodePath)
|
||||
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)
|
||||
|
||||
fmt.Println("Listening on 8888 ...")
|
||||
err = http.ListenAndServe(":8888", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
GOOS=linux GOARCH=386 go build -o fetcher .
|
||||
@@ -0,0 +1,35 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
//"time"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/environments/fetcher"
|
||||
//"github.com/fission/fission/router"
|
||||
)
|
||||
|
||||
func DoFetchRequest(fetcherUrl string, fr *fetcher.FetchRequest) error {
|
||||
body, err := json.Marshal(fr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// client := http.Client{
|
||||
// Transport: router.MakeRetryingRoundTripper(10, 50*time.Millisecond),
|
||||
// }
|
||||
|
||||
resp, err := http.Post(fetcherUrl, "application/json", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fission.MakeErrorFromHTTP(resp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o fetcher .
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/fission/fission/environments/fetcher"
|
||||
)
|
||||
|
||||
// Usage: fetcher <shared volume path>
|
||||
func main() {
|
||||
dir := os.Args[1]
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, os.ModeDir|0700)
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating directory: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
fetcher := fetcher.MakeFetcher(dir)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", fetcher.Handler)
|
||||
http.ListenAndServe(":8000", mux)
|
||||
}
|
||||
+207
-63
@@ -1,34 +1,120 @@
|
||||
package main
|
||||
package fetcher
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/mholt/archiver"
|
||||
"github.com/satori/go.uuid"
|
||||
"k8s.io/client-go/1.5/kubernetes"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/tpr"
|
||||
)
|
||||
|
||||
type FetchRequest struct {
|
||||
Url string `json:"url"`
|
||||
Filename string `json:"filename"`
|
||||
}
|
||||
type (
|
||||
FetchRequestType int
|
||||
|
||||
type Fetcher struct {
|
||||
sharedVolumePath string
|
||||
}
|
||||
FetchRequest struct {
|
||||
FetchType FetchRequestType `json:"fetchType"`
|
||||
Function api.ObjectMeta `json:"function"`
|
||||
Url string `json:"url"`
|
||||
StorageSvcUrl string `json:"storagesvcurl"`
|
||||
Filename string `json:"filename"`
|
||||
}
|
||||
|
||||
Fetcher struct {
|
||||
sharedVolumePath string
|
||||
fissionClient *tpr.FissionClient
|
||||
kubeClient *kubernetes.Clientset
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
FETCH_SOURCE = iota
|
||||
FETCH_DEPLOYMENT
|
||||
FETCH_URL // remove this?
|
||||
)
|
||||
|
||||
func MakeFetcher(sharedVolumePath string) *Fetcher {
|
||||
return &Fetcher{sharedVolumePath: sharedVolumePath}
|
||||
fissionClient, kubeClient, err := tpr.MakeFissionClient()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &Fetcher{
|
||||
sharedVolumePath: sharedVolumePath,
|
||||
fissionClient: fissionClient,
|
||||
kubeClient: kubeClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (fetcher *Fetcher) handler(w http.ResponseWriter, r *http.Request) {
|
||||
func downloadUrl(url string, localPath string) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(localPath, body, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyChecksum(path string, checksum *fission.Checksum) error {
|
||||
if checksum.Type != fission.ChecksumTypeSHA256 {
|
||||
return fission.MakeError(fission.ErrorInvalidArgument, "Unsupported checksum type")
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
_, err = io.Copy(hasher, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c := hex.EncodeToString(hasher.Sum(nil))
|
||||
if c != checksum.Sum {
|
||||
return fission.MakeError(fission.ErrorChecksumFail, "Checksum validation failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fetcher *Fetcher) Handler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "", 404)
|
||||
http.Error(w, "", 405)
|
||||
return
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
elapsed := time.Now().Sub(startTime)
|
||||
log.Printf("elapsed time in fetch request = %v", elapsed)
|
||||
}()
|
||||
|
||||
// parse request
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
@@ -40,65 +126,123 @@ func (fetcher *Fetcher) handler(w http.ResponseWriter, r *http.Request) {
|
||||
err = json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
log.Printf("Error reading request body: %v", err)
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
log.Printf("fetcher received request: %v", req)
|
||||
|
||||
tmpFile := req.Filename + ".tmp"
|
||||
tmpPath := filepath.Join(fetcher.sharedVolumePath, tmpFile)
|
||||
|
||||
if req.FetchType == FETCH_URL {
|
||||
// fetch the file and save it to the tmp path
|
||||
err := downloadUrl(req.Url, tmpPath)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to download url %v: %v", req.Url, err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 400)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// get function object
|
||||
fn, err := fetcher.fissionClient.Functions(req.Function.Namespace).Get(req.Function.Name)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to get function: %v", err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 500)
|
||||
return
|
||||
}
|
||||
|
||||
// get pkg
|
||||
var pkg *tpr.Package
|
||||
if req.FetchType == FETCH_SOURCE {
|
||||
pkg, err = fetcher.fissionClient.Packages(fn.Spec.Source.PackageRef.Namespace).Get(fn.Spec.Source.PackageRef.Name)
|
||||
} else if req.FetchType == FETCH_DEPLOYMENT {
|
||||
pkg, err = fetcher.fissionClient.Packages(fn.Spec.Deployment.PackageRef.Namespace).Get(fn.Spec.Deployment.PackageRef.Name)
|
||||
}
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to get package: %v", err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 500)
|
||||
return
|
||||
}
|
||||
|
||||
// get package data as literal or by url
|
||||
if len(pkg.Spec.Literal) > 0 {
|
||||
// write pkg.Literal into tmpPath
|
||||
err = ioutil.WriteFile(tmpPath, pkg.Spec.Literal, 0600)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to write file %v: %v", tmpPath, err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 500)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// download and verify
|
||||
|
||||
err = downloadUrl(pkg.Spec.URL, tmpPath)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to download url %v: %v", req.Url, err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 400)
|
||||
return
|
||||
}
|
||||
|
||||
err = verifyChecksum(tmpPath, &pkg.Spec.Checksum)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to verify checksum: %v", err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check file type here, if the file is a zip file unarchive it.
|
||||
if archiver.Zip.Match(tmpPath) {
|
||||
// unarchive tmp file to a tmp unarchive path
|
||||
tmpUnarchivePath := filepath.Join(fetcher.sharedVolumePath, uuid.NewV4().String())
|
||||
err = fetcher.unarchive(tmpPath, tmpUnarchivePath)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
tmpPath = tmpUnarchivePath
|
||||
}
|
||||
|
||||
// move tmp file to requested filename
|
||||
err = fetcher.rename(tmpPath, filepath.Join(fetcher.sharedVolumePath, req.Filename))
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
log.Printf("fetcher request: %v", req)
|
||||
|
||||
// fetch the file and save it to tmp path
|
||||
resp, err := http.Get(req.Url)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to fetch from url: %v", err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 400)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to read from url: %v", err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 400)
|
||||
return
|
||||
}
|
||||
tmpFile := req.Filename + ".tmp"
|
||||
tmpPath := filepath.Join(fetcher.sharedVolumePath, tmpFile)
|
||||
err = ioutil.WriteFile(tmpPath, body, 0600)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to write file: %v", err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 500)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: add signature verification
|
||||
|
||||
// move tmp file to requested filename
|
||||
err = os.Rename(tmpPath, filepath.Join(fetcher.sharedVolumePath, req.Filename))
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("Failed to move file: %v", err)
|
||||
log.Printf(e)
|
||||
http.Error(w, e, 500)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Completed fetch request")
|
||||
// all done
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// Usage: fetcher <shared volume path>
|
||||
func main() {
|
||||
dir := os.Args[1]
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, os.ModeDir|0700)
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating directory: %v", err)
|
||||
}
|
||||
}
|
||||
func (fetcher *Fetcher) rename(src string, dst string) error {
|
||||
err := os.Rename(src, dst)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Failed to move file: %v", err))
|
||||
}
|
||||
fetcher := MakeFetcher(dir)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", fetcher.handler)
|
||||
http.ListenAndServe(":8000", mux)
|
||||
return nil
|
||||
}
|
||||
|
||||
// archive is a function that zips directory into a zip file
|
||||
func (fetcher *Fetcher) archive(src string, dst string) error {
|
||||
return archiver.Zip.Make(dst, []string{src})
|
||||
}
|
||||
|
||||
// unarchive is a function that unzips a zip file to destination
|
||||
func (fetcher *Fetcher) unarchive(src string, dst string) error {
|
||||
err := archiver.Zip.Open(src, dst)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Failed to unzip file: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
FROM alpine:3.5
|
||||
|
||||
RUN apk update
|
||||
RUN apk add --no-cache python3 python3-dev build-base
|
||||
RUN pip3 install --upgrade pip
|
||||
RUN rm -r /root/.cache
|
||||
|
||||
ADD defaultBuildCmd /usr/local/bin/build
|
||||
ADD builder /
|
||||
|
||||
EXPOSE 8000
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
builderDir=${GOPATH}/src/github.com/fission/fission/builder/cmd
|
||||
pushd ${builderDir}
|
||||
GOOS=linux GOARCH=386 go build -o builder .
|
||||
popd
|
||||
cp ${builderDir}/builder .
|
||||
docker build -t python-builder .
|
||||
docker tag python-builder fission/python-builder:$tag
|
||||
docker push fission/python-builder:$tag
|
||||
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
pip3 install -r ${SRC_PKG}/requirements.txt -t ${SRC_PKG} && cp -r ${SRC_PKG} ${DEPLOY_PKG}
|
||||
@@ -2,9 +2,20 @@
|
||||
|
||||
This is the Ruby environment for Fission.
|
||||
|
||||
It's a Docker image containing a Ruby 2.4.1 runtime, along with a
|
||||
dynamic loader. A few common dependencies are included in the
|
||||
Gemfile.
|
||||
It's a Docker image containing a Ruby 2.4.1 runtime. The image uses
|
||||
Rack with WEBrick to host the internal web server.
|
||||
|
||||
The environment works via convention where you create a Ruby method
|
||||
called `handler` with a single optional argument, a `Fission::Context`
|
||||
object.
|
||||
|
||||
The `Fission::Context` object gives access to the Rack env, a
|
||||
request object, and a logger. Please see `fission/context.rb` for the
|
||||
public api.
|
||||
|
||||
The `Fission::Request` object is a subclass of `Rack::Request` and
|
||||
provides access to parameters and headers. See `fission/request.rb`
|
||||
for the public api.
|
||||
|
||||
## Customizing this image
|
||||
|
||||
@@ -39,3 +50,7 @@ Or, if you already have an environment, you can update its image:
|
||||
|
||||
After this, fission functions that have the env parameter set to the
|
||||
same environment name as this command will use this environment.
|
||||
|
||||
## Creating functions to use this image
|
||||
|
||||
See the [examples README](examples/ruby/README.md).
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'request'
|
||||
require 'forwardable'
|
||||
|
||||
module Fission
|
||||
class Context
|
||||
extend Forwardable
|
||||
|
||||
def_instance_delegator :request, :logger
|
||||
|
||||
attr_reader :env
|
||||
|
||||
def initialize(env)
|
||||
@env = env
|
||||
end
|
||||
|
||||
def request
|
||||
@request ||= Request.new(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'context'
|
||||
|
||||
module Fission
|
||||
module Handler
|
||||
def self.call(env)
|
||||
context = Context.new(env)
|
||||
|
||||
response = if method(:handler).arity > 0
|
||||
handler(context)
|
||||
else
|
||||
handler
|
||||
end
|
||||
|
||||
response.is_a?(Array) ? response : Rack::Response.new([response]).finish
|
||||
|
||||
rescue => e
|
||||
context.logger.error(%(Function failed - #{e}\n#{e.backtrace.join("\n")}))
|
||||
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Fission
|
||||
HEADER_PREFIX = 'HTTP_'
|
||||
PARAM_HEADER_PREFIX = 'HTTP_X_FISSION_PARAMS_'
|
||||
PARAMETERS_KEY = 'fission.request.parameters'
|
||||
|
||||
class Request < Rack::Request
|
||||
def headers
|
||||
Hash[
|
||||
*env.select { |k,v| k.start_with?(HEADER_PREFIX) }
|
||||
.map { |k,v| [k.sub(/\A#{HEADER_PREFIX}/, '').split('_').map(&:capitalize).join('-'), v] }
|
||||
.sort
|
||||
.flatten
|
||||
]
|
||||
end
|
||||
|
||||
def params
|
||||
env[PARAMETERS_KEY] ||= super.merge(path_parameters)
|
||||
end
|
||||
|
||||
def path_parameters
|
||||
Hash[
|
||||
*env.select { |k,v| k.start_with?(PARAM_HEADER_PREFIX) }
|
||||
.map { |k,v| [k.sub(/\A#{PARAM_HEADER_PREFIX}/, '').downcase, v] }
|
||||
.sort
|
||||
.flatten
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
require 'benchmark'
|
||||
|
||||
module Fission
|
||||
CODE_PATH = '/userfunc/user'
|
||||
|
||||
module Specializer
|
||||
def self.call(env)
|
||||
request = Request.new(env)
|
||||
|
||||
request.logger.info("Codepath defaulting to #{CODE_PATH}")
|
||||
time = Benchmark.measure { load CODE_PATH }
|
||||
request.logger.info("User code loaded in #{(time.real * 1000).round(3)}ms")
|
||||
|
||||
Rack::Response.new([], 201).finish
|
||||
|
||||
rescue => e
|
||||
request.logger.error(%(Specialization failed - #{e}\n#{e.backtrace.join("\n")}))
|
||||
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,58 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rack'
|
||||
require 'logger'
|
||||
|
||||
CODEPATH = '/userfunc/user'
|
||||
|
||||
module Fission
|
||||
class Request < Rack::Request
|
||||
def headers
|
||||
Hash[
|
||||
*env.select { |k,v| k.start_with?('HTTP_') }
|
||||
.map { |k,v| [k.sub(/\AHTTP_/, '').split('_').map(&:capitalize).join('-'), v] }
|
||||
.sort
|
||||
.flatten
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
class Context
|
||||
attr_reader :env
|
||||
|
||||
def initialize(env)
|
||||
@env = env
|
||||
end
|
||||
|
||||
def request
|
||||
@request ||= Request.new(env)
|
||||
end
|
||||
end
|
||||
|
||||
module Specializer
|
||||
def self.call(env)
|
||||
load CODEPATH
|
||||
Rack::Response.new([], 201).finish
|
||||
rescue
|
||||
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
|
||||
end
|
||||
end
|
||||
|
||||
module Handler
|
||||
def self.call(env)
|
||||
response = if method(:handler).arity > 0
|
||||
handler(Context.new(env))
|
||||
else
|
||||
handler
|
||||
end
|
||||
|
||||
response.is_a?(Array) ? response : Rack::Response.new([response]).finish
|
||||
rescue
|
||||
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
|
||||
end
|
||||
end
|
||||
end
|
||||
require_relative 'fission/specializer'
|
||||
require_relative 'fission/handler'
|
||||
|
||||
app = Rack::Builder.new do
|
||||
use Rack::CommonLogger, $stderr
|
||||
use Rack::Logger, Logger::DEBUG
|
||||
|
||||
map "/specialize" do
|
||||
run Fission::Specializer
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# Binary Environment Examples
|
||||
|
||||
The `binary` runtime is a go server that uses a subprocess to invoke executables or execute shell scripts.
|
||||
|
||||
For more info read the [environment README](../../environments/binary/README.md).
|
||||
|
||||
## Requirements
|
||||
|
||||
First, set up your fission deployment with the binary environment.
|
||||
|
||||
```bash
|
||||
fission env create --name binary-env --image fission/binary-env
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
||||
### hello.sh
|
||||
`hello.sh` is an very basic shell script that returns `"Hello, World!"`.
|
||||
|
||||
```bash
|
||||
# Upload the function to fission
|
||||
fission function create --name hello --env binary-env --code hello.sh
|
||||
|
||||
# Map /hello to the hello function
|
||||
fission route create --method GET --url /hello --function hello
|
||||
|
||||
# Run the function
|
||||
curl http://$FISSION_ROUTER/hello
|
||||
```
|
||||
|
||||
This should return a HTTP response with the body `Hello World!`
|
||||
|
||||
### echo.sh
|
||||
`echo.sh` shows the the use of STDIN to read the request body, echoing the input back in the response.
|
||||
|
||||
```bash
|
||||
# Upload the function to fission
|
||||
fission function create --name echo --env binary-env --code echo.sh
|
||||
|
||||
# Map /hello to the hello function
|
||||
fission route create --method POST --url /echo --function echo
|
||||
|
||||
# Run the function
|
||||
curl -XPOST -d 'Echoooooo!' http://$FISSION_ROUTER/echo
|
||||
```
|
||||
This should return a HTTP response with the body `... Echoooooo!`.
|
||||
|
||||
|
||||
### headers.sh
|
||||
`headers.sh` shows the access to the environment variables that hold the HTTP headers, returning the set HTTP headers.
|
||||
|
||||
```bash
|
||||
# Upload the function to fission
|
||||
fission function create --name headers --env binary-env --code headers.sh
|
||||
|
||||
# Map /hello to the hello function
|
||||
fission route create --url /headers --function headers
|
||||
|
||||
# Run the function
|
||||
curl -H 'X-FOO: BAR' http://$FISSION_ROUTER/headers
|
||||
```
|
||||
This should return a HTTP response with the body `... Echoooooo!`.
|
||||
|
||||
### hello..go
|
||||
This example shows the differences between using shell scripts and binaries. `hello.go` returns `Hello World!` + the
|
||||
environment variables it received from the server.
|
||||
|
||||
```bash
|
||||
# Build the function targeted at the right architecture
|
||||
GOOS=linux GOARCH=386 go build -o hello-go-func hello.go
|
||||
|
||||
# Upload the function to fission
|
||||
fission function create --name hello-go --env binary-env --code hello-go-func
|
||||
|
||||
# Map /hello to the hello function
|
||||
fission route create --url /hello-go --function hello-go
|
||||
|
||||
# Run the function
|
||||
curl -H 'X-GO: AWESOME!' http://$FISSION_ROUTER/hello-go
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
printf "... "
|
||||
cat -
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
env | grep "^HTTP_"
|
||||
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// See README.md in the examples/binary directory for instructions
|
||||
func main() {
|
||||
fmt.Println("Hello World!")
|
||||
fmt.Printf("Environment: %v", os.Environ())
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Hello World!"
|
||||
@@ -29,7 +29,7 @@ $ chmod +x go-function-build
|
||||
$ go-function-build hello.go
|
||||
|
||||
# Upload the function to fission
|
||||
$ fission function create --name hello --env go-runtime --package function.so
|
||||
$ fission function create --name hello --env go-env --package function.so
|
||||
|
||||
# Map /hello to the hello function
|
||||
$ fission route create --method GET --url /hello --function hello
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# Ruby examples
|
||||
|
||||
This directory contains several examples to get you started using Ruby
|
||||
with Fission.
|
||||
|
||||
Before running any of these functions, make sure you have created a
|
||||
`ruby` Fission environment:
|
||||
|
||||
```
|
||||
$ fission env create --name ruby --image USER/ruby-env
|
||||
```
|
||||
|
||||
## Method signature
|
||||
|
||||
A standard Ruby function has the basic form:
|
||||
|
||||
```ruby
|
||||
def handler(context)
|
||||
return [200, {}, []]
|
||||
end
|
||||
```
|
||||
|
||||
If the fission context is not required, the function can be simplified:
|
||||
|
||||
```ruby
|
||||
def handler
|
||||
[200, {}, ["Hello, world!\n"]]
|
||||
end
|
||||
```
|
||||
|
||||
If a simple text response is to be returned, with a status of 200, this
|
||||
can be further simplified.
|
||||
|
||||
```ruby
|
||||
def handler
|
||||
"Hello, world!\n"
|
||||
end
|
||||
```
|
||||
|
||||
## Hello example (`hello.rb`)
|
||||
|
||||
This example is the simplest possible Ruby function, as described above.
|
||||
|
||||
To run the example:
|
||||
|
||||
```
|
||||
$ fission function create --name hello --env ruby --code examples/ruby/hello.rb
|
||||
|
||||
$ fission route create --method GET --url /hello --function hello
|
||||
|
||||
$ curl http://$FISSION_ROUTER/hello
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
## Request data example (`request_data.rb`)
|
||||
|
||||
This example shows basic use of the `Fission::Context` and
|
||||
`Fission::Request` objects.
|
||||
|
||||
To run the example:
|
||||
|
||||
```
|
||||
$ fission function create --name request --env ruby --code examples/ruby/request_data.rb
|
||||
|
||||
$ fission route create --method GET --url /request/{id} --function request
|
||||
|
||||
$ curl http://$FISSION_ROUTER/request/123?key=abc
|
||||
---ENV---
|
||||
GATEWAY_INTERFACE=CGI/1.1
|
||||
PATH_INFO=/
|
||||
QUERY_STRING=key=abc
|
||||
REMOTE_ADDR=172.17.0.8
|
||||
REMOTE_HOST=172.17.0.8
|
||||
REQUEST_METHOD=GET
|
||||
REQUEST_URI=http://192.168.64.200:31314/?key=abc
|
||||
SCRIPT_NAME=
|
||||
SERVER_NAME=192.168.64.200
|
||||
SERVER_PORT=31314
|
||||
SERVER_PROTOCOL=HTTP/1.1
|
||||
SERVER_SOFTWARE=WEBrick/1.3.1 (Ruby/2.4.1/2017-03-22)
|
||||
HTTP_HOST=192.168.64.200:31314
|
||||
HTTP_USER_AGENT=curl/7.52.1
|
||||
HTTP_ACCEPT=*/*
|
||||
HTTP_X_FISSION_PARAMS_ID=123
|
||||
HTTP_X_FORWARDED_FOR=172.17.0.1
|
||||
HTTP_ACCEPT_ENCODING=gzip
|
||||
rack.version=1=3
|
||||
...
|
||||
HTTP_VERSION=HTTP/1.1
|
||||
REQUEST_PATH=/
|
||||
|
||||
---HEADERS---
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
Host: 192.168.64.200:31314
|
||||
User-Agent: curl/7.52.1
|
||||
Version: HTTP/1.1
|
||||
X-Fission-Params-Id: 123
|
||||
X-Forwarded-For: 172.17.0.1
|
||||
|
||||
---PARAMS---
|
||||
key=abc
|
||||
id=123
|
||||
|
||||
--BODY--
|
||||
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user