Compare commits
38
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 |
@@ -10,3 +10,4 @@ environments/php7/vendor/
|
|||||||
*~
|
*~
|
||||||
|
|
||||||
vendor/
|
vendor/
|
||||||
|
local/
|
||||||
+25
-11
@@ -1,20 +1,34 @@
|
|||||||
sudo: required
|
sudo: required
|
||||||
|
|
||||||
dist: trusty
|
dist: trusty
|
||||||
|
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.8
|
- 1.8
|
||||||
- tip
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/google-cloud-sdk/
|
||||||
|
- $HOME/k8scli
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- go get github.com/Masterminds/glide
|
- 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
|
- hack/travis-kube-setup.sh
|
||||||
|
|
||||||
|
env:
|
||||||
|
- KUBECONFIG=${HOME}/.kube/config PATH=$HOME/k8scli:${PATH}
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cd ${TRAVIS_BUILD_DIR}
|
- cd ${TRAVIS_BUILD_DIR}
|
||||||
- glide install
|
- glide install
|
||||||
- ./fission-bundle/build.sh
|
- ./fission-bundle/build.sh
|
||||||
- hack/verify-gofmt.sh
|
- hack/verify-gofmt.sh
|
||||||
- /tmp/test-etcd/etcd &
|
- hack/runtests.sh
|
||||||
- go test -v -i $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go')
|
- test/build_and_test.sh
|
||||||
- go test -v $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go')
|
|
||||||
|
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
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
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
|
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
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
|||||||
+13
-13
@@ -49,8 +49,8 @@ set up services with NodePort. This exposes fission on ports 31313
|
|||||||
and 31314.
|
and 31314.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170621/fission-rbac.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/nightly20170621/fission-nodeport.yaml
|
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-nodeport.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Set the FISSION_URL and FISSION_ROUTER environment variables.
|
Set the FISSION_URL and FISSION_ROUTER environment variables.
|
||||||
@@ -72,8 +72,8 @@ If you're using GKE or any other cloud provider that supports the
|
|||||||
LoadBalancer service type, use these commands:
|
LoadBalancer service type, use these commands:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ kubectl create -f https://github.com/fission/fission/releases/download/nightly20170621/fission-rbac.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/nightly20170621/fission-cloud.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
|
Save the external IP addresses of controller and router services in
|
||||||
@@ -106,8 +106,8 @@ If you're using minishift or no cloud provider, use these commands to set up ser
|
|||||||
|
|
||||||
```
|
```
|
||||||
$ oc login -u system:admin
|
$ oc login -u system:admin
|
||||||
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170621/fission-openshift.yaml
|
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-openshift.yaml
|
||||||
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170621/fission-nodeport.yaml
|
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-nodeport.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using other clouds
|
#### Using other clouds
|
||||||
@@ -116,8 +116,8 @@ If you're using any cloud provider that supports the LoadBalancer service type,
|
|||||||
|
|
||||||
```
|
```
|
||||||
$ oc login -u system:admin
|
$ oc login -u system:admin
|
||||||
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170621/fission-openshift.yaml
|
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170705/fission-openshift.yaml
|
||||||
$ oc create -f https://github.com/fission/fission/releases/download/nightly20170621/fission-cloud.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.
|
After these steps, you should be able to run fission client as with kubernetes.
|
||||||
|
|
||||||
@@ -128,19 +128,19 @@ After these steps, you should be able to run fission client as with kubernetes.
|
|||||||
Get the CLI binary for Mac:
|
Get the CLI binary for Mac:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/nightly20170621/fission-cli-osx && 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
|
#### Linux
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/nightly20170621/fission-cli-linux && 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
|
#### Windows
|
||||||
|
|
||||||
For Windows, you can use the linux binary on WSL. Or you can download
|
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/nightly20170621/fission-cli-windows.exe)
|
this windows executable: [fission.exe](https://github.com/fission/fission/releases/download/nightly20170705/fission-cli-windows.exe)
|
||||||
|
|
||||||
### Run an example
|
### Run an example
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ Fission uses InfluxDB to store logs and fluentd to forward them from
|
|||||||
function pods into InfluxDB.
|
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:
|
That's it for the basic setup. You can now use following command to view function logs:
|
||||||
@@ -210,7 +210,7 @@ only supported message queue.
|
|||||||
You can install NATS Streaming on your Kubernetes cluster with:
|
You can install NATS Streaming on your Kubernetes cluster with:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ kubectl create -f fission-nats.yaml
|
$ 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:
|
You can subscribe to a NATS Streaming queue with a command like this:
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ extend Fission if you want to.
|
|||||||
|
|
||||||
Fission is extensible to any language; the core is written in Go, and
|
Fission is extensible to any language; the core is written in Go, and
|
||||||
language-specific parts are isolated in something called
|
language-specific parts are isolated in something called
|
||||||
_environments_ (more below). Fission currently supports NodeJS and
|
_environments_ (more below). Fission currently supports NodeJS, Python, Ruby, Go,
|
||||||
Python, with more languages coming soon.
|
PHP, Bash, and any Linux executable, with more languages coming soon.
|
||||||
|
|
||||||
### Performance: 100msec cold start
|
### Performance: 100msec cold start
|
||||||
|
|
||||||
@@ -94,9 +94,8 @@ Status
|
|||||||
Fission is in early alpha. It's not suitable for production use just
|
Fission is in early alpha. It's not suitable for production use just
|
||||||
yet.
|
yet.
|
||||||
|
|
||||||
We're looking for early developer feedback -- if you do use Fission,
|
Reach us on [slack](http://slack.fission.io) or
|
||||||
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
|
|
||||||
[twitter](https://twitter.com/fissionio).
|
[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,28 +14,29 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package fission
|
package main
|
||||||
|
|
||||||
func (f Function) Key() string {
|
import (
|
||||||
return f.Metadata.Name
|
"log"
|
||||||
}
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
func (e Environment) Key() string {
|
builder "github.com/fission/fission/builder"
|
||||||
return e.Metadata.Name
|
)
|
||||||
}
|
|
||||||
|
|
||||||
func (ht HTTPTrigger) Key() string {
|
// Usage: builder <shared volume path>
|
||||||
return ht.Metadata.Name
|
func main() {
|
||||||
}
|
dir := os.Args[1]
|
||||||
|
if _, err := os.Stat(dir); err != nil {
|
||||||
func (mqt MessageQueueTrigger) Key() string {
|
if os.IsNotExist(err) {
|
||||||
return mqt.Metadata.Name
|
err = os.MkdirAll(dir, os.ModeDir|0700)
|
||||||
}
|
if err != nil {
|
||||||
|
log.Fatalf("Error creating directory: %v", err)
|
||||||
func (tt TimeTrigger) Key() string {
|
}
|
||||||
return tt.Metadata.Name
|
}
|
||||||
}
|
}
|
||||||
|
builder := builder.MakeBuilder(dir)
|
||||||
func (w Watch) Key() string {
|
mux := http.NewServeMux()
|
||||||
return w.Metadata.Name
|
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.
|
1. Install the client CLI.
|
||||||
|
|
||||||
Mac:
|
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:
|
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.
|
2. Set the FISSION_URL and FISSION_ROUTER environment variables.
|
||||||
FISSION_URL is used by the fission CLI to find the server.
|
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
|
targetPort: 8888
|
||||||
nodePort: {{ .Values.controllerPort }}
|
nodePort: {{ .Values.controllerPort }}
|
||||||
selector:
|
selector:
|
||||||
svc: controller
|
svc: controller
|
||||||
@@ -6,11 +6,20 @@
|
|||||||
## For minikube, set this to NodePort, elsewhere use LoadBalancer.
|
## For minikube, set this to NodePort, elsewhere use LoadBalancer.
|
||||||
serviceType: LoadBalancer
|
serviceType: LoadBalancer
|
||||||
|
|
||||||
## Fission image repostiroy
|
## Fission image repository
|
||||||
image: fission/fission-bundle
|
image: fission/fission-bundle
|
||||||
|
|
||||||
## Fission image version
|
## 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
|
## Port at which Fission controller service should be exposed
|
||||||
controllerPort: 31313
|
controllerPort: 31313
|
||||||
@@ -21,3 +30,6 @@ routerPort: 31314
|
|||||||
## Namespace in which to run fission functions (this is different from
|
## Namespace in which to run fission functions (this is different from
|
||||||
## the release namespace)
|
## the release namespace)
|
||||||
functionNamespace: fission-function
|
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"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UrlForFunction(m *Metadata) string {
|
func UrlForFunction(name string) string {
|
||||||
prefix := "/fission-function"
|
prefix := "/fission-function"
|
||||||
if len(m.Uid) > 0 {
|
return fmt.Sprintf("%v/%v", prefix, name)
|
||||||
return fmt.Sprintf("%v/%v/%v", prefix, m.Name, m.Uid)
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("%v/%v", prefix, m.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-45
@@ -26,19 +26,17 @@ import (
|
|||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
kerrors "k8s.io/client-go/1.5/pkg/api/errors"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/fission/logdb"
|
"github.com/fission/fission/fission/logdb"
|
||||||
|
"github.com/fission/fission/tpr"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
API struct {
|
API struct {
|
||||||
FunctionStore
|
fissionClient *tpr.FissionClient
|
||||||
HTTPTriggerStore
|
storageServiceUrl string
|
||||||
TimeTriggerStore
|
|
||||||
MessageQueueTriggerStore
|
|
||||||
EnvironmentStore
|
|
||||||
WatchStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logDBConfig struct {
|
logDBConfig struct {
|
||||||
@@ -48,16 +46,17 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeAPI(rs *ResourceStore) *API {
|
func MakeAPI() (*API, error) {
|
||||||
api := &API{
|
api, err := makeTPRBackedAPI()
|
||||||
FunctionStore: FunctionStore{ResourceStore: *rs},
|
|
||||||
HTTPTriggerStore: HTTPTriggerStore{ResourceStore: *rs},
|
u := os.Getenv("STORAGE_SERVICE_URL")
|
||||||
TimeTriggerStore: TimeTriggerStore{ResourceStore: *rs},
|
if len(u) > 0 {
|
||||||
MessageQueueTriggerStore: MessageQueueTriggerStore{ResourceStore: *rs},
|
api.storageServiceUrl = strings.TrimSuffix(u, "/")
|
||||||
EnvironmentStore: EnvironmentStore{ResourceStore: *rs},
|
} else {
|
||||||
WatchStore: WatchStore{ResourceStore: *rs},
|
api.storageServiceUrl = "http://storagesvc"
|
||||||
}
|
}
|
||||||
return api
|
|
||||||
|
return api, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) respondWithSuccess(w http.ResponseWriter, resp []byte) {
|
func (api *API) respondWithSuccess(w http.ResponseWriter, resp []byte) {
|
||||||
@@ -71,6 +70,14 @@ func (api *API) respondWithSuccess(w http.ResponseWriter, resp []byte) {
|
|||||||
|
|
||||||
func (api *API) respondWithError(w http.ResponseWriter, err error) {
|
func (api *API) respondWithError(w http.ResponseWriter, err error) {
|
||||||
debug.PrintStack()
|
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)
|
code, msg := fission.GetHTTPError(err)
|
||||||
log.Errorf("Error: %v: %v", code, msg)
|
log.Errorf("Error: %v: %v", code, msg)
|
||||||
http.Error(w, msg, code)
|
http.Error(w, msg, code)
|
||||||
@@ -98,47 +105,61 @@ func (api *API) HomeHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
fmt.Fprintf(w, "{\"message\": \"Fission API\", \"version\": \"0.1.0\"}\n")
|
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) {
|
func (api *API) Serve(port int) {
|
||||||
r := mux.NewRouter()
|
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("/", api.HomeHandler)
|
||||||
|
|
||||||
r.HandleFunc("/v1/functions", api.FunctionApiList).Methods("GET")
|
r.HandleFunc("/v2/packages", api.PackageApiList).Methods("GET")
|
||||||
r.HandleFunc("/v1/functions", api.FunctionApiCreate).Methods("POST")
|
r.HandleFunc("/v2/packages", api.PackageApiCreate).Methods("POST")
|
||||||
r.HandleFunc("/v1/functions/{function}", api.FunctionApiGet).Methods("GET")
|
r.HandleFunc("/v2/packages/{package}", api.PackageApiGet).Methods("GET")
|
||||||
r.HandleFunc("/v1/functions/{function}", api.FunctionApiUpdate).Methods("PUT")
|
r.HandleFunc("/v2/packages/{package}", api.PackageApiUpdate).Methods("PUT")
|
||||||
r.HandleFunc("/v1/functions/{function}", api.FunctionApiDelete).Methods("DELETE")
|
r.HandleFunc("/v2/packages/{package}", api.PackageApiDelete).Methods("DELETE")
|
||||||
|
|
||||||
r.HandleFunc("/v1/triggers/http", api.HTTPTriggerApiList).Methods("GET")
|
r.HandleFunc("/v2/functions", api.FunctionApiList).Methods("GET")
|
||||||
r.HandleFunc("/v1/triggers/http", api.HTTPTriggerApiCreate).Methods("POST")
|
r.HandleFunc("/v2/functions", api.FunctionApiCreate).Methods("POST")
|
||||||
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiGet).Methods("GET")
|
r.HandleFunc("/v2/functions/{function}", api.FunctionApiGet).Methods("GET")
|
||||||
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiUpdate).Methods("PUT")
|
r.HandleFunc("/v2/functions/{function}", api.FunctionApiUpdate).Methods("PUT")
|
||||||
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiDelete).Methods("DELETE")
|
r.HandleFunc("/v2/functions/{function}", api.FunctionApiDelete).Methods("DELETE")
|
||||||
|
|
||||||
r.HandleFunc("/v1/environments", api.EnvironmentApiList).Methods("GET")
|
r.HandleFunc("/v2/triggers/http", api.HTTPTriggerApiList).Methods("GET")
|
||||||
r.HandleFunc("/v1/environments", api.EnvironmentApiCreate).Methods("POST")
|
r.HandleFunc("/v2/triggers/http", api.HTTPTriggerApiCreate).Methods("POST")
|
||||||
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiGet).Methods("GET")
|
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiGet).Methods("GET")
|
||||||
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT")
|
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiUpdate).Methods("PUT")
|
||||||
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE")
|
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiDelete).Methods("DELETE")
|
||||||
|
|
||||||
r.HandleFunc("/v1/watches", api.WatchApiList).Methods("GET")
|
r.HandleFunc("/v2/environments", api.EnvironmentApiList).Methods("GET")
|
||||||
r.HandleFunc("/v1/watches", api.WatchApiCreate).Methods("POST")
|
r.HandleFunc("/v2/environments", api.EnvironmentApiCreate).Methods("POST")
|
||||||
r.HandleFunc("/v1/watches/{watch}", api.WatchApiGet).Methods("GET")
|
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiGet).Methods("GET")
|
||||||
r.HandleFunc("/v1/watches/{watch}", api.WatchApiUpdate).Methods("PUT")
|
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT")
|
||||||
r.HandleFunc("/v1/watches/{watch}", api.WatchApiDelete).Methods("DELETE")
|
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE")
|
||||||
|
|
||||||
r.HandleFunc("/v1/triggers/time", api.TimeTriggerApiList).Methods("GET")
|
r.HandleFunc("/v2/watches", api.WatchApiList).Methods("GET")
|
||||||
r.HandleFunc("/v1/triggers/time", api.TimeTriggerApiCreate).Methods("POST")
|
r.HandleFunc("/v2/watches", api.WatchApiCreate).Methods("POST")
|
||||||
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiGet).Methods("GET")
|
r.HandleFunc("/v2/watches/{watch}", api.WatchApiGet).Methods("GET")
|
||||||
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiUpdate).Methods("PUT")
|
r.HandleFunc("/v2/watches/{watch}", api.WatchApiUpdate).Methods("PUT")
|
||||||
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiDelete).Methods("DELETE")
|
r.HandleFunc("/v2/watches/{watch}", api.WatchApiDelete).Methods("DELETE")
|
||||||
|
|
||||||
r.HandleFunc("/v1/triggers/messagequeue", api.MessageQueueTriggerApiList).Methods("GET")
|
r.HandleFunc("/v2/triggers/time", api.TimeTriggerApiList).Methods("GET")
|
||||||
r.HandleFunc("/v1/triggers/messagequeue", api.MessageQueueApiCreate).Methods("POST")
|
r.HandleFunc("/v2/triggers/time", api.TimeTriggerApiCreate).Methods("POST")
|
||||||
r.HandleFunc("/v1/triggers/messagequeue/{mqTrigger}", api.MessageQueueApiGet).Methods("GET")
|
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiGet).Methods("GET")
|
||||||
r.HandleFunc("/v1/triggers/messagequeue/{mqTrigger}", api.MessageQueueApiUpdate).Methods("PUT")
|
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiUpdate).Methods("PUT")
|
||||||
r.HandleFunc("/v1/triggers/messagequeue/{mqTrigger}", api.MessageQueueApiDelete).Methods("DELETE")
|
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/{dbType}", api.FunctionLogsApiPost).Methods("POST")
|
||||||
|
r.HandleFunc("/proxy/storage/v1/archive", api.StorageServiceProxy)
|
||||||
|
|
||||||
address := fmt.Sprintf(":%v", port)
|
address := fmt.Sprintf(":%v", port)
|
||||||
|
|
||||||
|
|||||||
+137
-215
@@ -19,35 +19,49 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
etcdClient "github.com/coreos/etcd/client"
|
"k8s.io/client-go/1.5/pkg/api"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/controller/client"
|
"github.com/fission/fission/controller/client"
|
||||||
|
"github.com/fission/fission/tpr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var g struct {
|
var g struct {
|
||||||
client *client.Client
|
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")
|
assert(err != nil, "recreating "+name+" with same name must fail")
|
||||||
fe, ok := err.(fission.Error)
|
fe, ok := err.(fission.Error)
|
||||||
assert(ok, "error must be a fission Error")
|
assert(ok, "error must be a fission Error")
|
||||||
assert(fe.Code == fission.ErrorNameExists, "error must be a name exists 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")
|
assert(err != nil, "requesting a non-existent "+name+" must fail")
|
||||||
fe, ok := err.(fission.Error)
|
fe, ok := err.(fission.Error)
|
||||||
assert(ok, "error must be a 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) {
|
func assertCronSpecFails(err error) {
|
||||||
@@ -58,68 +72,39 @@ func assertCronSpecFails(err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFunctionApi(t *testing.T) {
|
func TestFunctionApi(t *testing.T) {
|
||||||
log.SetFormatter(&log.TextFormatter{DisableColors: true})
|
name1 := "foo"
|
||||||
|
name2 := "bar"
|
||||||
|
|
||||||
testFunc := &fission.Function{
|
testFunc := &tpr.Function{
|
||||||
Metadata: fission.Metadata{
|
Metadata: api.ObjectMeta{
|
||||||
Name: "foo",
|
Name: name1,
|
||||||
Uid: "",
|
Namespace: api.NamespaceDefault,
|
||||||
},
|
},
|
||||||
Environment: fission.Metadata{
|
Spec: fission.FunctionSpec{
|
||||||
Name: "nodejs",
|
EnvironmentName: "nodejs",
|
||||||
Uid: "xxx",
|
Deployment: fission.FunctionPackageRef{
|
||||||
|
FunctionName: "xxx",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Code: "code1",
|
|
||||||
}
|
}
|
||||||
_, err := g.client.FunctionGet(&fission.Metadata{Name: "foo"})
|
_, err := g.client.FunctionGet(&api.ObjectMeta{
|
||||||
assertNotFoundFails(err, "function")
|
Name: testFunc.Metadata.Name,
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
m, err := g.client.FunctionCreate(testFunc)
|
})
|
||||||
panicIf(err)
|
assertNotFoundFailure(err, "function")
|
||||||
uid1 := m.Uid
|
|
||||||
//log.Printf("Created function %v: %v", m.Name, m.Uid)
|
|
||||||
|
|
||||||
_, err = g.client.FunctionCreate(testFunc)
|
_, 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)
|
panicIf(err)
|
||||||
|
|
||||||
testFunc.Metadata.Uid = m.Uid
|
_, err = g.client.FunctionCreate(testFunc)
|
||||||
//log.Printf("f = %#v", f)
|
assertNameReuseFailure(err, "function")
|
||||||
//log.Printf("testFunc = %#v", testFunc)
|
|
||||||
assert(*f == *testFunc, "first version should match when read by uid")
|
|
||||||
|
|
||||||
m.Uid = uid2
|
testFunc.Spec.Deployment.FunctionName = "yyy"
|
||||||
testFunc.Metadata.Uid = m.Uid
|
_, err = g.client.FunctionUpdate(testFunc)
|
||||||
testFunc.Code = "code2"
|
|
||||||
f, err = g.client.FunctionGet(m)
|
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
|
|
||||||
assert(*f == *testFunc, "second version should match when read by uid")
|
testFunc.Metadata.Name = name2
|
||||||
|
_, err = g.client.FunctionCreate(testFunc)
|
||||||
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)
|
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
|
|
||||||
funcs, err := g.client.FunctionList()
|
funcs, err := g.client.FunctionList()
|
||||||
@@ -127,7 +112,7 @@ func TestFunctionApi(t *testing.T) {
|
|||||||
assert(len(funcs) == 2,
|
assert(len(funcs) == 2,
|
||||||
"created two functions, but didn't find them")
|
"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)
|
resp, err := http.Get(funcs_url)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
@@ -141,112 +126,55 @@ func TestFunctionApi(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert(found, "incorrect response content type")
|
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)
|
panicIf(err)
|
||||||
err = g.client.FunctionDelete(&fission.Metadata{Name: "bar"})
|
err = g.client.FunctionDelete(&api.ObjectMeta{Name: name2, Namespace: api.NamespaceDefault})
|
||||||
panicIf(err)
|
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) {
|
func TestHTTPTriggerApi(t *testing.T) {
|
||||||
testTrigger := &fission.HTTPTrigger{
|
testTrigger := &tpr.Httptrigger{
|
||||||
Metadata: fission.Metadata{
|
Metadata: api.ObjectMeta{
|
||||||
Name: "xxx",
|
Name: "foo",
|
||||||
Uid: "yyy",
|
Namespace: api.NamespaceDefault,
|
||||||
},
|
},
|
||||||
UrlPattern: "/hello",
|
Spec: fission.HTTPTriggerSpec{
|
||||||
Function: fission.Metadata{
|
RelativeURL: "/hello",
|
||||||
Name: "foo",
|
FunctionReference: fission.FunctionReference{
|
||||||
Uid: "",
|
Type: fission.FunctionReferenceTypeFunctionName,
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err := g.client.HTTPTriggerGet(&fission.Metadata{Name: "foo"})
|
_, err := g.client.HTTPTriggerGet(&api.ObjectMeta{
|
||||||
assertNotFoundFails(err, "trigger")
|
Name: testTrigger.Metadata.Name,
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
})
|
||||||
|
assertNotFoundFailure(err, "httptrigger")
|
||||||
|
|
||||||
m, err := g.client.HTTPTriggerCreate(testTrigger)
|
m, err := g.client.HTTPTriggerCreate(testTrigger)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.HTTPTriggerDelete(m)
|
defer g.client.HTTPTriggerDelete(m)
|
||||||
|
|
||||||
_, err = g.client.HTTPTriggerCreate(testTrigger)
|
_, err = g.client.HTTPTriggerCreate(testTrigger)
|
||||||
assertNameReuseFails(err, "trigger")
|
assertNameReuseFailure(err, "httptrigger")
|
||||||
|
|
||||||
tr, err := g.client.HTTPTriggerGet(m)
|
tr, err := g.client.HTTPTriggerGet(m)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
testTrigger.Metadata.Uid = m.Uid
|
assert(testTrigger.Spec == tr.Spec, "trigger should match after reading")
|
||||||
assert(*testTrigger == *tr, "trigger should match after reading")
|
|
||||||
|
|
||||||
testTrigger.UrlPattern = "/hi"
|
testTrigger.Spec.RelativeURL = "/hi"
|
||||||
m2, err := g.client.HTTPTriggerUpdate(testTrigger)
|
_, err = g.client.HTTPTriggerUpdate(testTrigger)
|
||||||
panicIf(err)
|
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"
|
testTrigger.Metadata.Name = "yyy"
|
||||||
m, err = g.client.HTTPTriggerCreate(testTrigger)
|
_, err = g.client.HTTPTriggerCreate(testTrigger)
|
||||||
assert(err != nil, "duplicate trigger should not be allowed")
|
assert(err != nil, "duplicate trigger should not be allowed")
|
||||||
|
|
||||||
testTrigger.UrlPattern = "/hi2"
|
testTrigger.Spec.RelativeURL = "/hi2"
|
||||||
m, err = g.client.HTTPTriggerCreate(testTrigger)
|
m2, err := g.client.HTTPTriggerCreate(testTrigger)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.HTTPTriggerDelete(m)
|
defer g.client.HTTPTriggerDelete(m2)
|
||||||
|
|
||||||
ts, err := g.client.HTTPTriggerList()
|
ts, err := g.client.HTTPTriggerList()
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
@@ -254,42 +182,42 @@ func TestHTTPTriggerApi(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEnvironmentApi(t *testing.T) {
|
func TestEnvironmentApi(t *testing.T) {
|
||||||
testEnv := &fission.Environment{
|
testEnv := &tpr.Environment{
|
||||||
Metadata: fission.Metadata{
|
Metadata: api.ObjectMeta{
|
||||||
Name: "xxx",
|
Name: "foo",
|
||||||
Uid: "yyy",
|
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"})
|
_, err := g.client.EnvironmentGet(&api.ObjectMeta{
|
||||||
assertNotFoundFails(err, "environment")
|
Name: testEnv.Metadata.Name,
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
})
|
||||||
|
assertNotFoundFailure(err, "environment")
|
||||||
|
|
||||||
m, err := g.client.EnvironmentCreate(testEnv)
|
m, err := g.client.EnvironmentCreate(testEnv)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.EnvironmentDelete(m)
|
defer g.client.EnvironmentDelete(m)
|
||||||
|
|
||||||
_, err = g.client.EnvironmentCreate(testEnv)
|
_, 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)
|
panicIf(err)
|
||||||
testEnv.Metadata.Uid = m.Uid
|
assert(testEnv.Spec == e.Spec, "env should match after reading")
|
||||||
assert(*testEnv == *tr, "env should match after reading")
|
|
||||||
|
|
||||||
testEnv.RunContainerImageUrl = "/hi"
|
testEnv.Spec.Runtime.Image = "another-img"
|
||||||
m2, err := g.client.EnvironmentUpdate(testEnv)
|
_, err = g.client.EnvironmentUpdate(testEnv)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
|
|
||||||
m.Uid = m2.Uid
|
testEnv.Metadata.Name = "bar"
|
||||||
tr, err = g.client.EnvironmentGet(m)
|
m2, err := g.client.EnvironmentCreate(testEnv)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
testEnv.Metadata.Uid = m.Uid
|
defer g.client.EnvironmentDelete(m2)
|
||||||
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)
|
|
||||||
|
|
||||||
ts, err := g.client.EnvironmentList()
|
ts, err := g.client.EnvironmentList()
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
@@ -297,36 +225,38 @@ func TestEnvironmentApi(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchApi(t *testing.T) {
|
func TestWatchApi(t *testing.T) {
|
||||||
testWatch := &fission.Watch{
|
testWatch := &tpr.Kuberneteswatchtrigger{
|
||||||
Metadata: fission.Metadata{
|
Metadata: api.ObjectMeta{
|
||||||
Name: "xxx",
|
Name: "xxx",
|
||||||
Uid: "yyy",
|
Namespace: api.NamespaceDefault,
|
||||||
},
|
},
|
||||||
Namespace: "default",
|
Spec: fission.KubernetesWatchTriggerSpec{
|
||||||
ObjType: "pod",
|
Namespace: "default",
|
||||||
LabelSelector: "",
|
Type: "pod",
|
||||||
FieldSelector: "",
|
FunctionReference: fission.FunctionReference{
|
||||||
Function: fission.Metadata{
|
Type: fission.FunctionReferenceTypeFunctionName,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Uid: "",
|
},
|
||||||
},
|
},
|
||||||
Target: "",
|
|
||||||
}
|
}
|
||||||
_, err := g.client.WatchGet(&fission.Metadata{Name: "foo"})
|
_, err := g.client.WatchGet(&api.ObjectMeta{
|
||||||
assertNotFoundFails(err, "watch")
|
Name: testWatch.Metadata.Name,
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
})
|
||||||
|
assertNotFoundFailure(err, "watch")
|
||||||
|
|
||||||
m, err := g.client.WatchCreate(testWatch)
|
m, err := g.client.WatchCreate(testWatch)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.WatchDelete(m)
|
defer g.client.WatchDelete(m)
|
||||||
|
|
||||||
_, err = g.client.WatchCreate(testWatch)
|
_, err = g.client.WatchCreate(testWatch)
|
||||||
assertNameReuseFails(err, "watch")
|
assertNameReuseFailure(err, "watch")
|
||||||
|
|
||||||
w, err := g.client.WatchGet(m)
|
w, err := g.client.WatchGet(m)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
testWatch.Metadata.Uid = m.Uid
|
assert((testWatch.Spec.Namespace == w.Spec.Namespace &&
|
||||||
w.Target = ""
|
testWatch.Spec.Type == w.Spec.Type &&
|
||||||
assert(*testWatch == *w, "watch should match after reading")
|
testWatch.Spec.FunctionReference == w.Spec.FunctionReference), "watch should match after reading")
|
||||||
|
|
||||||
testWatch.Metadata.Name = "yyy"
|
testWatch.Metadata.Name = "yyy"
|
||||||
m2, err := g.client.WatchCreate(testWatch)
|
m2, err := g.client.WatchCreate(testWatch)
|
||||||
@@ -339,45 +269,40 @@ func TestWatchApi(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeTriggerApi(t *testing.T) {
|
func TestTimeTriggerApi(t *testing.T) {
|
||||||
testTrigger := &fission.TimeTrigger{
|
testTrigger := &tpr.Timetrigger{
|
||||||
Metadata: fission.Metadata{
|
Metadata: api.ObjectMeta{
|
||||||
Name: "xxx",
|
Name: "xxx",
|
||||||
Uid: "yyy",
|
Namespace: api.NamespaceDefault,
|
||||||
},
|
},
|
||||||
Cron: "0 30 * * * *",
|
Spec: fission.TimeTriggerSpec{
|
||||||
Function: fission.Metadata{
|
Cron: "0 30 * * * *",
|
||||||
Name: "foo",
|
FunctionReference: fission.FunctionReference{
|
||||||
Uid: "",
|
Type: fission.FunctionReferenceTypeFunctionName,
|
||||||
|
Name: "asdf",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err := g.client.TimeTriggerGet(&fission.Metadata{Name: "foo"})
|
_, err := g.client.TimeTriggerGet(&api.ObjectMeta{Name: testTrigger.Metadata.Name})
|
||||||
assertNotFoundFails(err, "trigger")
|
assertNotFoundFailure(err, "trigger")
|
||||||
|
|
||||||
m, err := g.client.TimeTriggerCreate(testTrigger)
|
m, err := g.client.TimeTriggerCreate(testTrigger)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.TimeTriggerDelete(m)
|
defer g.client.TimeTriggerDelete(m)
|
||||||
|
|
||||||
_, err = g.client.TimeTriggerCreate(testTrigger)
|
_, err = g.client.TimeTriggerCreate(testTrigger)
|
||||||
assertNameReuseFails(err, "trigger")
|
assertNameReuseFailure(err, "trigger")
|
||||||
|
|
||||||
tr, err := g.client.TimeTriggerGet(m)
|
tr, err := g.client.TimeTriggerGet(m)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
testTrigger.Metadata.Uid = m.Uid
|
assert(testTrigger.Spec == tr.Spec, "trigger should match after reading")
|
||||||
assert(*testTrigger == *tr, "trigger should match after reading")
|
|
||||||
|
|
||||||
testTrigger.Cron = "@hourly"
|
testTrigger.Spec.Cron = "@hourly"
|
||||||
m2, err := g.client.TimeTriggerUpdate(testTrigger)
|
_, err = g.client.TimeTriggerUpdate(testTrigger)
|
||||||
panicIf(err)
|
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.Metadata.Name = "yyy"
|
||||||
testTrigger.Cron = "Not valid cron spec"
|
testTrigger.Spec.Cron = "Not valid cron spec"
|
||||||
m, err = g.client.TimeTriggerCreate(testTrigger)
|
_, err = g.client.TimeTriggerCreate(testTrigger)
|
||||||
assertCronSpecFails(err)
|
assertCronSpecFails(err)
|
||||||
|
|
||||||
ts, err := g.client.TimeTriggerList()
|
ts, err := g.client.TimeTriggerList()
|
||||||
@@ -388,21 +313,18 @@ func TestTimeTriggerApi(t *testing.T) {
|
|||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
fileStore, ks, rs := getTestResourceStore()
|
// skip test if no cluster available for testing
|
||||||
defer os.RemoveAll(fileStore.root)
|
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")
|
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/")
|
resp, err := http.Get("http://localhost:8888/")
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
assert(resp.StatusCode == 200, "http get status code on root")
|
assert(resp.StatusCode == 200, "http get status code on root")
|
||||||
|
|||||||
+1
-666
@@ -18,10 +18,7 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -73,7 +70,7 @@ func (c *Client) put(relativeUrl string, contentType string, body []byte) (*http
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) url(relativeUrl string) string {
|
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) {
|
func (c *Client) handleResponse(resp *http.Response) ([]byte, error) {
|
||||||
@@ -91,665 +88,3 @@ func (c *Client) handleCreateResponse(resp *http.Response) ([]byte, error) {
|
|||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
return body, err
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) MessageQueueTriggerCreate(t *fission.MessageQueueTrigger) (*fission.Metadata, 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 fission.Metadata
|
|
||||||
err = json.Unmarshal(body, &m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) MessageQueueTriggerGet(m *fission.Metadata) (*fission.MessageQueueTrigger, error) {
|
|
||||||
relativeUrl := fmt.Sprintf("triggers/messagequeue/%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.MessageQueueTrigger
|
|
||||||
err = json.Unmarshal(body, &t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) MessageQueueTriggerUpdate(mqTrigger *fission.MessageQueueTrigger) (*fission.Metadata, 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 fission.Metadata
|
|
||||||
err = json.Unmarshal(body, &m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) MessageQueueTriggerDelete(m *fission.Metadata) error {
|
|
||||||
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
|
|
||||||
if len(m.Uid) > 0 {
|
|
||||||
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
|
|
||||||
}
|
|
||||||
err := c.delete(relativeUrl)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) MessageQueueTriggerList(mqType string) ([]fission.MessageQueueTrigger, error) {
|
|
||||||
relativeUrl := "triggers/messagequeue"
|
|
||||||
if len(mqType) > 0 {
|
|
||||||
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([]fission.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) 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
|
package controller
|
||||||
|
|
||||||
import (
|
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) {
|
err = tpr.EnsureFissionTPRs(kc)
|
||||||
return json.Marshal(r)
|
if err != nil {
|
||||||
}
|
log.Fatalf("Failed to create fission TPRs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
func (j JsonSerializer) deserialize(buf []byte, r resource) error {
|
fc.WaitForTPRs()
|
||||||
return json.Unmarshal(buf, r)
|
|
||||||
|
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"
|
"github.com/gorilla/mux"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"k8s.io/client-go/1.5/pkg/api"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
|
"github.com/fission/fission/tpr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) {
|
func (a *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) {
|
||||||
envs, err := api.EnvironmentStore.List()
|
envs, err := a.fissionClient.Environments(api.NamespaceAll).List(api.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(envs)
|
resp, err := json.Marshal(envs.Items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var env fission.Environment
|
var env tpr.Environment
|
||||||
err = json.Unmarshal(body, &env)
|
err = json.Unmarshal(body, &env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to unmarshal request body: [%v]", body)
|
log.Printf("Failed to unmarshal request body: [%v]", body)
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, err := api.EnvironmentStore.Create(&env)
|
err = validateResourceName(env.Metadata.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: env.Metadata.Name, Uid: uid}
|
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Create(&env)
|
||||||
resp, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
var m fission.Metadata
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
m.Name = vars["environment"]
|
name := vars["environment"]
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
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 {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(env)
|
resp, err := json.Marshal(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
vars := mux.Vars(r)
|
||||||
name := vars["environment"]
|
name := vars["environment"]
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var env fission.Environment
|
var env tpr.Environment
|
||||||
err = json.Unmarshal(body, &env)
|
err = json.Unmarshal(body, &env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if name != env.Metadata.Name {
|
if name != env.Metadata.Name {
|
||||||
err = fission.MakeError(fission.ErrorInvalidArgument, "Environment name doesn't match URL")
|
err = fission.MakeError(fission.ErrorInvalidArgument, "Environment name doesn't match URL")
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, err := api.EnvironmentStore.Update(&env)
|
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Update(&env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: env.Metadata.Name, Uid: uid}
|
resp, err := json.Marshal(enew.Metadata)
|
||||||
resp, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
vars := mux.Vars(r)
|
||||||
var m fission.Metadata
|
name := vars["environment"]
|
||||||
m.Name = vars["environment"]
|
ns := vars["namespace"]
|
||||||
|
if len(ns) == 0 {
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
ns = api.NamespaceDefault
|
||||||
if len(m.Uid) == 0 {
|
|
||||||
log.WithFields(log.Fields{"httpTrigger": m.Name}).Info("Deleting all versions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := api.EnvironmentStore.Delete(m)
|
err := a.fissionClient.Environments(ns).Delete(name, &api.DeleteOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -26,165 +25,149 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"k8s.io/client-go/1.5/pkg/api"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
|
"github.com/fission/fission/tpr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) FunctionApiList(w http.ResponseWriter, r *http.Request) {
|
func (a *API) FunctionApiList(w http.ResponseWriter, r *http.Request) {
|
||||||
funcs, err := api.FunctionStore.List()
|
funcs, err := a.fissionClient.Functions(api.NamespaceAll).List(api.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(funcs)
|
resp, err := json.Marshal(funcs.Items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var f fission.Function
|
var f tpr.Function
|
||||||
err = json.Unmarshal(body, &f)
|
err = json.Unmarshal(body, &f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dec, err := base64.StdEncoding.DecodeString(f.Code)
|
err = validateResourceName(f.Metadata.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
|
||||||
}
|
|
||||||
f.Code = string(dec)
|
|
||||||
|
|
||||||
uid, err := api.FunctionStore.Create(&f)
|
|
||||||
if err != nil {
|
|
||||||
api.respondWithError(w, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: f.Name, Uid: uid}
|
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Create(&f)
|
||||||
resp, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
var m fission.Metadata
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
m.Name = vars["function"]
|
name := vars["function"]
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
ns := vars["namespace"]
|
||||||
raw := r.FormValue("raw") // just the code
|
if len(ns) == 0 {
|
||||||
|
ns = api.NamespaceDefault
|
||||||
|
}
|
||||||
|
|
||||||
f, err := api.FunctionStore.Get(&m)
|
f, err := a.fissionClient.Functions(ns).Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp []byte
|
resp, err := json.Marshal(f)
|
||||||
if raw != "" {
|
if err != nil {
|
||||||
resp = []byte(f.Code)
|
a.respondWithError(w, err)
|
||||||
} else {
|
return
|
||||||
f.Code = base64.StdEncoding.EncodeToString([]byte(f.Code))
|
|
||||||
resp, err = json.Marshal(f)
|
|
||||||
if err != nil {
|
|
||||||
api.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)
|
vars := mux.Vars(r)
|
||||||
funcName := vars["function"]
|
name := vars["function"]
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var f fission.Function
|
var f tpr.Function
|
||||||
err = json.Unmarshal(body, &f)
|
err = json.Unmarshal(body, &f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if funcName != f.Metadata.Name {
|
if name != f.Metadata.Name {
|
||||||
err = fission.MakeError(fission.ErrorInvalidArgument, "Function name doesn't match URL")
|
err = fission.MakeError(fission.ErrorInvalidArgument, "Function name doesn't match URL")
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dec, err := base64.StdEncoding.DecodeString(f.Code)
|
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Update(&f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
|
||||||
}
|
|
||||||
f.Code = string(dec)
|
|
||||||
|
|
||||||
uid, err := api.FunctionStore.Update(&f)
|
|
||||||
if err != nil {
|
|
||||||
api.respondWithError(w, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: f.Name, Uid: uid}
|
resp, err := json.Marshal(fnew.Metadata)
|
||||||
resp, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
vars := mux.Vars(r)
|
||||||
var m fission.Metadata
|
name := vars["function"]
|
||||||
m.Name = vars["function"]
|
ns := vars["namespace"]
|
||||||
|
if len(ns) == 0 {
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
ns = api.NamespaceDefault
|
||||||
if len(m.Uid) == 0 {
|
|
||||||
log.WithFields(log.Fields{"function": m.Name}).Info("Deleting all versions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := api.FunctionStore.Delete(m)
|
err := a.fissionClient.Functions(ns).Delete(name, &api.DeleteOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
api.respondWithSuccess(w, []byte(""))
|
a.respondWithSuccess(w, []byte(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
// FunctionLogsApiPost establishes a proxy server to log database, and redirect
|
// FunctionLogsApiPost establishes a proxy server to log database, and redirect
|
||||||
// query command send from client to database then proxy back the db response.
|
// 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)
|
vars := mux.Vars(r)
|
||||||
// get dbType from url
|
// get dbType from url
|
||||||
dbType := vars["dbType"]
|
dbType := vars["dbType"]
|
||||||
|
|
||||||
// find correspond db http url
|
// find correspond db http url
|
||||||
dbCnf := api.getLogDBConfig(dbType)
|
dbCnf := a.getLogDBConfig(dbType)
|
||||||
|
|
||||||
svcUrl, err := url.Parse(dbCnf.httpURL)
|
svcUrl, err := url.Parse(dbCnf.httpURL)
|
||||||
if err != nil {
|
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"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"
|
||||||
|
"github.com/fission/fission/tpr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
func (a *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
||||||
triggers, err := api.HTTPTriggerStore.List()
|
triggers, err := a.fissionClient.Httptriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(triggers)
|
resp, err := json.Marshal(triggers.Items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
func (a *API) checkHttpTriggerDuplicates(t *tpr.Httptrigger) error {
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
triggers, err := a.fissionClient.Httptriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
for _, ht := range triggers.Items {
|
||||||
var t fission.HTTPTrigger
|
if ht.Spec.RelativeURL == t.Spec.RelativeURL && ht.Spec.Method == t.Spec.Method {
|
||||||
err = json.Unmarshal(body, &t)
|
return fission.MakeError(fission.ErrorNameExists,
|
||||||
if err != nil {
|
fmt.Sprintf("HTTPTrigger with same URL & method already exists (%v)",
|
||||||
api.respondWithError(w, err)
|
ht.Metadata.Name))
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
|
var t tpr.Httptrigger
|
||||||
resp, err := json.Marshal(m)
|
err = json.Unmarshal(body, &t)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
var m fission.Metadata
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
m.Name = vars["httpTrigger"]
|
name := vars["httpTrigger"]
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
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 {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(t)
|
resp, err := json.Marshal(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
vars := mux.Vars(r)
|
||||||
name := vars["httpTrigger"]
|
name := vars["httpTrigger"]
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var t fission.HTTPTrigger
|
var t tpr.Httptrigger
|
||||||
err = json.Unmarshal(body, &t)
|
err = json.Unmarshal(body, &t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if name != t.Metadata.Name {
|
if name != t.Metadata.Name {
|
||||||
err = fission.MakeError(fission.ErrorInvalidArgument, "HTTPTrigger name doesn't match URL")
|
err = fission.MakeError(fission.ErrorInvalidArgument, "HTTPTrigger name doesn't match URL")
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, err := api.HTTPTriggerStore.Update(&t)
|
err = a.checkHttpTriggerDuplicates(&t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
|
tnew, err := a.fissionClient.Httptriggers(t.Metadata.Namespace).Update(&t)
|
||||||
resp, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
vars := mux.Vars(r)
|
||||||
var m fission.Metadata
|
name := vars["httpTrigger"]
|
||||||
m.Name = vars["httpTrigger"]
|
ns := vars["namespace"]
|
||||||
|
if len(ns) == 0 {
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
ns = api.NamespaceDefault
|
||||||
if len(m.Uid) == 0 {
|
|
||||||
log.WithFields(log.Fields{"httpTrigger": m.Name}).Info("Deleting all versions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := api.HTTPTriggerStore.Delete(m)
|
err := a.fissionClient.Httptriggers(ns).Delete(name, &api.DeleteOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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
|
|
||||||
}
|
|
||||||
+54
-67
@@ -22,145 +22,132 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"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"
|
||||||
|
"github.com/fission/fission/tpr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) MessageQueueTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
func (a *API) MessageQueueTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
||||||
mqType := r.FormValue("mqtype")
|
//mqType := r.FormValue("mqtype") // ignored for now
|
||||||
triggers, err := api.MessageQueueTriggerStore.List(mqType)
|
triggers, err := a.fissionClient.Messagequeuetriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err := json.Marshal(triggers)
|
resp, err := json.Marshal(triggers.Items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) MessageQueueApiCreate(w http.ResponseWriter, r *http.Request) {
|
func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var mqTrigger fission.MessageQueueTrigger
|
var mqTrigger tpr.Messagequeuetrigger
|
||||||
err = json.Unmarshal(body, &mqTrigger)
|
err = json.Unmarshal(body, &mqTrigger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// trigger name must not conflict with any other trigger
|
err = validateResourceName(mqTrigger.Metadata.Name)
|
||||||
// even they are different message queue type
|
|
||||||
triggers, err := api.MessageQueueTriggerStore.List("")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, trigger := range triggers {
|
|
||||||
if trigger.Name == mqTrigger.Name {
|
|
||||||
err = fission.MakeError(fission.ErrorNameExists,
|
|
||||||
"Message queue trigger with same name already exists")
|
|
||||||
api.respondWithError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save trigger info
|
|
||||||
uid, err := api.MessageQueueTriggerStore.Create(&mqTrigger)
|
|
||||||
if err != nil {
|
|
||||||
api.respondWithError(w, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mqTriggerMeta := fission.Metadata{Name: mqTrigger.Metadata.Name, Uid: uid}
|
tnew, err := a.fissionClient.Messagequeuetriggers(mqTrigger.Metadata.Namespace).Create(&mqTrigger)
|
||||||
resp, err := json.Marshal(mqTriggerMeta)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := json.Marshal(tnew.Metadata)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) MessageQueueApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) MessageQueueTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
mqTriggerMeta := fission.Metadata{
|
name := vars["mqTrigger"]
|
||||||
Name: vars["mqTrigger"],
|
ns := vars["namespace"]
|
||||||
Uid: r.FormValue("uid"), // empty if uid is absent
|
if len(ns) == 0 {
|
||||||
|
ns = api.NamespaceDefault
|
||||||
}
|
}
|
||||||
mqTrigger, err := api.MessageQueueTriggerStore.Get(&mqTriggerMeta)
|
|
||||||
|
mqTrigger, err := a.fissionClient.Messagequeuetriggers(ns).Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err := json.Marshal(mqTrigger)
|
resp, err := json.Marshal(mqTrigger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) MessageQueueApiUpdate(w http.ResponseWriter, r *http.Request) {
|
func (a *API) MessageQueueTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
mqtName := vars["mqTrigger"]
|
name := vars["mqTrigger"]
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var mqTrigger fission.MessageQueueTrigger
|
var mqTrigger tpr.Messagequeuetrigger
|
||||||
err = json.Unmarshal(body, &mqTrigger)
|
err = json.Unmarshal(body, &mqTrigger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if mqtName != mqTrigger.Metadata.Name {
|
if name != mqTrigger.Metadata.Name {
|
||||||
err = fission.MakeError(fission.ErrorInvalidArgument, "Message queue trigger name doesn't match URL")
|
err = fission.MakeError(fission.ErrorInvalidArgument, "Message queue trigger name doesn't match URL")
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, err := api.MessageQueueTriggerStore.Update(&mqTrigger)
|
tnew, err := a.fissionClient.Messagequeuetriggers(mqTrigger.Metadata.Namespace).Update(&mqTrigger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mqTriggerMeta := fission.Metadata{Name: mqTrigger.Metadata.Name, Uid: uid}
|
resp, err := json.Marshal(tnew.Metadata)
|
||||||
resp, err := json.Marshal(mqTriggerMeta)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) MessageQueueApiDelete(w http.ResponseWriter, r *http.Request) {
|
func (a *API) MessageQueueTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
mqTriggerMeta := fission.Metadata{
|
name := vars["mqTrigger"]
|
||||||
Name: vars["mqTrigger"],
|
ns := vars["namespace"]
|
||||||
Uid: r.FormValue("uid"), // empty if uid is absent
|
if len(ns) == 0 {
|
||||||
|
ns = api.NamespaceDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(mqTriggerMeta.Uid) == 0 {
|
err := a.fissionClient.Messagequeuetriggers(ns).Delete(name, &api.DeleteOptions{})
|
||||||
log.WithFields(log.Fields{"mqTrigger": mqTriggerMeta.Name}).Info("Deleting all versions")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := api.MessageQueueTriggerStore.Delete(mqTriggerMeta)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
api.respondWithSuccess(w, []byte(""))
|
a.respondWithSuccess(w, []byte(""))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
CopyrigmqTrigger 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
|
|
||||||
|
|
||||||
mqTriggertp://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/fission/fission"
|
|
||||||
uuid "github.com/satori/go.uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MessageQueueTriggerStore struct {
|
|
||||||
ResourceStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mqs *MessageQueueTriggerStore) Create(mqTrigger *fission.MessageQueueTrigger) (string, error) {
|
|
||||||
mqTrigger.Metadata.Uid = uuid.NewV4().String()
|
|
||||||
return mqTrigger.Metadata.Uid, mqs.ResourceStore.create(mqTrigger)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mqs *MessageQueueTriggerStore) Get(m *fission.Metadata) (*fission.MessageQueueTrigger, error) {
|
|
||||||
var mqTrigger fission.MessageQueueTrigger
|
|
||||||
err := mqs.ResourceStore.read(m.Name, &mqTrigger)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &mqTrigger, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mqs *MessageQueueTriggerStore) Update(mqTrigger *fission.MessageQueueTrigger) (string, error) {
|
|
||||||
mqTrigger.Metadata.Uid = uuid.NewV4().String()
|
|
||||||
return mqTrigger.Metadata.Uid, mqs.ResourceStore.update(mqTrigger)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mqs *MessageQueueTriggerStore) Delete(m fission.Metadata) error {
|
|
||||||
typeName, err := getTypeName(fission.MessageQueueTrigger{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return mqs.ResourceStore.delete(typeName, m.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mqs *MessageQueueTriggerStore) List(mqType string) ([]fission.MessageQueueTrigger, error) {
|
|
||||||
typeName, err := getTypeName(fission.MessageQueueTrigger{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
bufs, err := mqs.ResourceStore.getAll(typeName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
triggers := make([]fission.MessageQueueTrigger, 0, len(bufs))
|
|
||||||
js := JsonSerializer{}
|
|
||||||
for _, buf := range bufs {
|
|
||||||
var mqTrigger fission.MessageQueueTrigger
|
|
||||||
err = js.deserialize([]byte(buf), &mqTrigger)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(mqType) > 0 && mqType != mqTrigger.MessageQueueType {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
triggers = append(triggers, mqTrigger)
|
|
||||||
}
|
|
||||||
|
|
||||||
return triggers, nil
|
|
||||||
}
|
|
||||||
@@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,13 +16,23 @@ limitations under the License.
|
|||||||
|
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
type (
|
import (
|
||||||
resource interface {
|
"fmt"
|
||||||
Key() string
|
"log"
|
||||||
}
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
serializer interface {
|
"net/url"
|
||||||
serialize(r resource) ([]byte, error)
|
|
||||||
deserialize(buf []byte, r resource) error
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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/gorilla/mux"
|
||||||
"github.com/robfig/cron"
|
"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"
|
||||||
|
"github.com/fission/fission/tpr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
func (a *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) {
|
||||||
triggers, err := api.TimeTriggerStore.List()
|
triggers, err := a.fissionClient.Timetriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(triggers)
|
resp, err := json.Marshal(triggers.Items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var t fission.TimeTrigger
|
var t tpr.Timetrigger
|
||||||
err = json.Unmarshal(body, &t)
|
err = json.Unmarshal(body, &t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
triggers, err := api.TimeTriggerStore.List()
|
err = validateResourceName(t.Metadata.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
|
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, err := api.TimeTriggerStore.Create(&t)
|
tnew, err := a.fissionClient.Timetriggers(t.Metadata.Namespace).Create(&t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
|
resp, err := json.Marshal(tnew.Metadata)
|
||||||
resp, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
var m fission.Metadata
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
m.Name = vars["timeTrigger"]
|
name := vars["timeTrigger"]
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
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 {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(t)
|
resp, err := json.Marshal(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
vars := mux.Vars(r)
|
||||||
name := vars["timeTrigger"]
|
name := vars["timeTrigger"]
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var t fission.TimeTrigger
|
var t tpr.Timetrigger
|
||||||
err = json.Unmarshal(body, &t)
|
err = json.Unmarshal(body, &t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if name != t.Metadata.Name {
|
if name != t.Metadata.Name {
|
||||||
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger name doesn't match URL")
|
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger name doesn't match URL")
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = cron.Parse(t.Cron)
|
_, err = cron.Parse(t.Spec.Cron)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
|
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, err := api.TimeTriggerStore.Update(&t)
|
tnew, err := a.fissionClient.Timetriggers(t.Metadata.Namespace).Update(&t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
|
resp, err := json.Marshal(tnew.Metadata)
|
||||||
resp, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
vars := mux.Vars(r)
|
||||||
var m fission.Metadata
|
name := vars["timeTrigger"]
|
||||||
m.Name = vars["timeTrigger"]
|
ns := vars["namespace"]
|
||||||
|
if len(ns) == 0 {
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
ns = api.NamespaceDefault
|
||||||
if len(m.Uid) == 0 {
|
|
||||||
log.WithFields(log.Fields{"timeTrigger": m.Name}).Info("Deleting all versions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := api.TimeTriggerStore.Delete(m)
|
err := a.fissionClient.Timetriggers(ns).Delete(name, &api.DeleteOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"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"
|
||||||
|
"github.com/fission/fission/tpr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) WatchApiList(w http.ResponseWriter, r *http.Request) {
|
func (a *API) WatchApiList(w http.ResponseWriter, r *http.Request) {
|
||||||
watches, err := api.WatchStore.List()
|
watches, err := a.fissionClient.Kuberneteswatchtriggers(api.NamespaceAll).List(api.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(watches)
|
resp, err := json.Marshal(watches.Items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var watch fission.Watch
|
var watch tpr.Kuberneteswatchtrigger
|
||||||
err = json.Unmarshal(body, &watch)
|
err = json.Unmarshal(body, &watch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
watch.Target = fission.UrlForFunction(&watch.Function)
|
err = validateResourceName(watch.Metadata.Name)
|
||||||
|
|
||||||
uid, err := api.WatchStore.Create(&watch)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &fission.Metadata{Name: watch.Metadata.Name, Uid: uid}
|
// TODO check for duplicate watches
|
||||||
resp, err := json.Marshal(m)
|
|
||||||
|
wnew, err := a.fissionClient.Kuberneteswatchtriggers(watch.Metadata.Namespace).Create(&watch)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
var m fission.Metadata
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
m.Name = vars["watch"]
|
name := vars["watch"]
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
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 {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := json.Marshal(watch)
|
resp, err := json.Marshal(watch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
api.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) WatchApiUpdate(w http.ResponseWriter, r *http.Request) {
|
func (a *API) WatchApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
api.respondWithError(w, fission.MakeError(fission.ErrorNotImplmented,
|
a.respondWithError(w, fission.MakeError(fission.ErrorNotImplmented,
|
||||||
"Not implemented"))
|
"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)
|
vars := mux.Vars(r)
|
||||||
var m fission.Metadata
|
name := vars["watch"]
|
||||||
m.Name = vars["watch"]
|
ns := vars["namespace"]
|
||||||
|
if len(ns) == 0 {
|
||||||
m.Uid = r.FormValue("uid") // empty if uid is absent
|
ns = api.NamespaceDefault
|
||||||
if len(m.Uid) == 0 {
|
|
||||||
log.WithFields(log.Fields{"watch": m.Name}).Info("Deleting all versions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := api.WatchStore.Delete(m)
|
err := a.fissionClient.Kuberneteswatchtriggers(ns).Delete(name, &api.DeleteOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
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 (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"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 {
|
type (
|
||||||
Url string `json:"url"`
|
FetchRequestType int
|
||||||
Filename string `json:"filename"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Fetcher struct {
|
FetchRequest struct {
|
||||||
sharedVolumePath string
|
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 {
|
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" {
|
if r.Method != "POST" {
|
||||||
http.Error(w, "", 404)
|
http.Error(w, "", 405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
defer func() {
|
||||||
|
elapsed := time.Now().Sub(startTime)
|
||||||
|
log.Printf("elapsed time in fetch request = %v", elapsed)
|
||||||
|
}()
|
||||||
|
|
||||||
// parse request
|
// parse request
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,65 +126,123 @@ func (fetcher *Fetcher) handler(w http.ResponseWriter, r *http.Request) {
|
|||||||
err = json.Unmarshal(body, &req)
|
err = json.Unmarshal(body, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading request body: %v", err)
|
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)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
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
|
// all done
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage: fetcher <shared volume path>
|
func (fetcher *Fetcher) rename(src string, dst string) error {
|
||||||
func main() {
|
err := os.Rename(src, dst)
|
||||||
dir := os.Args[1]
|
if err != nil {
|
||||||
if _, err := os.Stat(dir); err != nil {
|
return errors.New(fmt.Sprintf("Failed to move file: %v", err))
|
||||||
if os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(dir, os.ModeDir|0700)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error creating directory: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fetcher := MakeFetcher(dir)
|
return nil
|
||||||
mux := http.NewServeMux()
|
}
|
||||||
mux.HandleFunc("/", fetcher.handler)
|
|
||||||
http.ListenAndServe(":8000", mux)
|
// 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}
|
||||||
@@ -9,8 +9,9 @@ The environment works via convention where you create a Ruby method
|
|||||||
called `handler` with a single optional argument, a `Fission::Context`
|
called `handler` with a single optional argument, a `Fission::Context`
|
||||||
object.
|
object.
|
||||||
|
|
||||||
The `Fission::Context` object gives access to the Rack env, and a
|
The `Fission::Context` object gives access to the Rack env, a
|
||||||
request object. Please see `fission/context.rb` for the public api.
|
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
|
The `Fission::Request` object is a subclass of `Rack::Request` and
|
||||||
provides access to parameters and headers. See `fission/request.rb`
|
provides access to parameters and headers. See `fission/request.rb`
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative 'request'
|
require_relative 'request'
|
||||||
|
require 'forwardable'
|
||||||
|
|
||||||
module Fission
|
module Fission
|
||||||
class Context
|
class Context
|
||||||
|
extend Forwardable
|
||||||
|
|
||||||
|
def_instance_delegator :request, :logger
|
||||||
|
|
||||||
attr_reader :env
|
attr_reader :env
|
||||||
|
|
||||||
def initialize(env)
|
def initialize(env)
|
||||||
|
|||||||
@@ -5,14 +5,18 @@ require_relative 'context'
|
|||||||
module Fission
|
module Fission
|
||||||
module Handler
|
module Handler
|
||||||
def self.call(env)
|
def self.call(env)
|
||||||
|
context = Context.new(env)
|
||||||
|
|
||||||
response = if method(:handler).arity > 0
|
response = if method(:handler).arity > 0
|
||||||
handler(Context.new(env))
|
handler(context)
|
||||||
else
|
else
|
||||||
handler
|
handler
|
||||||
end
|
end
|
||||||
|
|
||||||
response.is_a?(Array) ? response : Rack::Response.new([response]).finish
|
response.is_a?(Array) ? response : Rack::Response.new([response]).finish
|
||||||
rescue
|
|
||||||
|
rescue => e
|
||||||
|
context.logger.error(%(Function failed - #{e}\n#{e.backtrace.join("\n")}))
|
||||||
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
|
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
require 'benchmark'
|
||||||
|
|
||||||
module Fission
|
module Fission
|
||||||
CODE_PATH = '/userfunc/user'
|
CODE_PATH = '/userfunc/user'
|
||||||
|
|
||||||
module Specializer
|
module Specializer
|
||||||
def self.call(env)
|
def self.call(env)
|
||||||
load CODE_PATH
|
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
|
Rack::Response.new([], 201).finish
|
||||||
rescue
|
|
||||||
|
rescue => e
|
||||||
|
request.logger.error(%(Specialization failed - #{e}\n#{e.backtrace.join("\n")}))
|
||||||
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
|
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rack'
|
require 'rack'
|
||||||
|
require 'logger'
|
||||||
|
|
||||||
require_relative 'fission/specializer'
|
require_relative 'fission/specializer'
|
||||||
require_relative 'fission/handler'
|
require_relative 'fission/handler'
|
||||||
|
|
||||||
app = Rack::Builder.new do
|
app = Rack::Builder.new do
|
||||||
use Rack::CommonLogger, $stderr
|
use Rack::Logger, Logger::DEBUG
|
||||||
|
|
||||||
map "/specialize" do
|
map "/specialize" do
|
||||||
run Fission::Specializer
|
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
|
$ go-function-build hello.go
|
||||||
|
|
||||||
# Upload the function to fission
|
# 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
|
# Map /hello to the hello function
|
||||||
$ fission route create --method GET --url /hello --function hello
|
$ fission route create --method GET --url /hello --function hello
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
def handler(context)
|
def handler(context)
|
||||||
request = context.request
|
context.logger.info("Received request")
|
||||||
|
|
||||||
msg = <<~MSG
|
msg = <<~MSG
|
||||||
---ENV---
|
---ENV---
|
||||||
#{request.env.map { |h| h.join('=') }.join("\n") }
|
#{context.request.env.map { |h| h.join('=') }.join("\n") }
|
||||||
|
|
||||||
---HEADERS---
|
---HEADERS---
|
||||||
#{request.headers.map { |h| h.join(': ') }.join("\n") }
|
#{context.request.headers.map { |h| h.join(': ') }.join("\n") }
|
||||||
|
|
||||||
---PARAMS---
|
---PARAMS---
|
||||||
#{request.params.map { |h| h.join('=') }.join("\n") }
|
#{context.request.params.map { |h| h.join('=') }.join("\n") }
|
||||||
|
|
||||||
--BODY--
|
--BODY--
|
||||||
#{request.body.read}
|
#{context.request.body.read}
|
||||||
MSG
|
MSG
|
||||||
|
|
||||||
Rack::Response.new([msg]).finish
|
Rack::Response.new([msg]).finish
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
GOOS=linux GOARCH=386 go build
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
|
||||||
|
|||||||
+71
-55
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/docopt/docopt-go"
|
"github.com/docopt/docopt-go"
|
||||||
@@ -11,40 +12,29 @@ import (
|
|||||||
"github.com/fission/fission/mqtrigger"
|
"github.com/fission/fission/mqtrigger"
|
||||||
"github.com/fission/fission/poolmgr"
|
"github.com/fission/fission/poolmgr"
|
||||||
"github.com/fission/fission/router"
|
"github.com/fission/fission/router"
|
||||||
|
"github.com/fission/fission/storagesvc"
|
||||||
"github.com/fission/fission/timer"
|
"github.com/fission/fission/timer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runController(port int, etcdUrl string, filepath string) {
|
func runController(port int) {
|
||||||
// filePath will be created if it doesn't exist.
|
controller.Start(port)
|
||||||
fileStore := controller.MakeFileStore(filepath)
|
|
||||||
if fileStore == nil {
|
|
||||||
log.Fatalf("Failed to initialize filestore")
|
|
||||||
}
|
|
||||||
|
|
||||||
rs, err := controller.MakeResourceStore(fileStore, []string{etcdUrl})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
api := controller.MakeAPI(rs)
|
|
||||||
api.Serve(port)
|
|
||||||
log.Fatalf("Error: Controller exited.")
|
log.Fatalf("Error: Controller exited.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRouter(port int, controllerUrl string, poolmgrUrl string) {
|
func runRouter(port int, poolmgrUrl string) {
|
||||||
router.Start(port, controllerUrl, poolmgrUrl)
|
router.Start(port, poolmgrUrl)
|
||||||
log.Fatalf("Error: Router exited.")
|
log.Fatalf("Error: Router exited.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPoolmgr(port int, controllerUrl string, namespace string) {
|
func runPoolmgr(port int, fissionNamespace, functionNamespace string) {
|
||||||
err := poolmgr.StartPoolmgr(controllerUrl, namespace, port)
|
err := poolmgr.StartPoolmgr(fissionNamespace, functionNamespace, port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error starting poolmgr: %v", err)
|
log.Fatalf("Error starting poolmgr: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runKubeWatcher(controllerUrl, routerUrl string) {
|
func runKubeWatcher(routerUrl string) {
|
||||||
err := kubewatcher.Start(controllerUrl, routerUrl)
|
err := kubewatcher.Start(routerUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error starting kubewatcher: %v", err)
|
log.Fatalf("Error starting kubewatcher: %v", err)
|
||||||
}
|
}
|
||||||
@@ -55,20 +45,29 @@ func runLogger() {
|
|||||||
log.Fatalf("Error: Logger exited.")
|
log.Fatalf("Error: Logger exited.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTimer(controllerUrl, routerUrl string) {
|
func runTimer(routerUrl string) {
|
||||||
err := timer.Start(controllerUrl, routerUrl)
|
err := timer.Start(routerUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error starting timer: %v", err)
|
log.Fatalf("Error starting timer: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMessageQueueMgr(controllerUrl, routerUrl string) {
|
func runMessageQueueMgr(routerUrl string) {
|
||||||
err := messagequeue.Start(controllerUrl, routerUrl)
|
err := messagequeue.Start(routerUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error starting timer: %v", err)
|
log.Fatalf("Error starting timer: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runStorageSvc(port int, filePath string) {
|
||||||
|
subdir := os.Getenv("SUBDIR")
|
||||||
|
if len(subdir) == 0 {
|
||||||
|
subdir = "fission-functions"
|
||||||
|
}
|
||||||
|
storagesvc.RunStorageService(storagesvc.StorageTypeLocal,
|
||||||
|
filePath, subdir, port)
|
||||||
|
}
|
||||||
|
|
||||||
func getPort(portArg interface{}) int {
|
func getPort(portArg interface{}) int {
|
||||||
portArgStr := portArg.(string)
|
portArgStr := portArg.(string)
|
||||||
port, err := strconv.Atoi(portArgStr)
|
port, err := strconv.Atoi(portArgStr)
|
||||||
@@ -91,64 +90,75 @@ func main() {
|
|||||||
|
|
||||||
Use it to start one or more of the fission servers:
|
Use it to start one or more of the fission servers:
|
||||||
|
|
||||||
Controller keeps track of functions, triggers, environments.
|
Controller is a stateless API frontend for fission resources.
|
||||||
|
|
||||||
Pool manager maintains a pool of generalized function containers, and specializes them on-demand. Poolmgr must be run from a pod in a Kubernetes cluster.
|
Pool manager maintains a pool of generalized function containers, and
|
||||||
|
specializes them on-demand. Poolmgr must be run from a pod in a
|
||||||
|
Kubernetes cluster.
|
||||||
|
|
||||||
Router implements HTTP triggers: it routes to running instances, working with the controller and poolmgr.
|
Router implements HTTP triggers: it routes to running instances,
|
||||||
|
working with the controller and poolmgr.
|
||||||
|
|
||||||
|
Kubewatcher implements Kubernetes Watch triggers: it watches
|
||||||
|
Kubernetes resources and invokes functions described in the
|
||||||
|
KubernetesWatchTrigger.
|
||||||
|
|
||||||
|
The storage service implements storage for functions too large to fit
|
||||||
|
in the Kubernetes API resource object. It supports various storage
|
||||||
|
backends.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
fission-bundle --controllerPort=<port> [--etcdUrl=<etcdUrl>] --filepath=<filepath>
|
fission-bundle --controllerPort=<port>
|
||||||
fission-bundle --routerPort=<port> [--controllerUrl=<url> --poolmgrUrl=<url>]
|
fission-bundle --routerPort=<port> [--poolmgrUrl=<url>]
|
||||||
fission-bundle --poolmgrPort=<port> [--controllerUrl=<url> --namespace=<namespace>]
|
fission-bundle --poolmgrPort=<port> [--namespace=<namespace>] [--fission-namespace=<namespace>]
|
||||||
fission-bundle --kubewatcher [--controllerUrl=<url> --routerUrl=<url>]
|
fission-bundle --kubewatcher [--routerUrl=<url>]
|
||||||
|
fission-bundle --storageServicePort=<port> --filePath=<filePath>
|
||||||
fission-bundle --logger
|
fission-bundle --logger
|
||||||
fission-bundle --timer [--controllerUrl=<url> --routerUrl=<url>]
|
fission-bundle --timer [--routerUrl=<url>]
|
||||||
fission-bundle --mqt [--controllerUrl=<url> --routerUrl=<url>]
|
fission-bundle --mqt [--routerUrl=<url>]
|
||||||
Options:
|
Options:
|
||||||
--controllerPort=<port> Port that the controller should listen on.
|
--controllerPort=<port> Port that the controller should listen on.
|
||||||
--routerPort=<port> Port that the router should listen on.
|
--routerPort=<port> Port that the router should listen on.
|
||||||
--poolmgrPort=<port> Port that the poolmgr should listen on.
|
--poolmgrPort=<port> Port that the poolmgr should listen on.
|
||||||
--controllerUrl=<url> Controller URL. Not required if --controllerPort is specified.
|
--storageServicePort=<port> Port that the storage service should listen on.
|
||||||
--poolmgrUrl=<url> Poolmgr URL. Not required if --poolmgrPort is specified.
|
--poolmgrUrl=<url> Poolmgr URL. Not required if --poolmgrPort is specified.
|
||||||
--routerUrl=<url> Router URL.
|
--routerUrl=<url> Router URL.
|
||||||
--etcdUrl=<etcdUrl> Etcd URL.
|
--etcdUrl=<etcdUrl> Etcd URL.
|
||||||
--filepath=<filepath> Directory to store functions in.
|
--filePath=<filePath> Directory to store functions in.
|
||||||
--namespace=<namespace> Kubernetes namespace in which to run function containers. Defaults to 'fission-function'.
|
--namespace=<namespace> Kubernetes namespace in which to run function containers. Defaults to 'fission-function'.
|
||||||
--kubewatcher Start Kubernetes events watcher.
|
--kubewatcher Start Kubernetes events watcher.
|
||||||
--logger Start logger.
|
--logger Start logger.
|
||||||
--timer Start Timer.
|
--timer Start Timer.
|
||||||
--mqt Start message queue trigger.
|
--mqt Start message queue trigger.
|
||||||
`
|
`
|
||||||
arguments, err := docopt.Parse(usage, nil, true, "fission-bundle", false)
|
arguments, err := docopt.Parse(usage, nil, true, "fission-bundle", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace := getStringArgWithDefault(arguments["--namespace"], "fission-function")
|
functionNs := getStringArgWithDefault(arguments["--namespace"], "fission-function")
|
||||||
|
fissionNs := getStringArgWithDefault(arguments["--fission-namespace"], "fission")
|
||||||
|
|
||||||
controllerUrl := getStringArgWithDefault(arguments["--controllerUrl"], "http://controller.fission")
|
|
||||||
etcdUrl := getStringArgWithDefault(arguments["--etcdUrl"], "http://etcd:2379")
|
|
||||||
poolmgrUrl := getStringArgWithDefault(arguments["--poolmgrUrl"], "http://poolmgr.fission")
|
poolmgrUrl := getStringArgWithDefault(arguments["--poolmgrUrl"], "http://poolmgr.fission")
|
||||||
routerUrl := getStringArgWithDefault(arguments["--routerUrl"], "http://router.fission")
|
routerUrl := getStringArgWithDefault(arguments["--routerUrl"], "http://router.fission")
|
||||||
|
|
||||||
if arguments["--controllerPort"] != nil {
|
if arguments["--controllerPort"] != nil {
|
||||||
port := getPort(arguments["--controllerPort"])
|
port := getPort(arguments["--controllerPort"])
|
||||||
runController(port, etcdUrl, arguments["--filepath"].(string))
|
runController(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
if arguments["--routerPort"] != nil {
|
if arguments["--routerPort"] != nil {
|
||||||
port := getPort(arguments["--routerPort"])
|
port := getPort(arguments["--routerPort"])
|
||||||
runRouter(port, controllerUrl, poolmgrUrl)
|
runRouter(port, poolmgrUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if arguments["--poolmgrPort"] != nil {
|
if arguments["--poolmgrPort"] != nil {
|
||||||
port := getPort(arguments["--poolmgrPort"])
|
port := getPort(arguments["--poolmgrPort"])
|
||||||
runPoolmgr(port, controllerUrl, namespace)
|
runPoolmgr(port, fissionNs, functionNs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if arguments["--kubewatcher"] == true {
|
if arguments["--kubewatcher"] == true {
|
||||||
runKubeWatcher(controllerUrl, routerUrl)
|
runKubeWatcher(routerUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if arguments["--logger"] == true {
|
if arguments["--logger"] == true {
|
||||||
@@ -156,11 +166,17 @@ Options:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if arguments["--timer"] == true {
|
if arguments["--timer"] == true {
|
||||||
runTimer(controllerUrl, routerUrl)
|
runTimer(routerUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if arguments["--mqt"] == true {
|
if arguments["--mqt"] == true {
|
||||||
runMessageQueueMgr(controllerUrl, routerUrl)
|
runMessageQueueMgr(routerUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if arguments["--storageServicePort"] == true {
|
||||||
|
port := getPort(arguments["--storageServicePort"])
|
||||||
|
filePath := arguments["--filePath"].(string)
|
||||||
|
runStorageSvc(port, filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
|
|||||||
+23
-38
@@ -46,6 +46,25 @@ roleRef:
|
|||||||
name: admin
|
name: admin
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
---
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: fission-fetcher
|
||||||
|
namespace: fission-function
|
||||||
|
---
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: fission-fetcher-tpr
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: fission-fetcher
|
||||||
|
namespace: fission-function
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: cluster-admin
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
---
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
@@ -62,7 +81,7 @@ spec:
|
|||||||
- name: controller
|
- name: controller
|
||||||
image: fission/fission-bundle
|
image: fission/fission-bundle
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--controllerPort", "8888", "--filepath", "/filestore"]
|
args: ["--controllerPort", "8888"]
|
||||||
serviceAccountName: fission-svc
|
serviceAccountName: fission-svc
|
||||||
---
|
---
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
@@ -115,6 +134,9 @@ spec:
|
|||||||
image: fission/fission-bundle
|
image: fission/fission-bundle
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--poolmgrPort", "8888"]
|
args: ["--poolmgrPort", "8888"]
|
||||||
|
env:
|
||||||
|
- name: FETCHER_IMAGE
|
||||||
|
value: fission/fetcher:v2
|
||||||
serviceAccountName: fission-svc
|
serviceAccountName: fission-svc
|
||||||
---
|
---
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
@@ -135,40 +157,3 @@ spec:
|
|||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--kubewatcher"]
|
args: ["--kubewatcher"]
|
||||||
serviceAccountName: fission-svc
|
serviceAccountName: fission-svc
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: etcd
|
|
||||||
namespace: fission
|
|
||||||
labels:
|
|
||||||
svc: etcd
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
- port: 2379
|
|
||||||
targetPort: 2379
|
|
||||||
selector:
|
|
||||||
svc: etcd
|
|
||||||
---
|
|
||||||
apiVersion: extensions/v1beta1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: etcd
|
|
||||||
namespace: fission
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
svc: etcd
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: etcd
|
|
||||||
image: quay.io/coreos/etcd
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
env:
|
|
||||||
- name: ETCD_LISTEN_CLIENT_URLS
|
|
||||||
value: http://0.0.0.0:2379
|
|
||||||
- name: ETCD_ADVERTISE_CLIENT_URLS
|
|
||||||
value: http://etcd:2379
|
|
||||||
serviceAccountName: fission-svc
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user