Compare commits

...
56 Commits
Author SHA1 Message Date
Soam Vasani db372c11eb Version -> 0.4.1 2018-01-20 06:15:11 -08:00
Ta-Ching ChenandSoam Vasani 6a1da19e73 Fix python environment failed to launch due to port needs integer not string (#451) 2018-01-20 06:06:50 -08:00
Soam Vasani 816fd25a6c Version -> 0.4.1 2018-01-20 06:05:19 -08:00
prithvirameshandSoam Vasani c03bb01bca Fix fission function logs (#448)
Remove hard coded field indices in influx query, instead searching for them by name.
2018-01-19 17:37:12 -08:00
VishalandSoam Vasani bed189c715 Fscache support for multiple kubernetes objects (#435)
Modifies the fsCache to support a list of arbitrary kubernetes objects instead of just one pod.  This allows better support for the newdeploy backend.

Also removes the byKubeObject cache index; cleanup is changed to use the byFunction index, and byKubeObject is no longer needed.
2018-01-16 13:05:24 -08:00
Soam VasaniandGitHub 80a4bcbcff Integration test improvements (#447)
A few improvements to the integration test:

* Search for test files recursively in the test dir
* Output a test report at the end for a quick summary of what tests passed/failed/were skipped
* Add support skipping test files; skip the logging test since it's broken (#446 is tracking this)
* Switch to latest helm version
* Cleanup namespace
2018-01-15 16:08:38 -08:00
Graham RoundsandSoam Vasani f420383882 Use storageClassName in Helm Charts (#444) (#445) 2018-01-13 00:24:44 -08:00
Soam Vasani fac508c7e0 Demo script bugfix 2018-01-02 14:10:23 +05:30
Ta-Ching ChenandSoam Vasani 64a82747a9 Improve travi-ci test scripts (#434)
Make our travis-based integration test less flaky:

* Wait for tiller ready before installation

* Remove all existing old releases before installation
2018-01-01 11:34:11 +05:30
Ta-Ching ChenandSoam Vasani 5f9e54bbeb Fix glide failed to check out github.com/dsnet/compress & update gorilla pkg version (#429) 2017-12-14 05:23:49 +05:30
Valentin OuvrardandSoam Vasani 8d6b33258d Format fetcher and specialize URLs correctly in case of IPv6 (#408) 2017-12-11 01:26:13 -06:00
Adam BouhenguelandSoam Vasani be84a864fb Add support for Host matching in HTTP triggers (#425)
Include Host in httptrigger output table and fix duplicate error detection to consider Host as well.
2017-12-11 01:22:05 -06:00
Soam Vasani dff34b08e9 Update install link 2017-12-07 17:33:24 -06:00
Soam VasaniandGitHub 1e6b7a4fc8 Golang v2 environment -- runtime and builder (#427)
v2 environment for Go; includes runtime and builder.
2017-12-06 23:03:05 -06:00
Ta-Ching ChenandSoam Vasani eed3018458 [Issue 423] build logs not saved on build error (#426)
* Fix builder return empty buildlogs when encounters errors

* Append error logs to build logs

* Builder manager updates package with build logs plus error log

* Fix potential nil pointer bug

* Replace numeric status code with built-in const

* Remove no use parameter

* Fix go fmt
2017-12-06 22:20:41 -06:00
Karim BoumedhelandSoam Vasani fa810e6f80 Removed openshift specifics as they are no longer necessary (#424)
tested fission on openshift 3.7.0 and installed fine, without having to use openshift variables
those variables were used prior to switching to custom resource definitions, and as such are obsolete
2017-12-04 13:25:39 -08:00
Soam Vasani b1483e5e78 minor comment update in fetcher 2017-12-01 10:45:31 -08:00
Ta-Ching ChenandSoam Vasani 43fea01bd9 Overwrite request host with internal host to prevent request rejection (#419) 2017-11-30 23:58:14 -08:00
Ta-Ching ChenandSoam Vasani 49169e672d Fix pool manager crash problem if failed at http call (#418) 2017-11-30 13:40:17 -08:00
Ta-Ching ChenandSoam Vasani 140b132c8e Update nats dependencies (#411) 2017-11-28 16:12:31 -08:00
Ta-Ching ChenandSoam Vasani 36008f28d8 Add package command (#385)
Add package command support: This PR adds package command to CLI. package provides some useful subcommands to use such as packages CRUD and display package detail information. Also it is able to reuse existing package at function creation.

* Check function existence before creating package

* Check package existence

* Add support for downloading archive from given url

The functionality was supported before e238776bf7. Add this functionality back for more flexible usage.

* Add output flag to save archive content in specific file

* Add logs when package create/update

* Fix fnCreate requires environment argument when a pkg is specified

* Fix fnUpdate failed to update function pkg info when a package is specified

* Add package command test

* Fix wrong python test image in test script

* Remove package description fields

* Fix package command not update package but creating a new one

* Set package as pending status only when there is no deploy archive

* Rename function from fetchArchiveFromArbitraryURL to downloadToTempFile

* Use io.Copy to prevent loading all body into memory

* Revise some messages

* Allow user to update package build command

* Allow user to update package content when using function update

* Retrieve pkgName from function packageref if it’s not specified

* Fix function failed to update due to resource conflict

* Fix test case failure due to single quote

* kick ci

* Fix failed to update function packageRef

* kick ci

* kick ci
2017-11-25 06:54:43 -08:00
Soam Vasani 8fc1bf4bc1 script to update and copy hugo site 2017-11-23 09:55:16 -08:00
prithvirameshandSoam Vasani 2247d3118a fission function logs returns logs in correct order now (#405)
Fix a bug in `fission function logs` due to which logs with the same timestamp were printed in the wrong order.
2017-11-22 18:05:49 -08:00
VishalandSoam Vasani 04531850d5 Executor API panics if there is err in getting function from backends (#407) 2017-11-22 14:06:36 -08:00
VishalandSoam Vasani 13a58e715d Fetcher retry (#403)
Before this change, poolmgr tried to hit fetcher just once.  However if the pod has just started, fetcher may not be up yet, causing the initial request to fail with a connection refused error.  This change adds a similar retry loop that we currently use on the specialize endpoint of the runtime environment container.
2017-11-22 11:27:37 -08:00
Erwin van EykandSoam Vasani b2678783b6 fission/builder image, log collection improvement in builder (#397)
Create a container image with the buidler, so we can avoid copying the builder binary into the env container during the env container build. 

Gather both stdout and stderr of a build into the logs.
 

* Add Dockerfile for builder + update release scripts

* Improve logging of buildermgr and builder

* Add support for environment-scoped buildcmd

* Fix fork/exec wd when package is a file

* Update tests to also include builder image

* Add logging to builder's fork/exec
2017-11-20 23:58:33 -08:00
VishalandSoam Vasani da820186f0 Executor abstraction (#384)
This change adds a layer of abstraction over poolmgr. Poolmgr is now just one of the ways to turn a function into a service; other implementations will be added. The executor abstraction is a uniform API over all these implementations.

* Executor layer added on top of pool manager

* Removed the external server for executor

* Minor changes to keep existing semantics as much possible

* Separating the executor vs. poolmgr backend functionality and associated data members

* Executor logic separated from Poolmgr backend completely, placeholder for new backend

* Changed references to poolmgr in tests

* Moved poolmgr to it's package, as a side effect moved Cache to its's package (was causing cyclical dependency) and had to make some data structures exposed outside package

* Rebased from master and changed references to tpr -> crd

* Executor layer added on top of pool manager

* Executor logic separated from Poolmgr backend completely, placeholder for new backend

* Changed podName to a generic objectReference in fscache (#391)

Changed podName to a generic objectReference in function service cache implementation.

* Moved poolmgr to it's package, as a side effect moved Cache to its's package (was causing cyclical dependency) and had to make some data structures exposed outside package

* Rebased from master and changed references to tpr -> crd

* Merged from master with latest changes

* Removed stale executor service & deployment from previous merge

* Addressed review comments, still testing some areas
2017-11-20 20:51:14 -08:00
VishalandSoam Vasani 7bb397dfee Changed podName to a generic objectReference in fscache (#391)
Changed podName to a generic objectReference in function service cache implementation.
2017-11-15 13:57:19 -08:00
Soam Vasani 8a097abf9b Chart version update 2017-11-15 13:25:28 -08:00
Soam Vasani 6d0ebfd881 update version to 0.4.0 in docs 2017-11-15 13:18:50 -08:00
Soam Vasani 310183a944 update version to 0.4.0 2017-11-15 13:18:22 -08:00
Soam Vasani 8fc3479428 only set nodeports when servicetype == nodeport 2017-11-15 13:18:22 -08:00
Josh KellyandSoam Vasani b2883a30de Added AWS to install cloud setup (#392)
Add AWS instructions to setup commands, since AWS only provides service hostname, not IP.
2017-11-15 12:49:19 -08:00
prithvirameshandSoam Vasani 8b40ad0b33 Refactor logging to remove logger, use fluentd with kubernetes filter (#380)
Remove the `logger` container from the logging daemonset. 

Remove the outgoing call from the poolmgr to the logger.  Use Fluentd's Kubernetes filter to add function name and UID to influx metadata.  

This means fluentd now figures out when to start collecting function logs on its own, without being informed by poolmgr.  This is great for other execution strategies, and for autoscaling, where fission isn't in direct control of function pod creation.

Also adds an integration test to make sure logging keeps working.
2017-11-14 18:08:57 -08:00
svicenteruizandSoam Vasani 74e05ba3a1 created weather.js in node.js examples, modified README.md (#394) 2017-11-14 15:33:41 -08:00
Cody HillandSoam Vasani e77a3946d7 Added python example to demonstrate status codes. (#395) 2017-11-14 05:51:37 -08:00
Soam Vasani bcf04b1a6c Build both python 3.5 and 2.7 images from the same env 2017-11-09 14:01:02 -08:00
Soam Vasani d8844ab13c release script: use helm package to create charts 2017-11-09 13:38:01 -08:00
Soam Vasani 2e63787b96 rename readme to format markdown 2017-11-09 08:12:47 -08:00
Soam Vasani de19a42b8b multifile example 2017-11-09 08:11:02 -08:00
Ta-Ching ChenandSoam Vasani da07b35a96 Use common controller/store for httpTriggerSet and functionReferenceResolver (#390)
This fixes a bug where functionReferenceResolver returned out-of-date function metadata and caused the router to proxy requests to old function pods.

It also uses the go context package to shutdown the controller when the router is shutting down.
2017-11-09 07:07:14 -08:00
Ta-Ching ChenandSoam Vasani 9694ba4fd1 Delete failed helm releases to prevent test case failure (#393) 2017-11-09 06:04:06 -08:00
Soam Vasani 3644760f19 hugo config 2017-11-08 17:15:54 -08:00
Soam Vasani b89576ff7d chart version update 2017-11-08 15:51:23 -08:00
Soam Vasani d4b4385596 0.4.0rc version update 2017-11-08 12:45:09 -08:00
Soam Vasani b8eb27d276 Upgrade guide doc update 2017-11-07 15:45:39 -08:00
Ta-Ching ChenandSoam Vasani a63b8b372f Builder manager bugfixes (#367)
This contains a few different builder manager fixes:

* Update the zip file structure to avoid an extra subdirectory
* Annotate packages with what functions are using them
* Only trigger builds when there is no deployment archive
* Python environment loadpath bugfixes
* Other bugfixes
2017-11-06 23:01:46 -08:00
Ta-Ching ChenandSoam Vasani 380b7d1bcd Use k8s client store to sync functions and triggers for fast synchronization (#382) 2017-11-06 12:58:19 -08:00
Ta-Ching ChenandSoam Vasani 5f14b9b0ae Switch from ThirdPartyResources to CustomResourceDefinitions (#381)
Switch Fission's storage over to the new CustomResourceDefinitions, from the deprecated ThirdPartyResources. This allows us to be compatible with Kubernets 1.8 and onwards.

This also adds a CLI tool for dumping state from an old fission version and restoring state into new CRDs.

The storage service is unaffected by this change.
2017-11-04 14:55:16 -07:00
VishalandSoam Vasani 746c51901d Test functions 236 (#355)
A single CLI command to invoke a function, print results, and get logs if the function fails. Meant to be an easy way to do testing from the CLI.
2017-10-30 22:32:15 -07:00
Ta-Ching ChenandSoam Vasani 439e7d4535 Use Kubernetes informer and cache in router (#376)
Use Kubernetes client's informer and cache to watch functions and triggers in the router. This replaces the polling loop we were using so far.
2017-10-25 15:10:44 -07:00
prithvirameshandSoam Vasani 8abb0005f6 Compile instructions: changed pullPolicy to IfNotPresent (#378)
Containers will get stuck in ErrImagePull/ImagePullBackOff state otherwise
2017-10-16 13:39:46 -07:00
Soam Vasani c8f4647373 update compiling.md to use helm 2017-10-05 16:24:25 -07:00
rapitableandSoam Vasani 5c47073518 Make default node-env use alpine. List envs in documentation. (#354)
closes #319 

* Make default node-env use alpine, and support separate debian env. List envs in documentation.

* Copy ONBUILD base image commands into node Dockerfiles

* Copy more commands from onbuild image

* Tidy dockerfiles
2017-10-04 19:05:43 -07:00
Soam Vasani fd1461cc98 doc formatting fixes 2017-10-02 14:54:40 -07:00
Ta-Ching ChenandSoam Vasani a25c167d23 Update k8s client version to 4.0.0 (#351)
Move to client-go v4.0.0 from 1.5.
2017-09-29 07:37:36 -07:00
152 changed files with 5067 additions and 3367 deletions
+7
View File
@@ -12,6 +12,13 @@ cache:
- $HOME/google-cloud-sdk/ - $HOME/google-cloud-sdk/
- $HOME/k8scli - $HOME/k8scli
services:
- docker
before_install:
- sudo apt-get update
- sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
install: install:
- go get github.com/Masterminds/glide - go get github.com/Masterminds/glide
- hack/travis-kube-setup.sh - hack/travis-kube-setup.sh
+2 -5
View File
@@ -38,13 +38,10 @@ minikube and its built-in docker daemon:
$ docker build -t minikube/fission-bundle . $ docker build -t minikube/fission-bundle .
``` ```
Next, install fission on your kubernetes cluster: Next, install fission with this image on your kubernetes cluster using the helm chart:
``` ```
# To install, update fission.yaml to point to the image you just made: "minikube/fission-bundle" $ helm install --set "image=minikube/fission-bundle,pullPolicy=IfNotPresent,analytics=false" charts/fission-all
$ $EDITOR fission.yaml
$ kubectl create -f fission.yaml
$ kubectl create -f fission-nodeport.yaml
``` ```
And if you're changing the CLI too, you can build it with: And if you're changing the CLI too, you can build it with:
+5 -5
View File
@@ -24,7 +24,7 @@ Language-neutral components:
Language-specific components: Language-specific components:
* Environment container * Environment container(s)
Controller Controller
@@ -112,13 +112,13 @@ Environment Container
--------------------- ---------------------
Environment containers run user-defined functions. Environment Environment containers run user-defined functions. Environment
containers are language specific. They must contain an HTTP server containers are language specific. Each environment container must
and a loader for functions. contain an HTTP server and a loader for functions.
Poolmgr deploys the environment container into a pod with fetcher Poolmgr deploys the environment container into a pod with fetcher
(fetcher is a simple utility that can fetch an HTTP url to a file at a (fetcher is a simple utility that can fetch an HTTP url to a file at a
configured location). This pod forms a "generic pod", because it can configured location). This pod forms a "generic pod", because it can
be loaded with any function. be loaded with any function in that language.
When poolmgr needs to create a service for a function, it calls When poolmgr needs to create a service for a function, it calls
fetcher to fetch the function. Fetcher downloads the function into a fetcher to fetch the function. Fetcher downloads the function into a
@@ -134,7 +134,7 @@ Following is a diagram describe how log service works:
![Logger Diagram](https://cloud.githubusercontent.com/assets/202578/23100399/b0e3ea00-f6ba-11e6-8f2f-6588cfef2e84.png) ![Logger Diagram](https://cloud.githubusercontent.com/assets/202578/23100399/b0e3ea00-f6ba-11e6-8f2f-6588cfef2e84.png)
1. Pool manager choose a pod from pool to execute user function 1. Pool manager chooses a pod from the pool to execute user function
2. Pool manager makes a HTTP POST to logger helper, once helper receives 2. Pool manager makes a HTTP POST to logger helper, once helper receives
the request it creates a symlink to container log for fluentd. the request it creates a symlink to container log for fluentd.
3. Fluentd reads log from symlink and pipes to influxdb 3. Fluentd reads log from symlink and pipes to influxdb
+1 -1
View File
@@ -10,7 +10,7 @@
## Fission API ([area-api](https://github.com/fission/fission/labels/area-api)) ## Fission API ([area-api](https://github.com/fission/fission/labels/area-api))
- TPR-based controller - CRD-based controller
- API authentication - API authentication
- Aggregated API server - Aggregated API server
+2 -1
View File
@@ -1,3 +1,4 @@
baseURL = "http://fission.io/docs/v0.2.1" baseURL = "http://fission.io/docs/0.3.0"
languageCode = "en-us" languageCode = "en-us"
title = "Fission: Serverless Functions for Kubernetes" title = "Fission: Serverless Functions for Kubernetes"
theme = "docdock"
+3 -3
View File
@@ -1,6 +1,6 @@
## Fission Documentation ## Fission Documentation
* [Installation guide](install)
* [Upgrade guide for v0.3 to v0.4](upgrade-from-v0.3)
[Installation guide](install)
[Upgrade guide](upgrade-from-v0.1)
+20 -22
View File
@@ -6,7 +6,7 @@ draft: false
Welcome! This guide will get you up and running with Fission on a Welcome! This guide will get you up and running with Fission on a
Kubernetes cluster. Kubernetes cluster.
### Cluster preliminaries ### Cluster preliminaries
If you don't have a Kubernetes cluster, [here's a quick guide to set If you don't have a Kubernetes cluster, [here's a quick guide to set
@@ -48,18 +48,18 @@ First, you'll need the helm CLI:
On __OS X__: On __OS X__:
``` ```
$ curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.6.1-darwin-amd64.tar.gz $ curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.7.0-darwin-amd64.tar.gz
$ tar xzf helm-v2.6.1-darwin-amd64.tar.gz $ tar xzf helm-v2.7.0-darwin-amd64.tar.gz
$ mv darwin-amd64/helm /usr/local/bin $ mv darwin-amd64/helm /usr/local/bin
``` ```
On __Linux__: On __Linux__:
``` ```
$ curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.6.1-linux-amd64.tar.gz $ curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.7.0-linux-amd64.tar.gz
$ tar xzf helm-v2.6.1-linux-amd64.tar.gz $ tar xzf helm-v2.7.0-linux-amd64.tar.gz
$ mv linux-amd64/helm /usr/local/bin $ mv linux-amd64/helm /usr/local/bin
``` ```
@@ -67,24 +67,15 @@ $ mv linux-amd64/helm /usr/local/bin
Next, install the Helm server on your Kubernetes cluster: Next, install the Helm server on your Kubernetes cluster:
``` ```
$ kubectl -n kube-system create sa tiller $ helm init
$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
$ helm init --service-account tiller
``` ```
(The first two commands are there to make sure that helm is allowed to
install stuff on Kubernetes, in the common case that your cluster has
role-based access control.)
### Install Fission ### Install Fission
#### Minikube #### Minikube
``` ```
$ helm install --namespace fission --set serviceType=NodePort https://github.com/fission/fission/releases/download/0.3.0/fission-all-0.3.0.tgz $ helm install --namespace fission --set serviceType=NodePort https://github.com/fission/fission/releases/download/0.4.1/fission-all-0.4.1.tgz
``` ```
The serviceType variable allows configuring the type of Kubernetes The serviceType variable allows configuring the type of Kubernetes
@@ -94,7 +85,7 @@ want to expose anything outside the cluster.
#### Cloud hosted clusters (GKE, AWS, Azure etc.) #### Cloud hosted clusters (GKE, AWS, Azure etc.)
``` ```
$ helm install --namespace fission https://github.com/fission/fission/releases/download/0.3.0/fission-all-0.3.0.tgz $ helm install --namespace fission https://github.com/fission/fission/releases/download/0.4.1/fission-all-0.4.1.tgz
``` ```
#### Minimal version #### Minimal version
@@ -104,7 +95,7 @@ the NATS message queue, influxDB for logs, etc. If you want a more
minimal setup, you can install the fission-core chart instead: minimal setup, you can install the fission-core chart instead:
``` ```
$ helm install --namespace fission https://github.com/fission/fission/releases/download/0.3.0/fission-core-0.3.0.tgz $ helm install --namespace fission https://github.com/fission/fission/releases/download/0.4.1/fission-core-0.4.1.tgz
``` ```
### Install the Fission CLI ### Install the Fission CLI
@@ -114,19 +105,19 @@ $ helm install --namespace fission https://github.com/fission/fission/releases/d
Get the CLI binary for Mac: Get the CLI binary for Mac:
``` ```
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.3.0/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.4.1/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
``` ```
#### Linux #### Linux
``` ```
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.3.0/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.4.1/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/0.3.0/fission-cli-windows.exe) this windows executable: [fission.exe](https://github.com/fission/fission/releases/download/0.4.1/fission-cli-windows.exe)
### Set environment vars ### Set environment vars
@@ -149,6 +140,13 @@ FISSION_URL and FISSION_ROUTER, respectively. Wait for services to
get IP addresses (check this with ```kubectl --namespace fission get get IP addresses (check this with ```kubectl --namespace fission get
svc```). Then: svc```). Then:
##### AWS
```
$ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..hostname}')
$ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..hostname}')
```
##### GCP
``` ```
$ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..ip}') $ 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}') $ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}')
@@ -159,7 +157,7 @@ svc```). Then:
Finally, you're ready to use Fission! Finally, you're ready to use Fission!
``` ```
$ fission env create --name nodejs --image fission/node-env:0.3.0 $ fission env create --name nodejs --image fission/node-env:0.4.1
$ curl -LO https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js $ curl -LO https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js
@@ -0,0 +1,131 @@
---
title: "Upgrading from Fission v0.3 to v0.4.x"
date: 2017-11-04T03:38:29+08:00
draft: false
---
## Introduction
Kubernetes ThirdPartyResources ("TPR") are replaced by
CustomResourceDefinitions ("CRD"). TPRs have been deprecated and are
removed in Kubernetes 1.8.
Since Fission stores state in TPRs, we need to migrate this state from
TPRs to CRDs while upgrading.
Follow the instructions below if you're upgrading a Fission 0.2.1 or
0.3.0 cluster to 0.4. If you're using a pre-0.2 Fission cluster, use
the [upgrade guide from 0.1 to 0.2]() and then upgrade to 0.4.0.
## How to Upgrade
1. Get the 0.4.0 CLI
2. Get the Fission state from v0.3 install
3. Upgrade to Fission 0.4.0
4. Upgrade Kubernetes cluster version to 1.7.x or higher
5. Remove all TPR definition (for Kubernetes 1.7.x)
6. Restore Fission state into CRDs
### Get the new CLI
#### OS X
```
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.4.0/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
```
#### Linux
```
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.4.0/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
```
#### Windows
For Windows, you can use the linux binary on WSL. Or you can download
this windows executable: [fission.exe](https://github.com/fission/fission/releases/download/0.4.0/fission-cli-windows.exe)
### Get Fission state from v0.3 install
```
fission --server <your v0.3 server> tpr2crd dump --file state.json
```
You can skip the --server argument if you have the environment
variable `$FISSION_URL` set to point at a v0.3 Fission server.
This will create a JSON file with all your fission state in the
current directory.
### Upgrade to Fission 0.4.0
Upgrade fission with a command similar to this:
```
helm upgrade fission-all --namespace fission
```
### Upgrade Kubernetes cluster version
Since CustomResource is only supported on Kubernetes v1.7+ and higher, please make sure
that you upgrade to the right version that supports CustomResource.
### Remove all TPR definition (for Kubernetes 1.7.x)
** NOTICE **: This step will remove TPR definition from your kubernetes cluster. Please make sure that you dump all TPRs at the second step!
Though Kubernetes will migrate TPRs to CRDs automatically when TPR definition is deleted if the same name CRD exists. We still need to make sure that there is no resource gets lost during the migration. Also, since we changed the capitalization of some CRDs to CamelCase (e.g. Httptrigger -> HTTPTrigger), we need to recreate those resources by ourselves.
```
fission tpr2crd delete
```
### Restore your Fission state into Fission 0.4.0
```
fission tpr2crd restore --file state.json
```
This commands needs `$FISSION_URL` set to point to new fission installation.
It uses the file created in the first step. It doesn't modify state.json.
(Note that you can run this restore on any cluster; it doesn't have the be the same kubernetes
cluster as your old install.)
### Verify
Let's check the migration result, first run following command to check CRD established state.
```
kubectl get crd -o 'custom-columns=NAME:{.metadata.name},ESTABLISHED:{.status.conditions[?(@.type=="Established")].status}'
```
The output should be like this
```
NAME ESTABLISHED
environments.fission.io True
functions.fission.io True
httptriggers.fission.io True
kuberneteswatchtriggers.fission.io True
messagequeuetriggers.fission.io True
packages.fission.io True
timetriggers.fission.io True
```
And check that CRD resources you expect are there.
```
COMMAND:
fission [resource] list
RESOURCES:
environments
functions
httptriggers
kuberneteswatchtriggers
messagequeuetriggers
packages
timetriggers
```
+19 -3
View File
@@ -45,8 +45,24 @@ A _function_ is a piece of code that follows the fission function
interface. interface.
An _environment_ contains the language- and runtime-specific parts of An _environment_ contains the language- and runtime-specific parts of
running a function. Fission comes with NodeJS and Python running a function.
environments; you can also extend environments or create entirely new
The following environments are currently available:
| Environment | Image |
| ------------------------------------ | ------------------------- |
| Binary (for executables or scripts) | `fission/binary-env` |
| Go | `fission/go-env` |
| .NET | `fission/dotnet-env` |
| .NET 2.0 | `fission/dotnet20-env` |
| NodeJS (Alpine) | `fission/node-env` |
| NodeJS (Debian) | `fission/node-env-debian` |
| Perl | `fission/perl-env` |
| PHP 7 | `fission/php-env` |
| Python 3 | `fission/python-env` |
| Ruby | `fission/ruby-env` |
You can also extend environments or create entirely new
ones if you want. (An environment is essentially just a container ones if you want. (An environment is essentially just a container
with a webserver and dynamic loader.) with a webserver and dynamic loader.)
@@ -81,7 +97,7 @@ See the [examples](examples) directory for more.
Running Fission on your Cluster Running Fission on your Cluster
=============================== ===============================
See the [installation guide](http://fission.io/docs/v0.2.1/install/). See the [installation guide](http://fission.io/docs/0.4.0/install/).
Compiling Fission Compiling Fission
================= =================
+59 -19
View File
@@ -21,11 +21,13 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@@ -68,7 +70,9 @@ func MakeBuilder(sharedVolumePath string) *Builder {
func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) { func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" { if r.Method != "POST" {
http.Error(w, "", 405) e := fmt.Sprintf("Method not allowed: %v", r.Method)
log.Println(e)
builder.reply(w, "", e, http.StatusMethodNotAllowed)
return return
} }
@@ -81,15 +85,17 @@ func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) {
// parse request // parse request
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
log.Printf("Error reading request body") e := errors.New(fmt.Sprintf("Error reading request body: %v", err))
http.Error(w, err.Error(), 500) log.Println(e.Error())
builder.reply(w, "", e.Error(), http.StatusInternalServerError)
return return
} }
var req PackageBuildRequest var req PackageBuildRequest
err = json.Unmarshal(body, &req) err = json.Unmarshal(body, &req)
if err != nil { if err != nil {
log.Printf("Error parsing json body: %v", err) e := errors.New(fmt.Sprintf("Error parsing json body: %v", err))
http.Error(w, err.Error(), 400) log.Println(e.Error())
builder.reply(w, "", e.Error(), http.StatusBadRequest)
return return
} }
log.Printf("Builder received request: %v", req) log.Printf("Builder received request: %v", req)
@@ -106,66 +112,100 @@ func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) {
buildLogs, err := builder.build(buildCmd, srcPkgPath, deployPkgPath) buildLogs, err := builder.build(buildCmd, srcPkgPath, deployPkgPath)
if err != nil { if err != nil {
e := errors.New(fmt.Sprintf("Error building source package: %v", err)) e := errors.New(fmt.Sprintf("Error building source package: %v", err))
http.Error(w, e.Error(), 500) log.Println(e.Error())
// append error at the end of build logs
buildLogs += fmt.Sprintf("%v\n", e.Error())
builder.reply(w, deployPkgFilename, buildLogs, http.StatusInternalServerError)
return return
} }
builder.reply(w, deployPkgFilename, buildLogs, http.StatusOK)
}
func (builder *Builder) reply(w http.ResponseWriter, pkgFilename string, buildLogs string, statusCode int) {
resp := PackageBuildResponse{ resp := PackageBuildResponse{
ArtifactFilename: deployPkgFilename, ArtifactFilename: pkgFilename,
BuildLogs: buildLogs, BuildLogs: buildLogs,
} }
rBody, err := json.Marshal(resp) rBody, err := json.Marshal(resp)
if err != nil { if err != nil {
e := errors.New(fmt.Sprintf("Error encoding response body: %v", err)) e := errors.New(fmt.Sprintf("Error encoding response body: %v", err))
http.Error(w, e.Error(), 500) rBody = []byte(fmt.Sprintf(`{"buildLogs": "%v"}`, e.Error()))
return statusCode = http.StatusInternalServerError
} }
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
// should write header before writing the body,
// or client will receive HTTP 200 regardless the real status code
w.WriteHeader(statusCode)
w.Write(rBody) w.Write(rBody)
w.WriteHeader(http.StatusOK)
} }
func (builder *Builder) build(command string, srcPkgPath string, deployPkgPath string) (string, error) { func (builder *Builder) build(command string, srcPkgPath string, deployPkgPath string) (string, error) {
cmd := exec.Command(command) cmd := exec.Command(command)
cmd.Dir = srcPkgPath
fi, err := os.Stat(srcPkgPath)
if err != nil {
return "", errors.New(fmt.Sprintf("could not find srcPkgPath: '%s'", srcPkgPath))
}
if fi.IsDir() {
cmd.Dir = srcPkgPath
} else {
cmd.Dir = path.Dir(srcPkgPath)
}
// set env variables for build command // set env variables for build command
cmd.Env = append(os.Environ(), cmd.Env = append(os.Environ(),
fmt.Sprintf("%v=%v", envSrcPkg, srcPkgPath), fmt.Sprintf("%v=%v", envSrcPkg, srcPkgPath),
fmt.Sprintf("%v=%v", envDeployPkg, deployPkgPath), fmt.Sprintf("%v=%v", envDeployPkg, deployPkgPath),
) )
cmdReader, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
return "", errors.New(fmt.Sprintf("Error creating stdout pipe for cmd: %v", err.Error())) return "", errors.New(fmt.Sprintf("Error creating stdout pipe for cmd: %v", err.Error()))
} }
scanner := bufio.NewScanner(cmdReader) stderr, err := cmd.StderrPipe()
if err != nil {
return "", errors.New(fmt.Sprintf("Error creating stderr pipe for cmd: %v", err.Error()))
}
var buildLogs string
fmt.Println("\n=== Build Logs ===")
// Init logs
fmt.Printf("command=%v\n", command)
fmt.Printf("env=%v\n", cmd.Env)
out := io.MultiReader(stdout, stderr)
scanner := bufio.NewScanner(out)
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
return "", errors.New(fmt.Sprintf("Error starting cmd: %v", err.Error())) return "", errors.New(fmt.Sprintf("Error starting cmd: %v", err.Error()))
} }
var buildLogs string // Runtime logs
fmt.Println("\n=== Build Logs ===")
for scanner.Scan() { for scanner.Scan() {
output := scanner.Text() output := scanner.Text()
fmt.Println(output) fmt.Println(output)
buildLogs += fmt.Sprintf("%v\n", output) buildLogs += fmt.Sprintf("%v\n", output)
} }
fmt.Println("==================\n")
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return "", errors.New(fmt.Sprintf("Error reading cmd output: %v", err.Error())) scanErr := errors.New(fmt.Sprintf("Error reading cmd output: %v", err.Error()))
fmt.Println(scanErr)
return buildLogs, scanErr
} }
err = cmd.Wait() err = cmd.Wait()
if err != nil { if err != nil {
return "", errors.New(fmt.Sprintf("Error waiting for cmd: %v", err.Error())) cmdErr := errors.New(fmt.Sprintf("Error waiting for cmd '%v': %v", command, err.Error()))
fmt.Println(cmdErr)
return buildLogs, cmdErr
} }
fmt.Println("==================\n")
return buildLogs, nil return buildLogs, nil
} }
+4 -5
View File
@@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"strings" "strings"
@@ -50,20 +51,18 @@ func (c *Client) Build(req *builder.PackageBuildRequest) (*builder.PackageBuildR
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fission.MakeErrorFromHTTP(resp)
}
rBody, err := ioutil.ReadAll(resp.Body) rBody, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Printf("Error reading resp body: %v", err)
return nil, err return nil, err
} }
pkgBuildResp := builder.PackageBuildResponse{} pkgBuildResp := builder.PackageBuildResponse{}
err = json.Unmarshal([]byte(rBody), &pkgBuildResp) err = json.Unmarshal([]byte(rBody), &pkgBuildResp)
if err != nil { if err != nil {
log.Printf("Error parsing resp body: %v", err)
return nil, err return nil, err
} }
return &pkgBuildResp, nil return &pkgBuildResp, fission.MakeErrorFromHTTP(resp)
} }
+9
View File
@@ -0,0 +1,9 @@
FROM alpine:3.5
RUN apk update
# Install some utility libraries for builders extending image
RUN apk add coreutils binutils findutils grep
ADD builder /builder
EXPOSE 8001
+24 -17
View File
@@ -26,41 +26,39 @@ import (
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"k8s.io/client-go/1.5/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api" "k8s.io/client-go/kubernetes"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
type ( type (
BuildRequest struct { BuildRequest struct {
Package api.ObjectMeta `json:"package"` Package metav1.ObjectMeta `json:"package"`
} }
BuilderMgr struct { BuilderMgr struct {
fissionClient *tpr.FissionClient fissionClient *crd.FissionClient
kubernetesClient *kubernetes.Clientset storageSvcUrl string
storageSvcUrl string namespace string
namespace string
} }
) )
func MakeBuilderMgr(fissionClient *tpr.FissionClient, func MakeBuilderMgr(fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, storageSvcUrl string, kubernetesClient *kubernetes.Clientset, storageSvcUrl string,
envBuilderNamespace string) *BuilderMgr { envBuilderNamespace string) *BuilderMgr {
envWatcher := makeEnvironmentWatcher(fissionClient, kubernetesClient, envBuilderNamespace) envWatcher := makeEnvironmentWatcher(fissionClient, kubernetesClient, envBuilderNamespace)
go envWatcher.watchEnvironments() go envWatcher.watchEnvironments()
pkgWatcher := makePackageWatcher(fissionClient, kubernetesClient, envBuilderNamespace, storageSvcUrl) pkgWatcher := makePackageWatcher(fissionClient, envBuilderNamespace, storageSvcUrl)
go pkgWatcher.watchPackages() go pkgWatcher.watchPackages()
return &BuilderMgr{ return &BuilderMgr{
fissionClient: fissionClient, fissionClient: fissionClient,
kubernetesClient: kubernetesClient, storageSvcUrl: storageSvcUrl,
storageSvcUrl: storageSvcUrl, namespace: envBuilderNamespace,
namespace: envBuilderNamespace,
} }
} }
@@ -76,14 +74,23 @@ func (builderMgr *BuilderMgr) build(w http.ResponseWriter, r *http.Request) {
buildReq := BuildRequest{} buildReq := BuildRequest{}
err = json.Unmarshal([]byte(body), &buildReq) err = json.Unmarshal([]byte(body), &buildReq)
if err != nil { if err != nil {
e := fmt.Sprintf("invalid request body: %v", err) e := fmt.Sprintf("Invalid request body: %v", err)
log.Println(e) log.Println(e)
http.Error(w, e, 400) http.Error(w, e, 400)
return return
} }
buildLogs, err := buildPackage(builderMgr.fissionClient, builderMgr.kubernetesClient, pkg, err := builderMgr.fissionClient.
builderMgr.namespace, builderMgr.storageSvcUrl, buildReq) Packages(buildReq.Package.Namespace).
Get(buildReq.Package.Name)
if err != nil {
e := fmt.Sprintf("Error getting package CRD info: %v", err)
log.Println(e)
http.Error(w, e, 500)
return
}
buildLogs, err := buildPackage(builderMgr.fissionClient, builderMgr.namespace, builderMgr.storageSvcUrl, pkg)
if err != nil { if err != nil {
code, e := fission.GetHTTPError(err) code, e := fission.GetHTTPError(err)
http.Error(w, e, code) http.Error(w, e, code)
+2 -2
View File
@@ -19,12 +19,12 @@ package buildermgr
import ( import (
"log" "log"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
// Start the buildermgr service. // Start the buildermgr service.
func Start(port int, storageSvcUrl string, envBuilderNamespace string) error { func Start(port int, storageSvcUrl string, envBuilderNamespace string) error {
fissionClient, kubernetesClient, err := tpr.MakeFissionClient() fissionClient, kubernetesClient, _, err := crd.MakeFissionClient()
if err != nil { if err != nil {
log.Printf("Failed to get kubernetes client: %v", err) log.Printf("Failed to get kubernetes client: %v", err)
return err return err
+52 -46
View File
@@ -19,18 +19,18 @@ package buildermgr
import ( import (
"fmt" "fmt"
"log" "log"
"net/http"
"strings" "strings"
"k8s.io/client-go/1.5/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api"
"github.com/dchest/uniuri" "github.com/dchest/uniuri"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/builder" "github.com/fission/fission/builder"
builderClient "github.com/fission/fission/builder/client" builderClient "github.com/fission/fission/builder/client"
"github.com/fission/fission/crd"
"github.com/fission/fission/environments/fetcher" "github.com/fission/fission/environments/fetcher"
fetcherClient "github.com/fission/fission/environments/fetcher/client" fetcherClient "github.com/fission/fission/environments/fetcher/client"
"github.com/fission/fission/tpr"
) )
// buildPackage helps to build source package into deployment package. // buildPackage helps to build source package into deployment package.
@@ -43,41 +43,39 @@ import (
// 6. Update package status to succeed state // 6. Update package status to succeed state
// 7. Update package resource in package ref of functions that share the same package // 7. Update package resource in package ref of functions that share the same package
// *. Update package status to failed state,if any one of steps above failed // *. Update package status to failed state,if any one of steps above failed
func buildPackage(fissionClient *tpr.FissionClient, kubernetesClient *kubernetes.Clientset, func buildPackage(fissionClient *crd.FissionClient, builderNamespace string,
builderNamespace string, storageSvcUrl string, buildReq BuildRequest) (buildLogs string, err error) { storageSvcUrl string, pkg *crd.Package) (buildLogs string, err error) {
pkg, err := fissionClient.Packages(
buildReq.Package.Namespace).Get(buildReq.Package.Name)
if err != nil {
e := fmt.Sprintf("Error getting function TPR info: %v", err)
log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil)
return e, fission.MakeError(500, e)
}
// Only do build for pending packages // Only do build for pending packages
if pkg.Status.BuildStatus != fission.BuildStatusPending { if pkg.Status.BuildStatus != fission.BuildStatusPending {
e := "package is not in pending state" e := "package is not in pending state"
log.Println(e) log.Println(e)
return e, fission.MakeError(400, e) return e, fission.MakeError(http.StatusBadRequest, e)
} }
// update package status to running state, so that // update package status to running state, so that
// we can know what status a package is through cli. // we can know what status a package is through cli.
_, err = updatePackage(fissionClient, pkg, fission.BuildStatusRunning, "", nil) newPkgRV, err := updatePackage(fissionClient, pkg, fission.BuildStatusRunning, "", nil)
// Kubernetes checks resource version before applying
// new resource config. The update operation will be
// rejected if the resource version in metadata is lower
// than the latest version. Set resource version with
// latest return from updatePackage.
pkg.Metadata.ResourceVersion = newPkgRV
if err != nil { if err != nil {
e := fmt.Sprintf("Error setting package pending state: %v", err) e := fmt.Sprintf("Error setting package pending state: %v", err)
log.Println(e) log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil) updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil)
return e, fission.MakeError(500, e) return e, fission.MakeError(http.StatusInternalServerError, e)
} }
env, err := fissionClient.Environments(api.NamespaceDefault).Get(pkg.Spec.Environment.Name) env, err := fissionClient.Environments(metav1.NamespaceDefault).Get(pkg.Spec.Environment.Name)
if err != nil { if err != nil {
e := fmt.Sprintf("Error getting environment TPR info: %v", err) e := fmt.Sprintf("Error getting environment CRD info: %v", err)
log.Println(e) log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil) updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil)
return e, fission.MakeError(500, e) return e, fission.MakeError(http.StatusInternalServerError, e)
} }
svcName := fmt.Sprintf("%v-%v.%v", env.Metadata.Name, env.Metadata.ResourceVersion, builderNamespace) svcName := fmt.Sprintf("%v-%v.%v", env.Metadata.Name, env.Metadata.ResourceVersion, builderNamespace)
@@ -97,12 +95,17 @@ func buildPackage(fissionClient *tpr.FissionClient, kubernetesClient *kubernetes
e := fmt.Sprintf("Error fetching source package: %v", err) e := fmt.Sprintf("Error fetching source package: %v", err)
log.Println(e) log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil) updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil)
return e, fission.MakeError(500, e) return e, fission.MakeError(http.StatusInternalServerError, e)
}
buildCmd := pkg.Spec.BuildCommand
if len(buildCmd) == 0 {
buildCmd = env.Spec.Builder.Command
} }
pkgBuildReq := &builder.PackageBuildRequest{ pkgBuildReq := &builder.PackageBuildRequest{
SrcPkgFilename: srcPkgFilename, SrcPkgFilename: srcPkgFilename,
BuildCommand: pkg.Spec.BuildCommand, BuildCommand: buildCmd,
} }
log.Printf("Start building with source package: %v", srcPkgFilename) log.Printf("Start building with source package: %v", srcPkgFilename)
@@ -111,8 +114,13 @@ func buildPackage(fissionClient *tpr.FissionClient, kubernetesClient *kubernetes
if err != nil { if err != nil {
e := fmt.Sprintf("Error building deployment package: %v", err) e := fmt.Sprintf("Error building deployment package: %v", err)
log.Println(e) log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil) var buildLogs string
return e, fission.MakeError(500, e) if buildResp != nil {
buildLogs = buildResp.BuildLogs
}
buildLogs += fmt.Sprintf("%v\n", e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, buildLogs, nil)
return e, fission.MakeError(http.StatusInternalServerError, e)
} }
log.Printf("Build succeed, source package: %v, deployment package: %v", srcPkgFilename, buildResp.ArtifactFilename) log.Printf("Build succeed, source package: %v, deployment package: %v", srcPkgFilename, buildResp.ArtifactFilename)
@@ -128,28 +136,32 @@ func buildPackage(fissionClient *tpr.FissionClient, kubernetesClient *kubernetes
if err != nil { if err != nil {
e := fmt.Sprintf("Error uploading deployment package: %v", err) e := fmt.Sprintf("Error uploading deployment package: %v", err)
log.Println(e) log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil) buildResp.BuildLogs += fmt.Sprintf("%v\n", e)
return e, fission.MakeError(500, e) updatePackage(fissionClient, pkg, fission.BuildStatusFailed, buildResp.BuildLogs, nil)
return e, fission.MakeError(http.StatusInternalServerError, e)
} }
log.Printf("Start updating info of package: %v", pkg.Metadata.Name) log.Printf("Start updating info of package: %v", pkg.Metadata.Name)
// update package status and also build logs // update package status and also build logs
newPkgRV, err := updatePackage(fissionClient, pkg, newPkgRV, err = updatePackage(fissionClient, pkg,
fission.BuildStatusSucceeded, buildResp.BuildLogs, uploadResp) fission.BuildStatusSucceeded, buildResp.BuildLogs, uploadResp)
pkg.Metadata.ResourceVersion = newPkgRV
if err != nil { if err != nil {
e := fmt.Sprintf("Error creating deployment package TPR resource: %v", err) e := fmt.Sprintf("Error creating deployment package CRD resource: %v", err)
log.Println(e) log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil) buildResp.BuildLogs += fmt.Sprintf("%v\n", e)
return e, fission.MakeError(500, e) updatePackage(fissionClient, pkg, fission.BuildStatusFailed, buildResp.BuildLogs, nil)
return e, fission.MakeError(http.StatusInternalServerError, e)
} }
fnList, err := fissionClient. fnList, err := fissionClient.
Functions(api.NamespaceDefault).List(api.ListOptions{}) Functions(metav1.NamespaceDefault).List(metav1.ListOptions{})
if err != nil { if err != nil {
e := fmt.Sprintf("Error getting function list: %v", err) e := fmt.Sprintf("Error getting function list: %v", err)
log.Println(e) log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil) buildResp.BuildLogs += fmt.Sprintf("%v\n", e)
return e, fission.MakeError(500, e) updatePackage(fissionClient, pkg, fission.BuildStatusFailed, buildResp.BuildLogs, nil)
return e, fission.MakeError(http.StatusInternalServerError, e)
} }
// A package may be used by multiple functions. Update // A package may be used by multiple functions. Update
@@ -159,13 +171,14 @@ func buildPackage(fissionClient *tpr.FissionClient, kubernetesClient *kubernetes
fn.Spec.Package.PackageRef.Namespace == pkg.Metadata.Namespace && fn.Spec.Package.PackageRef.Namespace == pkg.Metadata.Namespace &&
fn.Spec.Package.PackageRef.ResourceVersion != pkg.Metadata.ResourceVersion { fn.Spec.Package.PackageRef.ResourceVersion != pkg.Metadata.ResourceVersion {
fn.Spec.Package.PackageRef.ResourceVersion = newPkgRV fn.Spec.Package.PackageRef.ResourceVersion = newPkgRV
// update TPR // update CRD
_, err = fissionClient.Functions(fn.Metadata.Namespace).Update(&fn) _, err = fissionClient.Functions(fn.Metadata.Namespace).Update(&fn)
if err != nil { if err != nil {
e := fmt.Sprintf("Error updating function package resource version: %v", err) e := fmt.Sprintf("Error updating function package resource version: %v", err)
log.Println(e) log.Println(e)
updatePackage(fissionClient, pkg, fission.BuildStatusFailed, e, nil) buildResp.BuildLogs += fmt.Sprintf("%v\n", e)
return e, fission.MakeError(500, e) updatePackage(fissionClient, pkg, fission.BuildStatusFailed, buildResp.BuildLogs, nil)
return e, fission.MakeError(http.StatusInternalServerError, e)
} }
} }
} }
@@ -175,17 +188,10 @@ func buildPackage(fissionClient *tpr.FissionClient, kubernetesClient *kubernetes
return buildResp.BuildLogs, nil return buildResp.BuildLogs, nil
} }
func updatePackage(fissionClient *tpr.FissionClient, func updatePackage(fissionClient *crd.FissionClient,
pkg *tpr.Package, status fission.BuildStatus, buildLogs string, pkg *crd.Package, status fission.BuildStatus, buildLogs string,
uploadResp *fetcher.UploadResponse) (string, error) { uploadResp *fetcher.UploadResponse) (string, error) {
// Kubernetes checks resource version before applying
// new resource config. The update operation will be
// rejected if the resource version in metadata is lower
// than the latest version. Set resource version empty
// string to skip resource version check.
pkg.Metadata.ResourceVersion = ""
pkg.Status = fission.PackageStatus{ pkg.Status = fission.PackageStatus{
BuildStatus: status, BuildStatus: status,
BuildLog: buildLogs, BuildLog: buildLogs,
@@ -200,7 +206,7 @@ func updatePackage(fissionClient *tpr.FissionClient,
} }
// update package spec // update package spec
pkg, err := fissionClient.Packages(api.NamespaceDefault).Update(pkg) pkg, err := fissionClient.Packages(metav1.NamespaceDefault).Update(pkg)
if err != nil { if err != nil {
log.Printf("Error updating package: %v", err) log.Printf("Error updating package: %v", err)
return "", err return "", err
+70 -60
View File
@@ -22,15 +22,15 @@ import (
"os" "os"
"time" "time"
"k8s.io/client-go/1.5/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api" "k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/1.5/pkg/api/v1" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/pkg/labels" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/1.5/pkg/util/intstr" apiv1 "k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/1.5/pkg/watch" "k8s.io/client-go/pkg/apis/extensions/v1beta1"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
type requestType int type requestType int
@@ -45,15 +45,15 @@ const (
type ( type (
builderInfo struct { builderInfo struct {
envMetadata *api.ObjectMeta envMetadata *metav1.ObjectMeta
deployment *v1beta1.Deployment deployment *v1beta1.Deployment
service *v1.Service service *apiv1.Service
} }
envwRequest struct { envwRequest struct {
requestType requestType
env *tpr.Environment env *crd.Environment
envList []tpr.Environment envList []crd.Environment
respChan chan envwResponse respChan chan envwResponse
} }
@@ -66,14 +66,14 @@ type (
cache map[string]*builderInfo cache map[string]*builderInfo
requestChan chan envwRequest requestChan chan envwRequest
builderNamespace string builderNamespace string
fissionClient *tpr.FissionClient fissionClient *crd.FissionClient
kubernetesClient *kubernetes.Clientset kubernetesClient *kubernetes.Clientset
fetcherImage string fetcherImage string
fetcherImagePullPolicy v1.PullPolicy fetcherImagePullPolicy apiv1.PullPolicy
} }
) )
func makeEnvironmentWatcher(fissionClient *tpr.FissionClient, func makeEnvironmentWatcher(fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, builderNamespace string) *environmentWatcher { kubernetesClient *kubernetes.Clientset, builderNamespace string) *environmentWatcher {
fetcherImage := os.Getenv("FETCHER_IMAGE") fetcherImage := os.Getenv("FETCHER_IMAGE")
@@ -86,14 +86,14 @@ func makeEnvironmentWatcher(fissionClient *tpr.FissionClient,
fetcherImagePullPolicy = "IfNotPresent" fetcherImagePullPolicy = "IfNotPresent"
} }
var pullPolicy v1.PullPolicy var pullPolicy apiv1.PullPolicy
switch fetcherImagePullPolicy { switch fetcherImagePullPolicy {
case "Always": case "Always":
pullPolicy = v1.PullAlways pullPolicy = apiv1.PullAlways
case "Never": case "Never":
pullPolicy = v1.PullNever pullPolicy = apiv1.PullNever
default: default:
pullPolicy = v1.PullIfNotPresent pullPolicy = apiv1.PullIfNotPresent
} }
envWatcher := &environmentWatcher{ envWatcher := &environmentWatcher{
@@ -125,7 +125,7 @@ func (envw *environmentWatcher) getLabels(envName string, envResourceVersion str
func (envw *environmentWatcher) watchEnvironments() { func (envw *environmentWatcher) watchEnvironments() {
rv := "" rv := ""
for { for {
wi, err := envw.fissionClient.Environments(api.NamespaceAll).Watch(api.ListOptions{ wi, err := envw.fissionClient.Environments(metav1.NamespaceAll).Watch(metav1.ListOptions{
ResourceVersion: rv, ResourceVersion: rv,
}) })
if err != nil { if err != nil {
@@ -144,7 +144,7 @@ func (envw *environmentWatcher) watchEnvironments() {
time.Sleep(time.Second) time.Sleep(time.Second)
break break
} }
env := ev.Object.(*tpr.Environment) env := ev.Object.(*crd.Environment)
rv = env.Metadata.ResourceVersion rv = env.Metadata.ResourceVersion
envw.sync() envw.sync()
} }
@@ -152,9 +152,9 @@ func (envw *environmentWatcher) watchEnvironments() {
} }
func (envw *environmentWatcher) sync() { func (envw *environmentWatcher) sync() {
envList, err := envw.fissionClient.Environments(api.NamespaceAll).List(api.ListOptions{}) envList, err := envw.fissionClient.Environments(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
log.Fatalf("Error syncing environment TPR resources: %v", err) log.Fatalf("Error syncing environment CRD resources: %v", err)
} }
// Create environment builders for all environments // Create environment builders for all environments
@@ -191,7 +191,7 @@ func (envw *environmentWatcher) service() {
req.respChan <- envwResponse{builderInfo: builderInfo} req.respChan <- envwResponse{builderInfo: builderInfo}
case CLEANUP_BUILDERS: case CLEANUP_BUILDERS:
latestEnvList := make(map[string]*tpr.Environment) latestEnvList := make(map[string]*crd.Environment)
for i := range req.envList { for i := range req.envList {
env := req.envList[i] env := req.envList[i]
key := envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion) key := envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion)
@@ -201,7 +201,7 @@ func (envw *environmentWatcher) service() {
// If an environment is deleted when builder manager down, // If an environment is deleted when builder manager down,
// the builder belongs to the environment will be out-of- // the builder belongs to the environment will be out-of-
// control (an orphan builder) since there is no record in // control (an orphan builder) since there is no record in
// cache and TPR. We need to iterate over the services & // cache and CRD. We need to iterate over the services &
// deployments to remove both normal and orphan builders. // deployments to remove both normal and orphan builders.
svcList, err := envw.getBuilderServiceList(nil) svcList, err := envw.getBuilderServiceList(nil)
@@ -241,7 +241,7 @@ func (envw *environmentWatcher) service() {
} }
} }
func (envw *environmentWatcher) getEnvBuilder(env *tpr.Environment) (*builderInfo, error) { func (envw *environmentWatcher) getEnvBuilder(env *crd.Environment) (*builderInfo, error) {
respChan := make(chan envwResponse) respChan := make(chan envwResponse)
envw.requestChan <- envwRequest{ envw.requestChan <- envwRequest{
requestType: GET_BUILDER, requestType: GET_BUILDER,
@@ -252,15 +252,15 @@ func (envw *environmentWatcher) getEnvBuilder(env *tpr.Environment) (*builderInf
return resp.builderInfo, resp.err return resp.builderInfo, resp.err
} }
func (envw *environmentWatcher) cleanupEnvBuilders(envs []tpr.Environment) { func (envw *environmentWatcher) cleanupEnvBuilders(envs []crd.Environment) {
envw.requestChan <- envwRequest{ envw.requestChan <- envwRequest{
requestType: CLEANUP_BUILDERS, requestType: CLEANUP_BUILDERS,
envList: envs, envList: envs,
} }
} }
func (envw *environmentWatcher) createBuilder(env *tpr.Environment) (*builderInfo, error) { func (envw *environmentWatcher) createBuilder(env *crd.Environment) (*builderInfo, error) {
var svc *v1.Service var svc *apiv1.Service
var deploy *v1beta1.Deployment var deploy *v1beta1.Deployment
sel := envw.getLabels(env.Metadata.Name, env.Metadata.ResourceVersion) sel := envw.getLabels(env.Metadata.Name, env.Metadata.ResourceVersion)
@@ -269,22 +269,32 @@ func (envw *environmentWatcher) createBuilder(env *tpr.Environment) (*builderInf
if err != nil { if err != nil {
return nil, err return nil, err
} }
// there should be only one service in svcList
if len(svcList) == 0 { if len(svcList) == 0 {
svc, err = envw.createBuilderService(env) svc, err = envw.createBuilderService(env)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error creating builder service: %v", err) return nil, fmt.Errorf("Error creating builder service: %v", err)
} }
} else if len(svcList) == 1 {
svc = &svcList[0]
} else {
return nil, fmt.Errorf("Found more than one builder service for environment %v", env.Metadata.Name)
} }
deployList, err := envw.getBuilderDeploymentList(sel) deployList, err := envw.getBuilderDeploymentList(sel)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// there should be only one deploy in deployList
if len(deployList) == 0 { if len(deployList) == 0 {
deploy, err = envw.createBuilderDeployment(env) deploy, err = envw.createBuilderDeployment(env)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error creating builder deployment: %v", err) return nil, fmt.Errorf("Error creating builder deployment: %v", err)
} }
} else if len(deployList) == 1 {
deploy = &deployList[0]
} else {
return nil, fmt.Errorf("Found more than one builder deployment for environment %v", env.Metadata.Name)
} }
return &builderInfo{ return &builderInfo{
@@ -305,7 +315,7 @@ func (envw *environmentWatcher) deleteBuilderService(sel map[string]string) erro
// cascading deletion // cascading deletion
// https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/ // https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/
falseVal := false falseVal := false
delOpt := &api.DeleteOptions{ delOpt := &metav1.DeleteOptions{
OrphanDependents: &falseVal, OrphanDependents: &falseVal,
} }
@@ -328,11 +338,11 @@ func (envw *environmentWatcher) deleteBuilderDeployment(sel map[string]string) e
log.Printf("Removing builder deployment: %v", deploy.ObjectMeta.Name) log.Printf("Removing builder deployment: %v", deploy.ObjectMeta.Name)
falseVal := false falseVal := false
delOpt := &api.DeleteOptions{ delOpt := &metav1.DeleteOptions{
OrphanDependents: &falseVal, OrphanDependents: &falseVal,
} }
err = envw.kubernetesClient. err = envw.kubernetesClient.ExtensionsV1beta1().
Deployments(envw.builderNamespace). Deployments(envw.builderNamespace).
Delete(deploy.ObjectMeta.Name, delOpt) Delete(deploy.ObjectMeta.Name, delOpt)
if err != nil { if err != nil {
@@ -342,10 +352,10 @@ func (envw *environmentWatcher) deleteBuilderDeployment(sel map[string]string) e
return nil return nil
} }
func (envw *environmentWatcher) getBuilderServiceList(sel map[string]string) ([]v1.Service, error) { func (envw *environmentWatcher) getBuilderServiceList(sel map[string]string) ([]apiv1.Service, error) {
svcList, err := envw.kubernetesClient.Services(envw.builderNamespace).List( svcList, err := envw.kubernetesClient.Services(envw.builderNamespace).List(
api.ListOptions{ metav1.ListOptions{
LabelSelector: labels.Set(sel).AsSelector(), LabelSelector: labels.Set(sel).AsSelector().String(),
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("Error getting builder service list: %v", err) return nil, fmt.Errorf("Error getting builder service list: %v", err)
@@ -353,22 +363,22 @@ func (envw *environmentWatcher) getBuilderServiceList(sel map[string]string) ([]
return svcList.Items, nil return svcList.Items, nil
} }
func (envw *environmentWatcher) createBuilderService(env *tpr.Environment) (*v1.Service, error) { func (envw *environmentWatcher) createBuilderService(env *crd.Environment) (*apiv1.Service, error) {
name := envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion) name := envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion)
sel := envw.getLabels(env.Metadata.Name, env.Metadata.ResourceVersion) sel := envw.getLabels(env.Metadata.Name, env.Metadata.ResourceVersion)
service := v1.Service{ service := apiv1.Service{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Namespace: envw.builderNamespace, Namespace: envw.builderNamespace,
Name: name, Name: name,
Labels: sel, Labels: sel,
}, },
Spec: v1.ServiceSpec{ Spec: apiv1.ServiceSpec{
Selector: sel, Selector: sel,
Type: v1.ServiceTypeClusterIP, Type: apiv1.ServiceTypeClusterIP,
Ports: []v1.ServicePort{ Ports: []apiv1.ServicePort{
{ {
Name: "fetcher-port", Name: "fetcher-port",
Protocol: v1.ProtocolTCP, Protocol: apiv1.ProtocolTCP,
Port: 8000, Port: 8000,
TargetPort: intstr.IntOrString{ TargetPort: intstr.IntOrString{
Type: intstr.Int, Type: intstr.Int,
@@ -377,7 +387,7 @@ func (envw *environmentWatcher) createBuilderService(env *tpr.Environment) (*v1.
}, },
{ {
Name: "builder-port", Name: "builder-port",
Protocol: v1.ProtocolTCP, Protocol: apiv1.ProtocolTCP,
Port: 8001, Port: 8001,
TargetPort: intstr.IntOrString{ TargetPort: intstr.IntOrString{
Type: intstr.Int, Type: intstr.Int,
@@ -396,9 +406,9 @@ func (envw *environmentWatcher) createBuilderService(env *tpr.Environment) (*v1.
} }
func (envw *environmentWatcher) getBuilderDeploymentList(sel map[string]string) ([]v1beta1.Deployment, error) { func (envw *environmentWatcher) getBuilderDeploymentList(sel map[string]string) ([]v1beta1.Deployment, error) {
deployList, err := envw.kubernetesClient.Deployments(envw.builderNamespace).List( deployList, err := envw.kubernetesClient.ExtensionsV1beta1().Deployments(envw.builderNamespace).List(
api.ListOptions{ metav1.ListOptions{
LabelSelector: labels.Set(sel).AsSelector(), LabelSelector: labels.Set(sel).AsSelector().String(),
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("Error getting builder deployment list: %v", err) return nil, fmt.Errorf("Error getting builder deployment list: %v", err)
@@ -406,42 +416,42 @@ func (envw *environmentWatcher) getBuilderDeploymentList(sel map[string]string)
return deployList.Items, nil return deployList.Items, nil
} }
func (envw *environmentWatcher) createBuilderDeployment(env *tpr.Environment) (*v1beta1.Deployment, error) { func (envw *environmentWatcher) createBuilderDeployment(env *crd.Environment) (*v1beta1.Deployment, error) {
sharedMountPath := "/package" sharedMountPath := "/package"
name := envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion) name := envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion)
sel := envw.getLabels(env.Metadata.Name, env.Metadata.ResourceVersion) sel := envw.getLabels(env.Metadata.Name, env.Metadata.ResourceVersion)
var replicas int32 = 1 var replicas int32 = 1
deployment := &v1beta1.Deployment{ deployment := &v1beta1.Deployment{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Namespace: envw.builderNamespace, Namespace: envw.builderNamespace,
Name: name, Name: name,
Labels: sel, Labels: sel,
}, },
Spec: v1beta1.DeploymentSpec{ Spec: v1beta1.DeploymentSpec{
Replicas: &replicas, Replicas: &replicas,
Selector: &v1beta1.LabelSelector{ Selector: &metav1.LabelSelector{
MatchLabels: sel, MatchLabels: sel,
}, },
Template: v1.PodTemplateSpec{ Template: apiv1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Labels: sel, Labels: sel,
}, },
Spec: v1.PodSpec{ Spec: apiv1.PodSpec{
Volumes: []v1.Volume{ Volumes: []apiv1.Volume{
{ {
Name: "package", Name: "package",
VolumeSource: v1.VolumeSource{ VolumeSource: apiv1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{}, EmptyDir: &apiv1.EmptyDirVolumeSource{},
}, },
}, },
}, },
Containers: []v1.Container{ Containers: []apiv1.Container{
{ {
Name: "builder", Name: "builder",
Image: env.Spec.Builder.Image, Image: env.Spec.Builder.Image,
ImagePullPolicy: v1.PullAlways, ImagePullPolicy: apiv1.PullAlways,
TerminationMessagePath: "/dev/termination-log", TerminationMessagePath: "/dev/termination-log",
VolumeMounts: []v1.VolumeMount{ VolumeMounts: []apiv1.VolumeMount{
{ {
Name: "package", Name: "package",
MountPath: sharedMountPath, MountPath: sharedMountPath,
@@ -454,7 +464,7 @@ func (envw *environmentWatcher) createBuilderDeployment(env *tpr.Environment) (*
Image: envw.fetcherImage, Image: envw.fetcherImage,
ImagePullPolicy: envw.fetcherImagePullPolicy, ImagePullPolicy: envw.fetcherImagePullPolicy,
TerminationMessagePath: "/dev/termination-log", TerminationMessagePath: "/dev/termination-log",
VolumeMounts: []v1.VolumeMount{ VolumeMounts: []apiv1.VolumeMount{
{ {
Name: "package", Name: "package",
MountPath: sharedMountPath, MountPath: sharedMountPath,
@@ -469,7 +479,7 @@ func (envw *environmentWatcher) createBuilderDeployment(env *tpr.Environment) (*
}, },
} }
log.Printf("Creating builder deployment: %v", envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion)) log.Printf("Creating builder deployment: %v", envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion))
_, err := envw.kubernetesClient.Deployments(envw.builderNamespace).Create(deployment) _, err := envw.kubernetesClient.ExtensionsV1beta1().Deployments(envw.builderNamespace).Create(deployment)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+13 -20
View File
@@ -20,53 +20,46 @@ import (
"log" "log"
"time" "time"
"k8s.io/client-go/1.5/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/pkg/watch"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
type ( type (
packageWatcher struct { packageWatcher struct {
fissionClient *tpr.FissionClient fissionClient *crd.FissionClient
kubernetesClient *kubernetes.Clientset
builderNamespace string builderNamespace string
storageSvcUrl string storageSvcUrl string
} }
) )
func makePackageWatcher(fissionClient *tpr.FissionClient, func makePackageWatcher(fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, builderNamespace string, storageSvcUrl string) *packageWatcher { builderNamespace string, storageSvcUrl string) *packageWatcher {
pkgw := &packageWatcher{ pkgw := &packageWatcher{
fissionClient: fissionClient, fissionClient: fissionClient,
kubernetesClient: kubernetesClient,
builderNamespace: builderNamespace, builderNamespace: builderNamespace,
storageSvcUrl: storageSvcUrl, storageSvcUrl: storageSvcUrl,
} }
return pkgw return pkgw
} }
func (pkgw *packageWatcher) build(pkgMetadata api.ObjectMeta) { func (pkgw *packageWatcher) build(pkg *crd.Package) {
buildReq := BuildRequest{ _, err := buildPackage(pkgw.fissionClient, pkgw.builderNamespace, pkgw.storageSvcUrl, pkg)
Package: pkgMetadata,
}
_, err := buildPackage(pkgw.fissionClient,
pkgw.kubernetesClient, pkgw.builderNamespace, pkgw.storageSvcUrl, buildReq)
if err != nil { if err != nil {
log.Printf("Error building package %v: %v", buildReq.Package.Name, err) log.Printf("Error building package %v: %v", pkg.Metadata.Name, err)
} }
} }
func (pkgw *packageWatcher) watchPackages() { func (pkgw *packageWatcher) watchPackages() {
rv := "" rv := ""
for { for {
wi, err := pkgw.fissionClient.Packages(api.NamespaceDefault).Watch(api.ListOptions{ wi, err := pkgw.fissionClient.Packages(metav1.NamespaceDefault).Watch(metav1.ListOptions{
ResourceVersion: rv, ResourceVersion: rv,
}) })
if err != nil { if err != nil {
log.Fatalf("Error watching package TPR resources: %v", err) log.Fatalf("Error watching package CRD resources: %v", err)
} }
for { for {
@@ -79,12 +72,12 @@ func (pkgw *packageWatcher) watchPackages() {
time.Sleep(time.Second) time.Sleep(time.Second)
break break
} }
pkg := ev.Object.(*tpr.Package) pkg := ev.Object.(*crd.Package)
rv = pkg.Metadata.ResourceVersion rv = pkg.Metadata.ResourceVersion
// only do build for packages in pending state // only do build for packages in pending state
if pkg.Status.BuildStatus == fission.BuildStatusPending { if pkg.Status.BuildStatus == fission.BuildStatusPending {
go pkgw.build(pkg.Metadata) go pkgw.build(pkg)
} }
} }
} }
+1 -2
View File
@@ -5,7 +5,7 @@
## Prerequisites ## Prerequisites
- Kubernetes 1.6+ with Beta APIs enabled - Kubernetes 1.8 or later
## Helm charts ## Helm charts
@@ -48,7 +48,6 @@ The following table lists the configurable parameters of the Fission chart and t
| `routerPort` | Fission Router Service Port | `31314` | | `routerPort` | Fission Router Service Port | `31314` |
| `functionNamespace` | Namespace for Fission functions | `fission-function` | | `functionNamespace` | Namespace for Fission functions | `fission-function` |
| `builderNamespace` | Namespace for Fission environment builders | `fission-builder` | | `builderNamespace` | Namespace for Fission environment builders | `fission-builder` |
| `openshift` | RBAC for openshift | `false` |
* Extra configuration for `fission-all` * Extra configuration for `fission-all`
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
name: fission-all name: fission-all
version: 0.3.0 version: 0.4.1
description: Fission is a fast serverless framework for Kubernetes. description: Fission is a fast serverless framework for Kubernetes.
keywords: keywords:
- fission - fission
@@ -12,4 +12,4 @@ maintainers:
- name: Ta Ching Chen - name: Ta Ching Chen
email: contact@tachingchen.com email: contact@tachingchen.com
engine: gotpl engine: gotpl
appVersion: 0.3.0 appVersion: 0.4.1
+3 -3
View File
@@ -1,13 +1,13 @@
1. Install the client CLI. 1. Install the client CLI.
Mac: Mac:
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.3.0/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.4.1/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
Linux: Linux:
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.3.0/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.4.1/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 this windows executable: https://github.com/fission/fission/releases/download/0.3.0/fission-cli-windows.exe For Windows, you can use the linux binary on WSL. Or you can download this windows executable: https://github.com/fission/fission/releases/download/0.4.1/fission-cli-windows.exe
2. Set the FISSION_URL and FISSION_ROUTER environment variables. 2. Set the FISSION_URL and FISSION_ROUTER environment variables.
+16 -110
View File
@@ -1,77 +1,3 @@
{{ 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 apiVersion: v1
kind: Namespace kind: Namespace
@@ -131,7 +57,7 @@ roleRef:
kind: ClusterRoleBinding kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1 apiVersion: rbac.authorization.k8s.io/v1beta1
metadata: metadata:
name: fission-tpr name: fission-crd
subjects: subjects:
- kind: ServiceAccount - kind: ServiceAccount
name: fission-svc name: fission-svc
@@ -152,7 +78,7 @@ metadata:
kind: ClusterRoleBinding kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1 apiVersion: rbac.authorization.k8s.io/v1beta1
metadata: metadata:
name: fission-fetcher-tpr name: fission-fetcher-crd
subjects: subjects:
- kind: ServiceAccount - kind: ServiceAccount
name: fission-fetcher name: fission-fetcher
@@ -173,7 +99,7 @@ metadata:
kind: ClusterRoleBinding kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1 apiVersion: rbac.authorization.k8s.io/v1beta1
metadata: metadata:
name: fission-builder-tpr name: fission-builder-crd
subjects: subjects:
- kind: ServiceAccount - kind: ServiceAccount
name: fission-builder name: fission-builder
@@ -183,8 +109,6 @@ roleRef:
name: cluster-admin name: cluster-admin
apiGroup: rbac.authorization.k8s.io apiGroup: rbac.authorization.k8s.io
{{ end }}
--- ---
apiVersion: extensions/v1beta1 apiVersion: extensions/v1beta1
kind: Deployment kind: Deployment
@@ -226,16 +150,16 @@ spec:
image: "{{ .Values.image }}:{{ .Values.imageTag }}" image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: {{ .Values.pullPolicy }} imagePullPolicy: {{ .Values.pullPolicy }}
command: ["/fission-bundle"] command: ["/fission-bundle"]
args: ["--routerPort", "8888", "--poolmgrUrl", "http://poolmgr.{{ .Release.Namespace }}"] args: ["--routerPort", "8888", "--executorUrl", "http://executor.{{ .Release.Namespace }}"]
serviceAccount: fission-svc serviceAccount: fission-svc
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: poolmgr name: executor
labels: labels:
svc: poolmgr svc: executor
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec: spec:
type: ClusterIP type: ClusterIP
@@ -243,13 +167,13 @@ spec:
- port: 80 - port: 80
targetPort: 8888 targetPort: 8888
selector: selector:
svc: poolmgr svc: executor
--- ---
apiVersion: extensions/v1beta1 apiVersion: extensions/v1beta1
kind: Deployment kind: Deployment
metadata: metadata:
name: poolmgr name: executor
labels: labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec: spec:
@@ -257,14 +181,14 @@ spec:
template: template:
metadata: metadata:
labels: labels:
svc: poolmgr svc: executor
spec: spec:
containers: containers:
- name: poolmgr - name: executor
image: "{{ .Values.image }}:{{ .Values.imageTag }}" image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: {{ .Values.pullPolicy }} imagePullPolicy: {{ .Values.pullPolicy }}
command: ["/fission-bundle"] command: ["/fission-bundle"]
args: ["--poolmgrPort", "8888", "--namespace", "{{ .Values.functionNamespace }}", "--fission-namespace", "{{ .Release.Namespace }}"] args: ["--executorPort", "8888", "--namespace", "{{ .Values.functionNamespace }}", "--fission-namespace", "{{ .Release.Namespace }}"]
env: env:
- name: FETCHER_IMAGE - name: FETCHER_IMAGE
value: "{{ .Values.fetcherImage }}:{{ .Values.fetcherImageTag }}" value: "{{ .Values.fetcherImage }}:{{ .Values.fetcherImageTag }}"
@@ -273,7 +197,6 @@ spec:
- name: RUNTIME_IMAGE_PULL_POLICY - name: RUNTIME_IMAGE_PULL_POLICY
value: "{{ .Values.pullPolicy }}" value: "{{ .Values.pullPolicy }}"
serviceAccount: fission-svc serviceAccount: fission-svc
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@@ -348,7 +271,7 @@ metadata:
svc: influxdb svc: influxdb
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec: spec:
type: ClusterIP type: ClusterIP
ports: ports:
- port: 8086 - port: 8086
targetPort: 8086 targetPort: 8086
@@ -400,25 +323,6 @@ spec:
svc: logger svc: logger
spec: spec:
containers: 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 - name: fluentd
image: {{ .Values.logger.fluentdImage }} image: {{ .Values.logger.fluentdImage }}
imagePullPolicy: {{ .Values.pullPolicy }} imagePullPolicy: {{ .Values.pullPolicy }}
@@ -439,9 +343,11 @@ spec:
secretKeyRef: secretKeyRef:
name: influxdb name: influxdb
key: password key: password
- name: FLUENTD_PATH
value: /var/log/containers/*{{.Values.functionNamespace}}*.log
volumeMounts: volumeMounts:
- name: container-log - name: container-log
mountPath: /var/log/containers mountPath: /var/log/
readOnly: true readOnly: true
- name: docker-log - name: docker-log
mountPath: /var/lib/docker/containers mountPath: /var/lib/docker/containers
@@ -453,7 +359,7 @@ spec:
volumes: volumes:
- name: container-log - name: container-log
hostPath: hostPath:
path: /var/log/containers path: /var/log/
- name: docker-log - name: docker-log
hostPath: hostPath:
path: /var/lib/docker/containers path: /var/lib/docker/containers
+7 -7
View File
@@ -7,17 +7,17 @@ metadata:
app: fission-storage app: fission-storage
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}" release: "{{ .Release.Name }}"
annotations:
{{- if .Values.persistence.storageClass }}
volume.beta.kubernetes.io/storage-class: {{ .Values.persistence.storageClass | quote }}
{{- else }}
volume.alpha.kubernetes.io/storage-class: default
{{- end }}
spec: spec:
accessModes: accessModes:
- {{ .Values.persistence.accessMode | quote }} - {{ .Values.persistence.accessMode | quote }}
resources: resources:
requests: requests:
storage: {{ .Values.persistence.size | quote }} storage: {{ .Values.persistence.size | quote }}
{{- if .Values.persistence.storageClass }}
{{- if (eq "-" .Values.persistence.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
{{- end }}
{{- end }}
{{- end }} {{- end }}
+6
View File
@@ -10,7 +10,9 @@ spec:
ports: ports:
- port: 80 - port: 80
targetPort: 8888 targetPort: 8888
{{ if eq .Values.serviceType "NodePort" }}
nodePort: {{ .Values.routerPort }} nodePort: {{ .Values.routerPort }}
{{ end }}
selector: selector:
svc: router svc: router
@@ -27,7 +29,9 @@ spec:
ports: ports:
- port: 80 - port: 80
targetPort: 8888 targetPort: 8888
{{ if eq .Values.serviceType "NodePort" }}
nodePort: {{ .Values.controllerPort }} nodePort: {{ .Values.controllerPort }}
{{ end }}
selector: selector:
svc: controller svc: controller
@@ -44,7 +48,9 @@ spec:
ports: ports:
- port: 4222 - port: 4222
targetPort: 4222 targetPort: 4222
{{ if eq .Values.serviceType "NodePort" }}
nodePort: {{ .Values.natsStreamingPort }} nodePort: {{ .Values.natsStreamingPort }}
{{ end }}
selector: selector:
svc: nats-streaming svc: nats-streaming
+10 -10
View File
@@ -1,6 +1,6 @@
# #
# Fission chart configuration # Fission chart configuration
# #
## Kubernetes configuration ## Kubernetes configuration
## For minikube, set this to NodePort, elsewhere use LoadBalancer. ## For minikube, set this to NodePort, elsewhere use LoadBalancer.
@@ -13,13 +13,13 @@ image: fission/fission-bundle
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
## Fission image version ## Fission image version
imageTag: 0.3.0 imageTag: 0.4.1
## Fission fetcher repository ## Fission fetcher repository
fetcherImage: fission/fetcher fetcherImage: fission/fetcher
## Fission fetcher image version ## Fission fetcher image version
fetcherImageTag: 0.3.0 fetcherImageTag: 0.4.1
## Port at which Fission controller service should be exposed ## Port at which Fission controller service should be exposed
controllerPort: 31313 controllerPort: 31313
@@ -38,9 +38,6 @@ functionNamespace: fission-function
## the release namespace) ## the release namespace)
builderNamespace: fission-builder builderNamespace: fission-builder
## Set up openshift RBAC rule
openshift: false
## Logger config ## Logger config
logger: logger:
influxdbAdmin: "admin" influxdbAdmin: "admin"
@@ -51,15 +48,18 @@ logger:
nats: nats:
authToken: "defaultFissionAuthToken" authToken: "defaultFissionAuthToken"
clusterID: "fissionMQTrigger" clusterID: "fissionMQTrigger"
## Persist data to a persistent volume. ## Persist data to a persistent volume.
persistence: persistence:
enabled: true enabled: true
## If defined, volume.beta.kubernetes.io/storage-class: <storageClass> ## If defined, storageClassName: <storageClass>
## Default: volume.alpha.kubernetes.io/storage-class: default ## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack)
## ##
# storageClass: # storageClass: "-"
accessMode: ReadWriteOnce accessMode: ReadWriteOnce
size: 8Gi size: 8Gi
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
name: fission-core name: fission-core
version: 0.3.0 version: 0.4.1
description: Fission is a fast serverless framework for Kubernetes. description: Fission is a fast serverless framework for Kubernetes.
keywords: keywords:
- fission - fission
@@ -12,4 +12,4 @@ maintainers:
- name: Ta Ching Chen - name: Ta Ching Chen
email: contact@tachingchen.com email: contact@tachingchen.com
engine: gotpl engine: gotpl
appVersion: 0.3.0 appVersion: 0.4.1
+3 -3
View File
@@ -1,13 +1,13 @@
1. Install the client CLI. 1. Install the client CLI.
Mac: Mac:
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.3.0/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.4.1/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
Linux: Linux:
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.3.0/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.4.1/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 this windows executable: https://github.com/fission/fission/releases/download/0.3.0/fission-cli-windows.exe For Windows, you can use the linux binary on WSL. Or you can download this windows executable: https://github.com/fission/fission/releases/download/0.4.1/fission-cli-windows.exe
2. Set the FISSION_URL and FISSION_ROUTER environment variables. 2. Set the FISSION_URL and FISSION_ROUTER environment variables.
+11 -87
View File
@@ -1,77 +1,3 @@
{{ 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 apiVersion: v1
kind: Namespace kind: Namespace
@@ -131,7 +57,7 @@ roleRef:
kind: ClusterRoleBinding kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1 apiVersion: rbac.authorization.k8s.io/v1beta1
metadata: metadata:
name: fission-tpr name: fission-crd
subjects: subjects:
- kind: ServiceAccount - kind: ServiceAccount
name: fission-svc name: fission-svc
@@ -152,7 +78,7 @@ metadata:
kind: ClusterRoleBinding kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1 apiVersion: rbac.authorization.k8s.io/v1beta1
metadata: metadata:
name: fission-fetcher-tpr name: fission-fetcher-crd
subjects: subjects:
- kind: ServiceAccount - kind: ServiceAccount
name: fission-fetcher name: fission-fetcher
@@ -173,7 +99,7 @@ metadata:
kind: ClusterRoleBinding kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1 apiVersion: rbac.authorization.k8s.io/v1beta1
metadata: metadata:
name: fission-builder-tpr name: fission-builder-crd
subjects: subjects:
- kind: ServiceAccount - kind: ServiceAccount
name: fission-builder name: fission-builder
@@ -183,8 +109,6 @@ roleRef:
name: cluster-admin name: cluster-admin
apiGroup: rbac.authorization.k8s.io apiGroup: rbac.authorization.k8s.io
{{ end }}
--- ---
apiVersion: extensions/v1beta1 apiVersion: extensions/v1beta1
kind: Deployment kind: Deployment
@@ -226,16 +150,16 @@ spec:
image: "{{ .Values.image }}:{{ .Values.imageTag }}" image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: {{ .Values.pullPolicy }} imagePullPolicy: {{ .Values.pullPolicy }}
command: ["/fission-bundle"] command: ["/fission-bundle"]
args: ["--routerPort", "8888", "--poolmgrUrl", "http://poolmgr.{{ .Release.Namespace }}"] args: ["--routerPort", "8888", "--executorUrl", "http://executor.{{ .Release.Namespace }}"]
serviceAccount: fission-svc serviceAccount: fission-svc
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: poolmgr name: executor
labels: labels:
svc: poolmgr svc: executor
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec: spec:
type: ClusterIP type: ClusterIP
@@ -243,13 +167,13 @@ spec:
- port: 80 - port: 80
targetPort: 8888 targetPort: 8888
selector: selector:
svc: poolmgr svc: executor
--- ---
apiVersion: extensions/v1beta1 apiVersion: extensions/v1beta1
kind: Deployment kind: Deployment
metadata: metadata:
name: poolmgr name: executor
labels: labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec: spec:
@@ -257,14 +181,14 @@ spec:
template: template:
metadata: metadata:
labels: labels:
svc: poolmgr svc: executor
spec: spec:
containers: containers:
- name: poolmgr - name: executor
image: "{{ .Values.image }}:{{ .Values.imageTag }}" image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: {{ .Values.pullPolicy }} imagePullPolicy: {{ .Values.pullPolicy }}
command: ["/fission-bundle"] command: ["/fission-bundle"]
args: ["--poolmgrPort", "8888", "--namespace", "{{ .Values.functionNamespace }}", "--fission-namespace", "{{ .Release.Namespace }}"] args: ["--executorPort", "8888", "--namespace", "{{ .Values.functionNamespace }}", "--fission-namespace", "{{ .Release.Namespace }}"]
env: env:
- name: FETCHER_IMAGE - name: FETCHER_IMAGE
value: "{{ .Values.fetcherImage }}:{{ .Values.fetcherImageTag }}" value: "{{ .Values.fetcherImage }}:{{ .Values.fetcherImageTag }}"
+7 -7
View File
@@ -7,17 +7,17 @@ metadata:
app: fission-storage app: fission-storage
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}" release: "{{ .Release.Name }}"
annotations:
{{- if .Values.persistence.storageClass }}
volume.beta.kubernetes.io/storage-class: {{ .Values.persistence.storageClass | quote }}
{{- else }}
volume.alpha.kubernetes.io/storage-class: default
{{- end }}
spec: spec:
accessModes: accessModes:
- {{ .Values.persistence.accessMode | quote }} - {{ .Values.persistence.accessMode | quote }}
resources: resources:
requests: requests:
storage: {{ .Values.persistence.size | quote }} storage: {{ .Values.persistence.size | quote }}
{{- if .Values.persistence.storageClass }}
{{- if (eq "-" .Values.persistence.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
{{- end }}
{{- end }}
{{- end }} {{- end }}
+4
View File
@@ -10,7 +10,9 @@ spec:
ports: ports:
- port: 80 - port: 80
targetPort: 8888 targetPort: 8888
{{ if eq .Values.serviceType "NodePort" }}
nodePort: {{ .Values.routerPort }} nodePort: {{ .Values.routerPort }}
{{ end }}
selector: selector:
svc: router svc: router
@@ -27,7 +29,9 @@ spec:
ports: ports:
- port: 80 - port: 80
targetPort: 8888 targetPort: 8888
{{ if eq .Values.serviceType "NodePort" }}
nodePort: {{ .Values.controllerPort }} nodePort: {{ .Values.controllerPort }}
{{ end }}
selector: selector:
svc: controller svc: controller
+9 -9
View File
@@ -1,6 +1,6 @@
# #
# Fission chart configuration # Fission chart configuration
# #
## Kubernetes configuration ## Kubernetes configuration
## For minikube, set this to NodePort, elsewhere use LoadBalancer. ## For minikube, set this to NodePort, elsewhere use LoadBalancer.
@@ -10,7 +10,7 @@ serviceType: LoadBalancer
image: fission/fission-bundle image: fission/fission-bundle
## Fission image version ## Fission image version
imageTag: 0.3.0 imageTag: 0.4.1
## Image pull policy ## Image pull policy
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
@@ -19,7 +19,7 @@ pullPolicy: IfNotPresent
fetcherImage: fission/fetcher fetcherImage: fission/fetcher
## Fission fetcher image version ## Fission fetcher image version
fetcherImageTag: 0.3.0 fetcherImageTag: 0.4.1
## Port at which Fission controller service should be exposed ## Port at which Fission controller service should be exposed
controllerPort: 31313 controllerPort: 31313
@@ -35,16 +35,16 @@ functionNamespace: fission-function
## the release namespace) ## the release namespace)
builderNamespace: fission-builder builderNamespace: fission-builder
## Set up openshift RBAC rule
openshift: false
## Persist data to a persistent volume. ## Persist data to a persistent volume.
persistence: persistence:
enabled: true enabled: true
## If defined, volume.beta.kubernetes.io/storage-class: <storageClass> ## If defined, storageClassName: <storageClass>
## Default: volume.alpha.kubernetes.io/storage-class: default ## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack)
## ##
# storageClass: # storageClass: "-"
accessMode: ReadWriteOnce accessMode: ReadWriteOnce
size: 8Gi size: 8Gi
+10 -6
View File
@@ -26,16 +26,18 @@ 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" kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/kubernetes"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/crd"
"github.com/fission/fission/fission/logdb" "github.com/fission/fission/fission/logdb"
"github.com/fission/fission/tpr"
) )
type ( type (
API struct { API struct {
fissionClient *tpr.FissionClient fissionClient *crd.FissionClient
kubernetesClient *kubernetes.Clientset
storageServiceUrl string storageServiceUrl string
builderManagerUrl string builderManagerUrl string
workflowApiUrl string workflowApiUrl string
@@ -49,7 +51,7 @@ type (
) )
func MakeAPI() (*API, error) { func MakeAPI() (*API, error) {
api, err := makeTPRBackedAPI() api, err := makeCRDBackedAPI()
u := os.Getenv("STORAGE_SERVICE_URL") u := os.Getenv("STORAGE_SERVICE_URL")
if len(u) > 0 { if len(u) > 0 {
@@ -118,7 +120,7 @@ func (api *API) getLogDBConfig(dbType string) logDBConfig {
func (api *API) HomeHandler(w http.ResponseWriter, r *http.Request) { func (api *API) HomeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprintf(w, "{\"message\": \"Fission API\", \"version\": \"0.3.0\"}\n") fmt.Fprintf(w, "{\"message\": \"Fission API\", \"version\": \"0.4.1\"}\n")
} }
func (api *API) ApiVersionMismatchHandler(w http.ResponseWriter, r *http.Request) { func (api *API) ApiVersionMismatchHandler(w http.ResponseWriter, r *http.Request) {
@@ -174,10 +176,12 @@ func (api *API) Serve(port int) {
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiUpdate).Methods("PUT") r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiDelete).Methods("DELETE") r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/v2/deleteTpr", api.Tpr2crdApi).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) r.HandleFunc("/proxy/storage/v1/archive", api.StorageServiceProxy)
r.HandleFunc("/proxy/buildermgr/v1/build", api.BuilderManagerBuildProxy) r.HandleFunc("/proxy/buildermgr/v1/build", api.BuilderManagerBuildProxy)
r.HandleFunc("/proxy/buildermgr/v1/builder", api.BuilderManagerEnvBuilderProxy) r.HandleFunc("/proxy/logs/{function}", api.FunctionPodLogs).Methods("POST")
r.HandleFunc("/proxy/workflows-apiserver/{path:.*}", api.WorkflowApiserverProxy) r.HandleFunc("/proxy/workflows-apiserver/{path:.*}", api.WorkflowApiserverProxy)
address := fmt.Sprintf(":%v", port) address := fmt.Sprintf(":%v", port)
+50 -45
View File
@@ -18,6 +18,7 @@ package controller
import ( import (
"flag" "flag"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
@@ -25,11 +26,11 @@ import (
"testing" "testing"
"time" "time"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"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" "github.com/fission/fission/crd"
) )
var g struct { var g struct {
@@ -72,13 +73,10 @@ func assertCronSpecFails(err error) {
} }
func TestFunctionApi(t *testing.T) { func TestFunctionApi(t *testing.T) {
name1 := "foo" testFunc := &crd.Function{
name2 := "bar" Metadata: metav1.ObjectMeta{
Name: "foo",
testFunc := &tpr.Function{ Namespace: metav1.NamespaceDefault,
Metadata: api.ObjectMeta{
Name: name1,
Namespace: api.NamespaceDefault,
}, },
Spec: fission.FunctionSpec{ Spec: fission.FunctionSpec{
Environment: fission.EnvironmentReference{ Environment: fission.EnvironmentReference{
@@ -89,30 +87,36 @@ func TestFunctionApi(t *testing.T) {
}, },
}, },
} }
_, err := g.client.FunctionGet(&api.ObjectMeta{ _, err := g.client.FunctionGet(&metav1.ObjectMeta{
Name: testFunc.Metadata.Name, Name: testFunc.Metadata.Name,
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}) })
assertNotFoundFailure(err, "function") assertNotFoundFailure(err, "function")
_, err = g.client.FunctionCreate(testFunc) m, err := g.client.FunctionCreate(testFunc)
panicIf(err) panicIf(err)
defer func() {
err := g.client.FunctionDelete(m)
panicIf(err)
}()
_, err = g.client.FunctionCreate(testFunc) _, err = g.client.FunctionCreate(testFunc)
assertNameReuseFailure(err, "function") assertNameReuseFailure(err, "function")
testFunc.Metadata.ResourceVersion = m.ResourceVersion
testFunc.Spec.Package.FunctionName = "yyy" testFunc.Spec.Package.FunctionName = "yyy"
_, err = g.client.FunctionUpdate(testFunc) _, err = g.client.FunctionUpdate(testFunc)
panicIf(err) panicIf(err)
testFunc.Metadata.Name = name2 testFunc.Metadata.ResourceVersion = ""
_, err = g.client.FunctionCreate(testFunc) testFunc.Metadata.Name = "bar"
m2, err := g.client.FunctionCreate(testFunc)
panicIf(err) panicIf(err)
defer g.client.FunctionDelete(m2)
funcs, err := g.client.FunctionList() funcs, err := g.client.FunctionList()
panicIf(err) panicIf(err)
assert(len(funcs) == 2, assert(len(funcs) == 2, fmt.Sprintf("created two functions, but found %v", len(funcs)))
"created two functions, but didn't find them")
funcs_url := g.client.Url + "/v2/functions" funcs_url := g.client.Url + "/v2/functions"
resp, err := http.Get(funcs_url) resp, err := http.Get(funcs_url)
@@ -127,18 +131,13 @@ func TestFunctionApi(t *testing.T) {
} }
} }
assert(found, "incorrect response content type") assert(found, "incorrect response content type")
err = g.client.FunctionDelete(&api.ObjectMeta{Name: name1, Namespace: api.NamespaceDefault})
panicIf(err)
err = g.client.FunctionDelete(&api.ObjectMeta{Name: name2, Namespace: api.NamespaceDefault})
panicIf(err)
} }
func TestHTTPTriggerApi(t *testing.T) { func TestHTTPTriggerApi(t *testing.T) {
testTrigger := &tpr.Httptrigger{ testTrigger := &crd.HTTPTrigger{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "foo", Name: "foo",
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.HTTPTriggerSpec{ Spec: fission.HTTPTriggerSpec{
RelativeURL: "/hello", RelativeURL: "/hello",
@@ -148,9 +147,9 @@ func TestHTTPTriggerApi(t *testing.T) {
}, },
}, },
} }
_, err := g.client.HTTPTriggerGet(&api.ObjectMeta{ _, err := g.client.HTTPTriggerGet(&metav1.ObjectMeta{
Name: testTrigger.Metadata.Name, Name: testTrigger.Metadata.Name,
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}) })
assertNotFoundFailure(err, "httptrigger") assertNotFoundFailure(err, "httptrigger")
@@ -165,10 +164,12 @@ func TestHTTPTriggerApi(t *testing.T) {
panicIf(err) panicIf(err)
assert(testTrigger.Spec == tr.Spec, "trigger should match after reading") assert(testTrigger.Spec == tr.Spec, "trigger should match after reading")
testTrigger.Metadata.ResourceVersion = m.ResourceVersion
testTrigger.Spec.RelativeURL = "/hi" testTrigger.Spec.RelativeURL = "/hi"
_, err = g.client.HTTPTriggerUpdate(testTrigger) _, err = g.client.HTTPTriggerUpdate(testTrigger)
panicIf(err) panicIf(err)
testTrigger.Metadata.ResourceVersion = ""
testTrigger.Metadata.Name = "yyy" testTrigger.Metadata.Name = "yyy"
_, 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")
@@ -180,14 +181,14 @@ func TestHTTPTriggerApi(t *testing.T) {
ts, err := g.client.HTTPTriggerList() ts, err := g.client.HTTPTriggerList()
panicIf(err) panicIf(err)
assert(len(ts) == 2, "created two triggers, but didn't find them") assert(len(ts) == 2, fmt.Sprintf("created two triggers, but found %v", len(ts)))
} }
func TestEnvironmentApi(t *testing.T) { func TestEnvironmentApi(t *testing.T) {
testEnv := &tpr.Environment{ testEnv := &crd.Environment{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "foo", Name: "foo",
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.EnvironmentSpec{ Spec: fission.EnvironmentSpec{
Runtime: fission.Runtime{ Runtime: fission.Runtime{
@@ -195,9 +196,9 @@ func TestEnvironmentApi(t *testing.T) {
}, },
}, },
} }
_, err := g.client.EnvironmentGet(&api.ObjectMeta{ _, err := g.client.EnvironmentGet(&metav1.ObjectMeta{
Name: testEnv.Metadata.Name, Name: testEnv.Metadata.Name,
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}) })
assertNotFoundFailure(err, "environment") assertNotFoundFailure(err, "environment")
@@ -212,10 +213,12 @@ func TestEnvironmentApi(t *testing.T) {
panicIf(err) panicIf(err)
assert(testEnv.Spec == e.Spec, "env should match after reading") assert(testEnv.Spec == e.Spec, "env should match after reading")
testEnv.Metadata.ResourceVersion = m.ResourceVersion
testEnv.Spec.Runtime.Image = "another-img" testEnv.Spec.Runtime.Image = "another-img"
_, err = g.client.EnvironmentUpdate(testEnv) _, err = g.client.EnvironmentUpdate(testEnv)
panicIf(err) panicIf(err)
testEnv.Metadata.ResourceVersion = ""
testEnv.Metadata.Name = "bar" testEnv.Metadata.Name = "bar"
m2, err := g.client.EnvironmentCreate(testEnv) m2, err := g.client.EnvironmentCreate(testEnv)
panicIf(err) panicIf(err)
@@ -223,14 +226,14 @@ func TestEnvironmentApi(t *testing.T) {
ts, err := g.client.EnvironmentList() ts, err := g.client.EnvironmentList()
panicIf(err) panicIf(err)
assert(len(ts) == 2, "created two envs, but didn't find them") assert(len(ts) == 2, fmt.Sprintf("created two envs, but found %v", len(ts)))
} }
func TestWatchApi(t *testing.T) { func TestWatchApi(t *testing.T) {
testWatch := &tpr.Kuberneteswatchtrigger{ testWatch := &crd.KubernetesWatchTrigger{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "xxx", Name: "xxx",
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.KubernetesWatchTriggerSpec{ Spec: fission.KubernetesWatchTriggerSpec{
Namespace: "default", Namespace: "default",
@@ -241,9 +244,9 @@ func TestWatchApi(t *testing.T) {
}, },
}, },
} }
_, err := g.client.WatchGet(&api.ObjectMeta{ _, err := g.client.WatchGet(&metav1.ObjectMeta{
Name: testWatch.Metadata.Name, Name: testWatch.Metadata.Name,
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}) })
assertNotFoundFailure(err, "watch") assertNotFoundFailure(err, "watch")
@@ -267,14 +270,14 @@ func TestWatchApi(t *testing.T) {
ws, err := g.client.WatchList() ws, err := g.client.WatchList()
panicIf(err) panicIf(err)
assert(len(ws) == 2, "created two envs, but didn't find them") assert(len(ws) == 2, fmt.Sprintf("created two watches, but found %v", len(ws)))
} }
func TestTimeTriggerApi(t *testing.T) { func TestTimeTriggerApi(t *testing.T) {
testTrigger := &tpr.Timetrigger{ testTrigger := &crd.TimeTrigger{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "xxx", Name: "xxx",
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.TimeTriggerSpec{ Spec: fission.TimeTriggerSpec{
Cron: "0 30 * * * *", Cron: "0 30 * * * *",
@@ -284,7 +287,7 @@ func TestTimeTriggerApi(t *testing.T) {
}, },
}, },
} }
_, err := g.client.TimeTriggerGet(&api.ObjectMeta{Name: testTrigger.Metadata.Name}) _, err := g.client.TimeTriggerGet(&metav1.ObjectMeta{Name: testTrigger.Metadata.Name})
assertNotFoundFailure(err, "trigger") assertNotFoundFailure(err, "trigger")
m, err := g.client.TimeTriggerCreate(testTrigger) m, err := g.client.TimeTriggerCreate(testTrigger)
@@ -298,10 +301,12 @@ func TestTimeTriggerApi(t *testing.T) {
panicIf(err) panicIf(err)
assert(testTrigger.Spec == tr.Spec, "trigger should match after reading") assert(testTrigger.Spec == tr.Spec, "trigger should match after reading")
testTrigger.Metadata.ResourceVersion = m.ResourceVersion
testTrigger.Spec.Cron = "@hourly" testTrigger.Spec.Cron = "@hourly"
_, err = g.client.TimeTriggerUpdate(testTrigger) _, err = g.client.TimeTriggerUpdate(testTrigger)
panicIf(err) panicIf(err)
testTrigger.Metadata.ResourceVersion = ""
testTrigger.Metadata.Name = "yyy" testTrigger.Metadata.Name = "yyy"
testTrigger.Spec.Cron = "Not valid cron spec" testTrigger.Spec.Cron = "Not valid cron spec"
_, err = g.client.TimeTriggerCreate(testTrigger) _, err = g.client.TimeTriggerCreate(testTrigger)
@@ -309,7 +314,7 @@ func TestTimeTriggerApi(t *testing.T) {
ts, err := g.client.TimeTriggerList() ts, err := g.client.TimeTriggerList()
panicIf(err) panicIf(err)
assert(len(ts) == 1, "created one trigger, but didn't find it") assert(len(ts) == 1, fmt.Sprintf("created two time triggers, but found %v", len(ts)))
} }
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@@ -324,7 +329,7 @@ func TestMain(m *testing.M) {
go Start(8888) go Start(8888)
time.Sleep(time.Second) time.Sleep(5 * time.Second)
g.client = client.MakeClient("http://localhost:8888") g.client = client.MakeClient("http://localhost:8888")
resp, err := http.Get("http://localhost:8888/") resp, err := http.Get("http://localhost:8888/")
+2 -14
View File
@@ -26,7 +26,7 @@ import (
func (api *API) BuilderManagerBuildProxy(w http.ResponseWriter, r *http.Request) { func (api *API) BuilderManagerBuildProxy(w http.ResponseWriter, r *http.Request) {
u := api.builderManagerUrl + "/v1/build" u := api.builderManagerUrl + "/v1/build"
proxy, err := api._getBuilderManagerProxy(u) proxy, err := api.getBuilderManagerProxy(u)
if err != nil { if err != nil {
msg := fmt.Sprintf("Failed to establish proxy server: %v", err) msg := fmt.Sprintf("Failed to establish proxy server: %v", err)
log.Println(msg) log.Println(msg)
@@ -36,19 +36,7 @@ func (api *API) BuilderManagerBuildProxy(w http.ResponseWriter, r *http.Request)
proxy.ServeHTTP(w, r) proxy.ServeHTTP(w, r)
} }
func (api *API) BuilderManagerEnvBuilderProxy(w http.ResponseWriter, r *http.Request) { func (api *API) getBuilderManagerProxy(targetUrl string) (*httputil.ReverseProxy, error) {
u := api.builderManagerUrl + "/v1/builder"
proxy, err := api._getBuilderManagerProxy(u)
if err != nil {
msg := fmt.Sprintf("Failed to establish proxy server: %v", err)
log.Println(msg)
http.Error(w, msg, 500)
return
}
proxy.ServeHTTP(w, r)
}
func (api *API) _getBuilderManagerProxy(targetUrl string) (*httputil.ReverseProxy, error) {
svcUrl, err := url.Parse(targetUrl) svcUrl, err := url.Parse(targetUrl)
if err != nil { if err != nil {
return nil, err return nil, err
+11 -11
View File
@@ -22,12 +22,12 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (c *Client) EnvironmentCreate(env *tpr.Environment) (*api.ObjectMeta, error) { func (c *Client) EnvironmentCreate(env *crd.Environment) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(env) reqbody, err := json.Marshal(env)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -44,7 +44,7 @@ func (c *Client) EnvironmentCreate(env *tpr.Environment) (*api.ObjectMeta, error
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -53,7 +53,7 @@ func (c *Client) EnvironmentCreate(env *tpr.Environment) (*api.ObjectMeta, error
return &m, nil return &m, nil
} }
func (c *Client) EnvironmentGet(m *api.ObjectMeta) (*tpr.Environment, error) { func (c *Client) EnvironmentGet(m *metav1.ObjectMeta) (*crd.Environment, error) {
relativeUrl := fmt.Sprintf("environments/%v", m.Name) relativeUrl := fmt.Sprintf("environments/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
@@ -68,7 +68,7 @@ func (c *Client) EnvironmentGet(m *api.ObjectMeta) (*tpr.Environment, error) {
return nil, err return nil, err
} }
var env tpr.Environment var env crd.Environment
err = json.Unmarshal(body, &env) err = json.Unmarshal(body, &env)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -77,7 +77,7 @@ func (c *Client) EnvironmentGet(m *api.ObjectMeta) (*tpr.Environment, error) {
return &env, nil return &env, nil
} }
func (c *Client) EnvironmentUpdate(env *tpr.Environment) (*api.ObjectMeta, error) { func (c *Client) EnvironmentUpdate(env *crd.Environment) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(env) reqbody, err := json.Marshal(env)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -95,7 +95,7 @@ func (c *Client) EnvironmentUpdate(env *tpr.Environment) (*api.ObjectMeta, error
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -103,14 +103,14 @@ func (c *Client) EnvironmentUpdate(env *tpr.Environment) (*api.ObjectMeta, error
return &m, nil return &m, nil
} }
func (c *Client) EnvironmentDelete(m *api.ObjectMeta) error { func (c *Client) EnvironmentDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("environments/%v", m.Name) relativeUrl := fmt.Sprintf("environments/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl) return c.delete(relativeUrl)
} }
func (c *Client) EnvironmentList() ([]tpr.Environment, error) { func (c *Client) EnvironmentList() ([]crd.Environment, error) {
resp, err := http.Get(c.url("environments")) resp, err := http.Get(c.url("environments"))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -122,7 +122,7 @@ func (c *Client) EnvironmentList() ([]tpr.Environment, error) {
return nil, err return nil, err
} }
envs := make([]tpr.Environment, 0) envs := make([]crd.Environment, 0)
err = json.Unmarshal(body, &envs) err = json.Unmarshal(body, &envs)
if err != nil { if err != nil {
return nil, err return nil, err
+12 -12
View File
@@ -22,12 +22,12 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (c *Client) FunctionCreate(f *tpr.Function) (*api.ObjectMeta, error) { func (c *Client) FunctionCreate(f *crd.Function) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(f) reqbody, err := json.Marshal(f)
if err != nil { if err != nil {
@@ -45,7 +45,7 @@ func (c *Client) FunctionCreate(f *tpr.Function) (*api.ObjectMeta, error) {
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -54,7 +54,7 @@ func (c *Client) FunctionCreate(f *tpr.Function) (*api.ObjectMeta, error) {
return &m, nil return &m, nil
} }
func (c *Client) FunctionGet(m *api.ObjectMeta) (*tpr.Function, error) { func (c *Client) FunctionGet(m *metav1.ObjectMeta) (*crd.Function, error) {
relativeUrl := fmt.Sprintf("functions/%v", m.Name) relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
@@ -69,7 +69,7 @@ func (c *Client) FunctionGet(m *api.ObjectMeta) (*tpr.Function, error) {
return nil, err return nil, err
} }
var f tpr.Function var f crd.Function
err = json.Unmarshal(body, &f) err = json.Unmarshal(body, &f)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -78,7 +78,7 @@ func (c *Client) FunctionGet(m *api.ObjectMeta) (*tpr.Function, error) {
return &f, nil return &f, nil
} }
func (c *Client) FunctionGetRawDeployment(m *api.ObjectMeta) ([]byte, error) { func (c *Client) FunctionGetRawDeployment(m *metav1.ObjectMeta) ([]byte, error) {
relativeUrl := fmt.Sprintf("functions/%v", m.Name) relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
relativeUrl += fmt.Sprintf("&deploymentraw=1") relativeUrl += fmt.Sprintf("&deploymentraw=1")
@@ -92,7 +92,7 @@ func (c *Client) FunctionGetRawDeployment(m *api.ObjectMeta) ([]byte, error) {
return c.handleResponse(resp) return c.handleResponse(resp)
} }
func (c *Client) FunctionUpdate(f *tpr.Function) (*api.ObjectMeta, error) { func (c *Client) FunctionUpdate(f *crd.Function) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(f) reqbody, err := json.Marshal(f)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -110,7 +110,7 @@ func (c *Client) FunctionUpdate(f *tpr.Function) (*api.ObjectMeta, error) {
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -118,13 +118,13 @@ func (c *Client) FunctionUpdate(f *tpr.Function) (*api.ObjectMeta, error) {
return &m, nil return &m, nil
} }
func (c *Client) FunctionDelete(m *api.ObjectMeta) error { func (c *Client) FunctionDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("functions/%v", m.Name) relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl) return c.delete(relativeUrl)
} }
func (c *Client) FunctionList() ([]tpr.Function, error) { func (c *Client) FunctionList() ([]crd.Function, error) {
resp, err := http.Get(c.url("functions")) resp, err := http.Get(c.url("functions"))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -136,7 +136,7 @@ func (c *Client) FunctionList() ([]tpr.Function, error) {
return nil, err return nil, err
} }
funcs := make([]tpr.Function, 0) funcs := make([]crd.Function, 0)
err = json.Unmarshal(body, &funcs) err = json.Unmarshal(body, &funcs)
if err != nil { if err != nil {
return nil, err return nil, err
+11 -11
View File
@@ -22,12 +22,12 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (c *Client) HTTPTriggerCreate(t *tpr.Httptrigger) (*api.ObjectMeta, error) { func (c *Client) HTTPTriggerCreate(t *crd.HTTPTrigger) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(t) reqbody, err := json.Marshal(t)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -44,7 +44,7 @@ func (c *Client) HTTPTriggerCreate(t *tpr.Httptrigger) (*api.ObjectMeta, error)
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -53,7 +53,7 @@ func (c *Client) HTTPTriggerCreate(t *tpr.Httptrigger) (*api.ObjectMeta, error)
return &m, nil return &m, nil
} }
func (c *Client) HTTPTriggerGet(m *api.ObjectMeta) (*tpr.Httptrigger, error) { func (c *Client) HTTPTriggerGet(m *metav1.ObjectMeta) (*crd.HTTPTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name) relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
@@ -68,7 +68,7 @@ func (c *Client) HTTPTriggerGet(m *api.ObjectMeta) (*tpr.Httptrigger, error) {
return nil, err return nil, err
} }
var t tpr.Httptrigger var t crd.HTTPTrigger
err = json.Unmarshal(body, &t) err = json.Unmarshal(body, &t)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -77,7 +77,7 @@ func (c *Client) HTTPTriggerGet(m *api.ObjectMeta) (*tpr.Httptrigger, error) {
return &t, nil return &t, nil
} }
func (c *Client) HTTPTriggerUpdate(t *tpr.Httptrigger) (*api.ObjectMeta, error) { func (c *Client) HTTPTriggerUpdate(t *crd.HTTPTrigger) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(t) reqbody, err := json.Marshal(t)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -95,7 +95,7 @@ func (c *Client) HTTPTriggerUpdate(t *tpr.Httptrigger) (*api.ObjectMeta, error)
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -103,13 +103,13 @@ func (c *Client) HTTPTriggerUpdate(t *tpr.Httptrigger) (*api.ObjectMeta, error)
return &m, nil return &m, nil
} }
func (c *Client) HTTPTriggerDelete(m *api.ObjectMeta) error { func (c *Client) HTTPTriggerDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name) relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl) return c.delete(relativeUrl)
} }
func (c *Client) HTTPTriggerList() ([]tpr.Httptrigger, error) { func (c *Client) HTTPTriggerList() ([]crd.HTTPTrigger, error) {
resp, err := http.Get(c.url("triggers/http")) resp, err := http.Get(c.url("triggers/http"))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -121,7 +121,7 @@ func (c *Client) HTTPTriggerList() ([]tpr.Httptrigger, error) {
return nil, err return nil, err
} }
triggers := make([]tpr.Httptrigger, 0) triggers := make([]crd.HTTPTrigger, 0)
err = json.Unmarshal(body, &triggers) err = json.Unmarshal(body, &triggers)
if err != nil { if err != nil {
return nil, err return nil, err
+10 -10
View File
@@ -22,13 +22,13 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (c *Client) WatchCreate(w *tpr.Kuberneteswatchtrigger) (*api.ObjectMeta, error) { func (c *Client) WatchCreate(w *crd.KubernetesWatchTrigger) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(w) reqbody, err := json.Marshal(w)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -45,7 +45,7 @@ func (c *Client) WatchCreate(w *tpr.Kuberneteswatchtrigger) (*api.ObjectMeta, er
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -54,7 +54,7 @@ func (c *Client) WatchCreate(w *tpr.Kuberneteswatchtrigger) (*api.ObjectMeta, er
return &m, nil return &m, nil
} }
func (c *Client) WatchGet(m *api.ObjectMeta) (*tpr.Kuberneteswatchtrigger, error) { func (c *Client) WatchGet(m *metav1.ObjectMeta) (*crd.KubernetesWatchTrigger, error) {
relativeUrl := fmt.Sprintf("watches/%v", m.Name) relativeUrl := fmt.Sprintf("watches/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
@@ -69,7 +69,7 @@ func (c *Client) WatchGet(m *api.ObjectMeta) (*tpr.Kuberneteswatchtrigger, error
return nil, err return nil, err
} }
var w tpr.Kuberneteswatchtrigger var w crd.KubernetesWatchTrigger
err = json.Unmarshal(body, &w) err = json.Unmarshal(body, &w)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -78,18 +78,18 @@ func (c *Client) WatchGet(m *api.ObjectMeta) (*tpr.Kuberneteswatchtrigger, error
return &w, nil return &w, nil
} }
func (c *Client) WatchUpdate(w *tpr.Kuberneteswatchtrigger) (*api.ObjectMeta, error) { func (c *Client) WatchUpdate(w *crd.KubernetesWatchTrigger) (*metav1.ObjectMeta, error) {
return nil, fission.MakeError(fission.ErrorNotImplmented, return nil, fission.MakeError(fission.ErrorNotImplmented,
"watch update not implemented") "watch update not implemented")
} }
func (c *Client) WatchDelete(m *api.ObjectMeta) error { func (c *Client) WatchDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("watches/%v", m.Name) relativeUrl := fmt.Sprintf("watches/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl) return c.delete(relativeUrl)
} }
func (c *Client) WatchList() ([]tpr.Kuberneteswatchtrigger, error) { func (c *Client) WatchList() ([]crd.KubernetesWatchTrigger, error) {
resp, err := http.Get(c.url("watches")) resp, err := http.Get(c.url("watches"))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -101,7 +101,7 @@ func (c *Client) WatchList() ([]tpr.Kuberneteswatchtrigger, error) {
return nil, err return nil, err
} }
watches := make([]tpr.Kuberneteswatchtrigger, 0) watches := make([]crd.KubernetesWatchTrigger, 0)
err = json.Unmarshal(body, &watches) err = json.Unmarshal(body, &watches)
if err != nil { if err != nil {
return nil, err return nil, err
+11 -11
View File
@@ -22,12 +22,12 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (c *Client) MessageQueueTriggerCreate(t *tpr.Messagequeuetrigger) (*api.ObjectMeta, error) { func (c *Client) MessageQueueTriggerCreate(t *crd.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(t) reqbody, err := json.Marshal(t)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -44,7 +44,7 @@ func (c *Client) MessageQueueTriggerCreate(t *tpr.Messagequeuetrigger) (*api.Obj
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -53,7 +53,7 @@ func (c *Client) MessageQueueTriggerCreate(t *tpr.Messagequeuetrigger) (*api.Obj
return &m, nil return &m, nil
} }
func (c *Client) MessageQueueTriggerGet(m *api.ObjectMeta) (*tpr.Messagequeuetrigger, error) { func (c *Client) MessageQueueTriggerGet(m *metav1.ObjectMeta) (*crd.MessageQueueTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name) relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
@@ -68,7 +68,7 @@ func (c *Client) MessageQueueTriggerGet(m *api.ObjectMeta) (*tpr.Messagequeuetri
return nil, err return nil, err
} }
var t tpr.Messagequeuetrigger var t crd.MessageQueueTrigger
err = json.Unmarshal(body, &t) err = json.Unmarshal(body, &t)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -77,7 +77,7 @@ func (c *Client) MessageQueueTriggerGet(m *api.ObjectMeta) (*tpr.Messagequeuetri
return &t, nil return &t, nil
} }
func (c *Client) MessageQueueTriggerUpdate(mqTrigger *tpr.Messagequeuetrigger) (*api.ObjectMeta, error) { func (c *Client) MessageQueueTriggerUpdate(mqTrigger *crd.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(mqTrigger) reqbody, err := json.Marshal(mqTrigger)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -95,7 +95,7 @@ func (c *Client) MessageQueueTriggerUpdate(mqTrigger *tpr.Messagequeuetrigger) (
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -103,13 +103,13 @@ func (c *Client) MessageQueueTriggerUpdate(mqTrigger *tpr.Messagequeuetrigger) (
return &m, nil return &m, nil
} }
func (c *Client) MessageQueueTriggerDelete(m *api.ObjectMeta) error { func (c *Client) MessageQueueTriggerDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name) relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl) return c.delete(relativeUrl)
} }
func (c *Client) MessageQueueTriggerList(mqType string) ([]tpr.Messagequeuetrigger, error) { func (c *Client) MessageQueueTriggerList(mqType string) ([]crd.MessageQueueTrigger, error) {
relativeUrl := "triggers/messagequeue" relativeUrl := "triggers/messagequeue"
if len(mqType) > 0 { if len(mqType) > 0 {
// TODO remove this, replace with field selector // TODO remove this, replace with field selector
@@ -127,7 +127,7 @@ func (c *Client) MessageQueueTriggerList(mqType string) ([]tpr.Messagequeuetrigg
return nil, err return nil, err
} }
triggers := make([]tpr.Messagequeuetrigger, 0) triggers := make([]crd.MessageQueueTrigger, 0)
err = json.Unmarshal(body, &triggers) err = json.Unmarshal(body, &triggers)
if err != nil { if err != nil {
return nil, err return nil, err
+11 -11
View File
@@ -22,12 +22,12 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (c *Client) PackageCreate(f *tpr.Package) (*api.ObjectMeta, error) { func (c *Client) PackageCreate(f *crd.Package) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(f) reqbody, err := json.Marshal(f)
if err != nil { if err != nil {
@@ -45,7 +45,7 @@ func (c *Client) PackageCreate(f *tpr.Package) (*api.ObjectMeta, error) {
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -54,7 +54,7 @@ func (c *Client) PackageCreate(f *tpr.Package) (*api.ObjectMeta, error) {
return &m, nil return &m, nil
} }
func (c *Client) PackageGet(m *api.ObjectMeta) (*tpr.Package, error) { func (c *Client) PackageGet(m *metav1.ObjectMeta) (*crd.Package, error) {
relativeUrl := fmt.Sprintf("packages/%v", m.Name) relativeUrl := fmt.Sprintf("packages/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
@@ -69,7 +69,7 @@ func (c *Client) PackageGet(m *api.ObjectMeta) (*tpr.Package, error) {
return nil, err return nil, err
} }
var f tpr.Package var f crd.Package
err = json.Unmarshal(body, &f) err = json.Unmarshal(body, &f)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -78,7 +78,7 @@ func (c *Client) PackageGet(m *api.ObjectMeta) (*tpr.Package, error) {
return &f, nil return &f, nil
} }
func (c *Client) PackageUpdate(f *tpr.Package) (*api.ObjectMeta, error) { func (c *Client) PackageUpdate(f *crd.Package) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(f) reqbody, err := json.Marshal(f)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -96,7 +96,7 @@ func (c *Client) PackageUpdate(f *tpr.Package) (*api.ObjectMeta, error) {
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -104,13 +104,13 @@ func (c *Client) PackageUpdate(f *tpr.Package) (*api.ObjectMeta, error) {
return &m, nil return &m, nil
} }
func (c *Client) PackageDelete(m *api.ObjectMeta) error { func (c *Client) PackageDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("packages/%v", m.Name) relativeUrl := fmt.Sprintf("packages/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl) return c.delete(relativeUrl)
} }
func (c *Client) PackageList() ([]tpr.Package, error) { func (c *Client) PackageList() ([]crd.Package, error) {
resp, err := http.Get(c.url("packages")) resp, err := http.Get(c.url("packages"))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -122,7 +122,7 @@ func (c *Client) PackageList() ([]tpr.Package, error) {
return nil, err return nil, err
} }
funcs := make([]tpr.Package, 0) funcs := make([]crd.Package, 0)
err = json.Unmarshal(body, &funcs) err = json.Unmarshal(body, &funcs)
if err != nil { if err != nil {
return nil, err return nil, err
+11 -11
View File
@@ -22,12 +22,12 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (c *Client) TimeTriggerCreate(t *tpr.Timetrigger) (*api.ObjectMeta, error) { func (c *Client) TimeTriggerCreate(t *crd.TimeTrigger) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(t) reqbody, err := json.Marshal(t)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -44,7 +44,7 @@ func (c *Client) TimeTriggerCreate(t *tpr.Timetrigger) (*api.ObjectMeta, error)
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -53,7 +53,7 @@ func (c *Client) TimeTriggerCreate(t *tpr.Timetrigger) (*api.ObjectMeta, error)
return &m, nil return &m, nil
} }
func (c *Client) TimeTriggerGet(m *api.ObjectMeta) (*tpr.Timetrigger, error) { func (c *Client) TimeTriggerGet(m *metav1.ObjectMeta) (*crd.TimeTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name) relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
@@ -68,7 +68,7 @@ func (c *Client) TimeTriggerGet(m *api.ObjectMeta) (*tpr.Timetrigger, error) {
return nil, err return nil, err
} }
var t tpr.Timetrigger var t crd.TimeTrigger
err = json.Unmarshal(body, &t) err = json.Unmarshal(body, &t)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -77,7 +77,7 @@ func (c *Client) TimeTriggerGet(m *api.ObjectMeta) (*tpr.Timetrigger, error) {
return &t, nil return &t, nil
} }
func (c *Client) TimeTriggerUpdate(t *tpr.Timetrigger) (*api.ObjectMeta, error) { func (c *Client) TimeTriggerUpdate(t *crd.TimeTrigger) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(t) reqbody, err := json.Marshal(t)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -95,7 +95,7 @@ func (c *Client) TimeTriggerUpdate(t *tpr.Timetrigger) (*api.ObjectMeta, error)
return nil, err return nil, err
} }
var m api.ObjectMeta var m metav1.ObjectMeta
err = json.Unmarshal(body, &m) err = json.Unmarshal(body, &m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -103,13 +103,13 @@ func (c *Client) TimeTriggerUpdate(t *tpr.Timetrigger) (*api.ObjectMeta, error)
return &m, nil return &m, nil
} }
func (c *Client) TimeTriggerDelete(m *api.ObjectMeta) error { func (c *Client) TimeTriggerDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name) relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace) relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl) return c.delete(relativeUrl)
} }
func (c *Client) TimeTriggerList() ([]tpr.Timetrigger, error) { func (c *Client) TimeTriggerList() ([]crd.TimeTrigger, error) {
resp, err := http.Get(c.url("triggers/time")) resp, err := http.Get(c.url("triggers/time"))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -121,7 +121,7 @@ func (c *Client) TimeTriggerList() ([]tpr.Timetrigger, error) {
return nil, err return nil, err
} }
triggers := make([]tpr.Timetrigger, 0) triggers := make([]crd.TimeTrigger, 0)
err = json.Unmarshal(body, &triggers) err = json.Unmarshal(body, &triggers)
if err != nil { if err != nil {
return nil, err return nil, err
+5 -5
View File
@@ -19,21 +19,21 @@ package controller
import ( import (
"log" "log"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func Start(port int) { func Start(port int) {
fc, kc, err := tpr.MakeFissionClient() fc, _, apiExtClient, err := crd.MakeFissionClient()
if err != nil { if err != nil {
log.Fatalf("Failed to connect to K8s API: %v", err) log.Fatalf("Failed to connect to K8s API: %v", err)
} }
err = tpr.EnsureFissionTPRs(kc) err = crd.EnsureFissionCRDs(apiExtClient)
if err != nil { if err != nil {
log.Fatalf("Failed to create fission TPRs: %v", err) log.Fatalf("Failed to create fission CRDs: %v", err)
} }
fc.WaitForTPRs() fc.WaitForCRDs()
api, err := MakeAPI() api, err := MakeAPI()
if err != nil { if err != nil {
+4 -4
View File
@@ -20,15 +20,15 @@ import (
"errors" "errors"
"regexp" "regexp"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func makeTPRBackedAPI() (*API, error) { func makeCRDBackedAPI() (*API, error) {
fissionClient, _, err := tpr.MakeFissionClient() fissionClient, kubernetesClient, _, err := crd.MakeFissionClient()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &API{fissionClient: fissionClient}, nil return &API{fissionClient: fissionClient, kubernetesClient: kubernetesClient}, nil
} }
func validateResourceName(name string) error { func validateResourceName(name string) error {
+8 -8
View File
@@ -23,14 +23,14 @@ 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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (a *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) { func (a *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) {
envs, err := a.fissionClient.Environments(api.NamespaceAll).List(api.ListOptions{}) envs, err := a.fissionClient.Environments(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -52,7 +52,7 @@ func (a *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
return return
} }
var env tpr.Environment var env crd.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)
@@ -87,7 +87,7 @@ func (a *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
name := vars["environment"] name := vars["environment"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
env, err := a.fissionClient.Environments(ns).Get(name) env, err := a.fissionClient.Environments(ns).Get(name)
@@ -115,7 +115,7 @@ func (a *API) EnvironmentApiUpdate(w http.ResponseWriter, r *http.Request) {
return return
} }
var env tpr.Environment var env crd.Environment
err = json.Unmarshal(body, &env) err = json.Unmarshal(body, &env)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -148,10 +148,10 @@ func (a *API) EnvironmentApiDelete(w http.ResponseWriter, r *http.Request) {
name := vars["environment"] name := vars["environment"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
err := a.fissionClient.Environments(ns).Delete(name, &api.DeleteOptions{}) err := a.fissionClient.Environments(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
+76 -8
View File
@@ -18,21 +18,27 @@ package controller
import ( import (
"encoding/json" "encoding/json"
"errors"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"sort"
"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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/pkg/api"
"k8s.io/client-go/pkg/api/v1"
restclient "k8s.io/client-go/rest"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (a *API) FunctionApiList(w http.ResponseWriter, r *http.Request) { func (a *API) FunctionApiList(w http.ResponseWriter, r *http.Request) {
funcs, err := a.fissionClient.Functions(api.NamespaceAll).List(api.ListOptions{}) funcs, err := a.fissionClient.Functions(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -54,7 +60,7 @@ func (a *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
return return
} }
var f tpr.Function var f crd.Function
err = json.Unmarshal(body, &f) err = json.Unmarshal(body, &f)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -88,7 +94,7 @@ func (a *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
name := vars["function"] name := vars["function"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
f, err := a.fissionClient.Functions(ns).Get(name) f, err := a.fissionClient.Functions(ns).Get(name)
@@ -115,7 +121,7 @@ func (a *API) FunctionApiUpdate(w http.ResponseWriter, r *http.Request) {
return return
} }
var f tpr.Function var f crd.Function
err = json.Unmarshal(body, &f) err = json.Unmarshal(body, &f)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -147,10 +153,10 @@ func (a *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
name := vars["function"] name := vars["function"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
err := a.fissionClient.Functions(ns).Delete(name, &api.DeleteOptions{}) err := a.fissionClient.Functions(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -188,3 +194,65 @@ func (a *API) FunctionLogsApiPost(w http.ResponseWriter, r *http.Request) {
} }
proxy.ServeHTTP(w, r) proxy.ServeHTTP(w, r)
} }
// FunctionPodLogs : Get logs for a function directly from pod
func (a *API) FunctionPodLogs(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
fnName := vars["function"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = "fission-function"
}
f, err := a.fissionClient.Functions(api.NamespaceDefault).Get(fnName)
if err != nil {
a.respondWithError(w, err)
return
}
envName := f.Spec.Environment.Name
if err != nil {
a.respondWithError(w, err)
return
}
// Get function Pods first
selector := "functionName=" + fnName
podList, err := a.kubernetesClient.Core().Pods(ns).List(metav1.ListOptions{LabelSelector: selector})
if err != nil {
a.respondWithError(w, err)
return
}
// Get the logs for last Pod executed
pods := podList.Items
sort.Slice(pods, func(i, j int) bool {
itime := pods[i].ObjectMeta.CreationTimestamp.Time
jtime := pods[j].ObjectMeta.CreationTimestamp.Time
return itime.After(jtime)
})
podLogOpts := v1.PodLogOptions{Container: envName} // Only the env container, not fetcher
var podLogsReq *restclient.Request
if len(pods) > 0 {
podLogsReq = a.kubernetesClient.Core().Pods(ns).GetLogs(pods[0].ObjectMeta.Name, &podLogOpts)
} else {
a.respondWithError(w, errors.New("No active pods found"))
return
}
podLogs, err := podLogsReq.Stream()
if err != nil {
a.respondWithError(w, err)
return
}
defer podLogs.Close()
_, err = io.Copy(w, podLogs)
if err != nil {
a.respondWithError(w, err)
return
}
return
}
+17 -17
View File
@@ -23,14 +23,14 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (a *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) { func (a *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
triggers, err := a.fissionClient.Httptriggers(api.NamespaceAll).List(api.ListOptions{}) triggers, err := a.fissionClient.HTTPTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -45,15 +45,15 @@ func (a *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
a.respondWithSuccess(w, resp) a.respondWithSuccess(w, resp)
} }
func (a *API) checkHttpTriggerDuplicates(t *tpr.Httptrigger) error { func (a *API) checkHTTPTriggerDuplicates(t *crd.HTTPTrigger) error {
triggers, err := a.fissionClient.Httptriggers(api.NamespaceAll).List(api.ListOptions{}) triggers, err := a.fissionClient.HTTPTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
for _, ht := range triggers.Items { for _, ht := range triggers.Items {
if ht.Spec.RelativeURL == t.Spec.RelativeURL && ht.Spec.Method == t.Spec.Method { if ht.Spec.RelativeURL == t.Spec.RelativeURL && ht.Spec.Method == t.Spec.Method && ht.Spec.Host == t.Spec.Host {
return fission.MakeError(fission.ErrorNameExists, return fission.MakeError(fission.ErrorNameExists,
fmt.Sprintf("HTTPTrigger with same URL & method already exists (%v)", fmt.Sprintf("HTTPTrigger with same Host, URL & method already exists (%v)",
ht.Metadata.Name)) ht.Metadata.Name))
} }
} }
@@ -67,7 +67,7 @@ func (a *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
return return
} }
var t tpr.Httptrigger var t crd.HTTPTrigger
err = json.Unmarshal(body, &t) err = json.Unmarshal(body, &t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -81,13 +81,13 @@ func (a *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
} }
// Ensure we don't have a duplicate HTTP route defined (same URL and method) // Ensure we don't have a duplicate HTTP route defined (same URL and method)
err = a.checkHttpTriggerDuplicates(&t) err = a.checkHTTPTriggerDuplicates(&t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
} }
tnew, err := a.fissionClient.Httptriggers(t.Metadata.Namespace).Create(&t) tnew, err := a.fissionClient.HTTPTriggers(t.Metadata.Namespace).Create(&t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -108,10 +108,10 @@ func (a *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
name := vars["httpTrigger"] name := vars["httpTrigger"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
t, err := a.fissionClient.Httptriggers(ns).Get(name) t, err := a.fissionClient.HTTPTriggers(ns).Get(name)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -136,7 +136,7 @@ func (a *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
return return
} }
var t tpr.Httptrigger var t crd.HTTPTrigger
err = json.Unmarshal(body, &t) err = json.Unmarshal(body, &t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -149,13 +149,13 @@ func (a *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
return return
} }
err = a.checkHttpTriggerDuplicates(&t) err = a.checkHTTPTriggerDuplicates(&t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
} }
tnew, err := a.fissionClient.Httptriggers(t.Metadata.Namespace).Update(&t) tnew, err := a.fissionClient.HTTPTriggers(t.Metadata.Namespace).Update(&t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -174,10 +174,10 @@ func (a *API) HTTPTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
name := vars["httpTrigger"] name := vars["httpTrigger"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
err := a.fissionClient.Httptriggers(ns).Delete(name, &api.DeleteOptions{}) err := a.fissionClient.HTTPTriggers(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
+11 -11
View File
@@ -22,15 +22,15 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (a *API) MessageQueueTriggerApiList(w http.ResponseWriter, r *http.Request) { func (a *API) MessageQueueTriggerApiList(w http.ResponseWriter, r *http.Request) {
//mqType := r.FormValue("mqtype") // ignored for now //mqType := r.FormValue("mqtype") // ignored for now
triggers, err := a.fissionClient.Messagequeuetriggers(api.NamespaceAll).List(api.ListOptions{}) triggers, err := a.fissionClient.MessageQueueTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -50,7 +50,7 @@ func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Reques
return return
} }
var mqTrigger tpr.Messagequeuetrigger var mqTrigger crd.MessageQueueTrigger
err = json.Unmarshal(body, &mqTrigger) err = json.Unmarshal(body, &mqTrigger)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -63,7 +63,7 @@ func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Reques
return return
} }
tnew, err := a.fissionClient.Messagequeuetriggers(mqTrigger.Metadata.Namespace).Create(&mqTrigger) tnew, err := a.fissionClient.MessageQueueTriggers(mqTrigger.Metadata.Namespace).Create(&mqTrigger)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -83,10 +83,10 @@ func (a *API) MessageQueueTriggerApiGet(w http.ResponseWriter, r *http.Request)
name := vars["mqTrigger"] name := vars["mqTrigger"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
mqTrigger, err := a.fissionClient.Messagequeuetriggers(ns).Get(name) mqTrigger, err := a.fissionClient.MessageQueueTriggers(ns).Get(name)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -109,7 +109,7 @@ func (a *API) MessageQueueTriggerApiUpdate(w http.ResponseWriter, r *http.Reques
return return
} }
var mqTrigger tpr.Messagequeuetrigger var mqTrigger crd.MessageQueueTrigger
err = json.Unmarshal(body, &mqTrigger) err = json.Unmarshal(body, &mqTrigger)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -122,7 +122,7 @@ func (a *API) MessageQueueTriggerApiUpdate(w http.ResponseWriter, r *http.Reques
return return
} }
tnew, err := a.fissionClient.Messagequeuetriggers(mqTrigger.Metadata.Namespace).Update(&mqTrigger) tnew, err := a.fissionClient.MessageQueueTriggers(mqTrigger.Metadata.Namespace).Update(&mqTrigger)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -141,10 +141,10 @@ func (a *API) MessageQueueTriggerApiDelete(w http.ResponseWriter, r *http.Reques
name := vars["mqTrigger"] name := vars["mqTrigger"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
err := a.fissionClient.Messagequeuetriggers(ns).Delete(name, &api.DeleteOptions{}) err := a.fissionClient.MessageQueueTriggers(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
+8 -8
View File
@@ -22,14 +22,14 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (a *API) PackageApiList(w http.ResponseWriter, r *http.Request) { func (a *API) PackageApiList(w http.ResponseWriter, r *http.Request) {
funcs, err := a.fissionClient.Packages(api.NamespaceAll).List(api.ListOptions{}) funcs, err := a.fissionClient.Packages(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -51,7 +51,7 @@ func (a *API) PackageApiCreate(w http.ResponseWriter, r *http.Request) {
return return
} }
var f tpr.Package var f crd.Package
err = json.Unmarshal(body, &f) err = json.Unmarshal(body, &f)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -97,7 +97,7 @@ func (a *API) PackageApiGet(w http.ResponseWriter, r *http.Request) {
name := vars["package"] name := vars["package"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
raw := r.FormValue("raw") // just the deployment pkg raw := r.FormValue("raw") // just the deployment pkg
@@ -130,7 +130,7 @@ func (a *API) PackageApiUpdate(w http.ResponseWriter, r *http.Request) {
return return
} }
var f tpr.Package var f crd.Package
err = json.Unmarshal(body, &f) err = json.Unmarshal(body, &f)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -162,10 +162,10 @@ func (a *API) PackageApiDelete(w http.ResponseWriter, r *http.Request) {
name := vars["package"] name := vars["package"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
err := a.fissionClient.Packages(ns).Delete(name, &api.DeleteOptions{}) err := a.fissionClient.Packages(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
+11 -11
View File
@@ -23,14 +23,14 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/robfig/cron" "github.com/robfig/cron"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (a *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) { func (a *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) {
triggers, err := a.fissionClient.Timetriggers(api.NamespaceAll).List(api.ListOptions{}) triggers, err := a.fissionClient.TimeTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -52,7 +52,7 @@ func (a *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
return return
} }
var t tpr.Timetrigger var t crd.TimeTrigger
err = json.Unmarshal(body, &t) err = json.Unmarshal(body, &t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -73,7 +73,7 @@ func (a *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
return return
} }
tnew, err := a.fissionClient.Timetriggers(t.Metadata.Namespace).Create(&t) tnew, err := a.fissionClient.TimeTriggers(t.Metadata.Namespace).Create(&t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -94,10 +94,10 @@ func (a *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
name := vars["timeTrigger"] name := vars["timeTrigger"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
t, err := a.fissionClient.Timetriggers(ns).Get(name) t, err := a.fissionClient.TimeTriggers(ns).Get(name)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -122,7 +122,7 @@ func (a *API) TimeTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
return return
} }
var t tpr.Timetrigger var t crd.TimeTrigger
err = json.Unmarshal(body, &t) err = json.Unmarshal(body, &t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -142,7 +142,7 @@ func (a *API) TimeTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
return return
} }
tnew, err := a.fissionClient.Timetriggers(t.Metadata.Namespace).Update(&t) tnew, err := a.fissionClient.TimeTriggers(t.Metadata.Namespace).Update(&t)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -161,10 +161,10 @@ func (a *API) TimeTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
name := vars["timeTrigger"] name := vars["timeTrigger"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
err := a.fissionClient.Timetriggers(ns).Delete(name, &api.DeleteOptions{}) err := a.fissionClient.TimeTriggers(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
+64
View File
@@ -0,0 +1,64 @@
/*
Copyright 2017 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/crd"
)
func (a *API) Tpr2crdApi(w http.ResponseWriter, r *http.Request) {
_, kubeClient, _, err := crd.MakeFissionClient()
if err != nil {
a.respondWithError(w, err)
return
}
fissionTprs := []string{
"function.fission.io",
"environment.fission.io",
"httptrigger.fission.io",
"kuberneteswatchtrigger.fission.io",
"timetrigger.fission.io",
"messagequeuetrigger.fission.io",
"package.fission.io",
}
tprList, err := kubeClient.ThirdPartyResources().List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
for _, tpr := range tprList.Items {
for _, tprName := range fissionTprs {
if tpr.Name == tprName {
err := kubeClient.ThirdPartyResources().Delete(tpr.Name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
break
}
}
}
a.respondWithSuccess(w, nil)
}
+9 -9
View File
@@ -22,14 +22,14 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func (a *API) WatchApiList(w http.ResponseWriter, r *http.Request) { func (a *API) WatchApiList(w http.ResponseWriter, r *http.Request) {
watches, err := a.fissionClient.Kuberneteswatchtriggers(api.NamespaceAll).List(api.ListOptions{}) watches, err := a.fissionClient.KubernetesWatchTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -51,7 +51,7 @@ func (a *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
return return
} }
var watch tpr.Kuberneteswatchtrigger var watch crd.KubernetesWatchTrigger
err = json.Unmarshal(body, &watch) err = json.Unmarshal(body, &watch)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
@@ -66,7 +66,7 @@ func (a *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
// TODO check for duplicate watches // TODO check for duplicate watches
wnew, err := a.fissionClient.Kuberneteswatchtriggers(watch.Metadata.Namespace).Create(&watch) wnew, err := a.fissionClient.KubernetesWatchTriggers(watch.Metadata.Namespace).Create(&watch)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -87,10 +87,10 @@ func (a *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
name := vars["watch"] name := vars["watch"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
watch, err := a.fissionClient.Kuberneteswatchtriggers(ns).Get(name) watch, err := a.fissionClient.KubernetesWatchTriggers(ns).Get(name)
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
@@ -115,10 +115,10 @@ func (a *API) WatchApiDelete(w http.ResponseWriter, r *http.Request) {
name := vars["watch"] name := vars["watch"]
ns := vars["namespace"] ns := vars["namespace"]
if len(ns) == 0 { if len(ns) == 0 {
ns = api.NamespaceDefault ns = metav1.NamespaceDefault
} }
err := a.fissionClient.Kuberneteswatchtriggers(ns).Delete(name, &api.DeleteOptions{}) err := a.fissionClient.KubernetesWatchTriggers(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil { if err != nil {
a.respondWithError(w, err) a.respondWithError(w, err)
return return
+220
View File
@@ -0,0 +1,220 @@
/*
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 crd
import (
"errors"
"os"
"time"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
type (
FissionClient struct {
crdClient *rest.RESTClient
}
)
// Get a kubernetes client using the kubeconfig file at the
// environment var $KUBECONFIG, or an in-cluster config if that's
// undefined.
func GetKubernetesClient() (*rest.Config, *kubernetes.Clientset, *apiextensionsclient.Clientset, error) {
var config *rest.Config
var err error
// get the config, either from kubeconfig or using our
// in-cluster service account
kubeConfig := os.Getenv("KUBECONFIG")
if len(kubeConfig) != 0 {
config, err = clientcmd.BuildConfigFromFlags("", kubeConfig)
if err != nil {
return nil, nil, nil, err
}
} else {
config, err = rest.InClusterConfig()
if err != nil {
return nil, nil, nil, err
}
}
// creates the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, nil, nil, err
}
apiExtClientset, err := apiextensionsclient.NewForConfig(config)
if err != nil {
return nil, nil, nil, err
}
return config, clientset, apiExtClientset, nil
}
// GetCrdClient gets a CRD client config
func GetCrdClient(config *rest.Config) (*rest.RESTClient, error) {
// mutate config to add our types
configureClient(config)
// make a REST client with that config
return rest.RESTClientFor(config)
}
// configureClient sets up a REST client for Fission CRD types.
//
// This is copied from the client-go CRD example. (I don't understand
// all of it completely.) It registers our types with the global API
// "scheme" (api.Scheme), which keeps a directory of types [I guess so
// it can use the string in the Kind field to make a Go object?]. It
// also puts the fission CRD types under a "group version" which we
// create for our CRDs types.
func configureClient(config *rest.Config) {
groupversion := schema.GroupVersion{
Group: "fission.io",
Version: "v1",
}
config.GroupVersion = &groupversion
config.APIPath = "/apis"
config.ContentType = runtime.ContentTypeJSON
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
schemeBuilder := runtime.NewSchemeBuilder(
func(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
groupversion,
&Function{},
&FunctionList{},
&metav1.ListOptions{},
&metav1.DeleteOptions{},
)
scheme.AddKnownTypes(
groupversion,
&Environment{},
&EnvironmentList{},
&metav1.ListOptions{},
&metav1.DeleteOptions{},
)
scheme.AddKnownTypes(
groupversion,
&HTTPTrigger{},
&HTTPTriggerList{},
&metav1.ListOptions{},
&metav1.DeleteOptions{},
)
scheme.AddKnownTypes(
groupversion,
&KubernetesWatchTrigger{},
&KubernetesWatchTriggerList{},
&metav1.ListOptions{},
&metav1.DeleteOptions{},
)
scheme.AddKnownTypes(
groupversion,
&TimeTrigger{},
&TimeTriggerList{},
&metav1.ListOptions{},
&metav1.DeleteOptions{},
)
scheme.AddKnownTypes(
groupversion,
&MessageQueueTrigger{},
&MessageQueueTriggerList{},
&metav1.ListOptions{},
&metav1.DeleteOptions{},
)
scheme.AddKnownTypes(
groupversion,
&Package{},
&PackageList{},
&metav1.ListOptions{},
&metav1.DeleteOptions{},
)
return nil
})
schemeBuilder.AddToScheme(scheme.Scheme)
}
func waitForCRDs(crdClient *rest.RESTClient) error {
start := time.Now()
for {
fi := MakeFunctionInterface(crdClient, metav1.NamespaceDefault)
_, err := fi.List(metav1.ListOptions{})
if err != nil {
time.Sleep(100 * time.Millisecond)
} else {
return nil
}
if time.Now().Sub(start) > 30*time.Second {
return errors.New("timeout waiting for CRDs")
}
}
}
func MakeFissionClient() (*FissionClient, *kubernetes.Clientset, *apiextensionsclient.Clientset, error) {
config, kubeClient, apiExtClient, err := GetKubernetesClient()
if err != nil {
return nil, nil, nil, err
}
crdClient, err := GetCrdClient(config)
if err != nil {
return nil, nil, nil, err
}
fc := &FissionClient{
crdClient: crdClient,
}
return fc, kubeClient, apiExtClient, nil
}
func (fc *FissionClient) Functions(ns string) FunctionInterface {
return MakeFunctionInterface(fc.crdClient, ns)
}
func (fc *FissionClient) Environments(ns string) EnvironmentInterface {
return MakeEnvironmentInterface(fc.crdClient, ns)
}
func (fc *FissionClient) HTTPTriggers(ns string) HTTPTriggerInterface {
return MakeHTTPTriggerInterface(fc.crdClient, ns)
}
func (fc *FissionClient) KubernetesWatchTriggers(ns string) KubernetesWatchTriggerInterface {
return MakeKubernetesWatchTriggerInterface(fc.crdClient, ns)
}
func (fc *FissionClient) TimeTriggers(ns string) TimeTriggerInterface {
return MakeTimeTriggerInterface(fc.crdClient, ns)
}
func (fc *FissionClient) MessageQueueTriggers(ns string) MessageQueueTriggerInterface {
return MakeMessageQueueTriggerInterface(fc.crdClient, ns)
}
func (fc *FissionClient) Packages(ns string) PackageInterface {
return MakePackageInterface(fc.crdClient, ns)
}
func (fc *FissionClient) WaitForCRDs() {
waitForCRDs(fc.crdClient)
}
func (fc *FissionClient) GetCrdClient() *rest.RESTClient {
return fc.crdClient
}
+170
View File
@@ -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 crd
import (
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
crdGroupName = "fission.io"
crdVersion = "v1"
)
// ensureCRD checks if the given CRD type exists, and creates it if
// needed. (Note that this creates the CRD type; it doesn't create any
// _instances_ of that type.)
func ensureCRD(clientset *apiextensionsclient.Clientset, crd *apiextensionsv1beta1.CustomResourceDefinition) error {
_, err := clientset.Apiextensions().CustomResourceDefinitions().Get(crd.ObjectMeta.Name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
_, err := clientset.Apiextensions().CustomResourceDefinitions().Create(crd)
if err != nil {
return err
}
}
}
return nil
}
// Ensure CRDs
func EnsureFissionCRDs(clientset *apiextensionsclient.Clientset) error {
crds := []apiextensionsv1beta1.CustomResourceDefinition{
// Functions
{
ObjectMeta: metav1.ObjectMeta{
Name: "functions.fission.io",
},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: crdGroupName,
Version: crdVersion,
Scope: apiextensionsv1beta1.NamespaceScoped,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Kind: "Function",
Plural: "functions",
Singular: "function",
},
},
},
// Environments (function containers)
{
ObjectMeta: metav1.ObjectMeta{
Name: "environments.fission.io",
},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: crdGroupName,
Version: crdVersion,
Scope: apiextensionsv1beta1.NamespaceScoped,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Kind: "Environment",
Plural: "environments",
Singular: "environment",
},
},
},
// HTTP triggers for functions
{
ObjectMeta: metav1.ObjectMeta{
Name: "httptriggers.fission.io",
},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: crdGroupName,
Version: crdVersion,
Scope: apiextensionsv1beta1.NamespaceScoped,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Kind: "HTTPTrigger",
Plural: "httptriggers",
Singular: "httptrigger",
},
},
},
// Kubernetes watch triggers for functions
{
ObjectMeta: metav1.ObjectMeta{
Name: "kuberneteswatchtriggers.fission.io",
},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: crdGroupName,
Version: crdVersion,
Scope: apiextensionsv1beta1.NamespaceScoped,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Kind: "KubernetesWatchTrigger",
Plural: "kuberneteswatchtriggers",
Singular: "kuberneteswatchtrigger",
},
},
},
// Time-based triggers for functions
{
ObjectMeta: metav1.ObjectMeta{
Name: "timetriggers.fission.io",
},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: crdGroupName,
Version: crdVersion,
Scope: apiextensionsv1beta1.NamespaceScoped,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Kind: "TimeTrigger",
Plural: "timetriggers",
Singular: "timetrigger",
},
},
},
// Message queue triggers for functions
{
ObjectMeta: metav1.ObjectMeta{
Name: "messagequeuetriggers.fission.io",
},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: crdGroupName,
Version: crdVersion,
Scope: apiextensionsv1beta1.NamespaceScoped,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Kind: "MessageQueueTrigger",
Plural: "messagequeuetriggers",
Singular: "messagequeuetrigger",
},
},
},
// Packages: archives containing source or binaries for one or more functions
{
ObjectMeta: metav1.ObjectMeta{
Name: "packages.fission.io",
},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: crdGroupName,
Version: crdVersion,
Scope: apiextensionsv1beta1.NamespaceScoped,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Kind: "Package",
Plural: "packages",
Singular: "package",
},
},
},
}
for _, crd := range crds {
err := ensureCRD(clientset, &crd)
if err != nil {
return err
}
}
return nil
}
+69 -58
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"log" "log"
@@ -22,9 +22,8 @@ import (
"testing" "testing"
"time" "time"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api/unversioned" "k8s.io/client-go/rest"
"k8s.io/client-go/1.5/rest"
"github.com/fission/fission" "github.com/fission/fission"
) )
@@ -35,15 +34,16 @@ func panicIf(err error) {
} }
} }
func functionTests(tprClient *rest.RESTClient) { func functionTests(crdClient *rest.RESTClient) {
// sample function object // sample function object
function := &Function{ function := &Function{
TypeMeta: unversioned.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: "Function", Kind: "Function",
APIVersion: "fission.io/v1", APIVersion: "fission.io/v1",
}, },
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.FunctionSpec{ Spec: fission.FunctionSpec{
Package: fission.FunctionPackageRef{ Package: fission.FunctionPackageRef{
@@ -60,7 +60,7 @@ func functionTests(tprClient *rest.RESTClient) {
} }
// Test function CRUD // Test function CRUD
fi := MakeFunctionInterface(tprClient, api.NamespaceDefault) fi := MakeFunctionInterface(crdClient, metav1.NamespaceDefault)
// cleanup from old crashed tests, ignore errors // cleanup from old crashed tests, ignore errors
fi.Delete(function.Metadata.Name, nil) fi.Delete(function.Metadata.Name, nil)
@@ -82,6 +82,7 @@ func functionTests(tprClient *rest.RESTClient) {
log.Printf("f.Metadata = %#v", f.Metadata) log.Printf("f.Metadata = %#v", f.Metadata)
// update // update
function.Metadata.ResourceVersion = f.Metadata.ResourceVersion
function.Spec.Environment.Name = "yyy" function.Spec.Environment.Name = "yyy"
f, err = fi.Update(function) f, err = fi.Update(function)
panicIf(err) panicIf(err)
@@ -89,12 +90,12 @@ func functionTests(tprClient *rest.RESTClient) {
log.Printf("f.Metadata = %#v", f.Metadata) log.Printf("f.Metadata = %#v", f.Metadata)
// list // list
fl, err := fi.List(api.ListOptions{}) fl, err := fi.List(metav1.ListOptions{})
panicIf(err) panicIf(err)
if len(fl.Items) != 1 { if len(fl.Items) != 1 {
log.Panicf("wrong count from list: %v", fl) log.Panicf("wrong count from function list: %v", len(fl.Items))
} }
if fl.Items[0].Spec.Environment.Name != function.Spec.Environment.Name { if fl.Items[0].Spec.Environment.Name != f.Spec.Environment.Name {
log.Panicf("bad object from list: %v", fl.Items[0]) log.Panicf("bad object from list: %v", fl.Items[0])
} }
@@ -103,10 +104,11 @@ func functionTests(tprClient *rest.RESTClient) {
panicIf(err) panicIf(err)
// start a watch // start a watch
wi, err := fi.Watch(api.ListOptions{}) wi, err := fi.Watch(metav1.ListOptions{})
panicIf(err) panicIf(err)
start := time.Now() start := time.Now()
function.Metadata.ResourceVersion = ""
f, err = fi.Create(function) f, err = fi.Create(function)
panicIf(err) panicIf(err)
defer fi.Delete(f.Metadata.Name, nil) defer fi.Delete(f.Metadata.Name, nil)
@@ -132,15 +134,16 @@ func functionTests(tprClient *rest.RESTClient) {
} }
func environmentTests(tprClient *rest.RESTClient) { func environmentTests(crdClient *rest.RESTClient) {
// sample environment object // sample environment object
environment := &Environment{ environment := &Environment{
TypeMeta: unversioned.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: "Environment", Kind: "Environment",
APIVersion: "fission.io/v1", APIVersion: "fission.io/v1",
}, },
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.EnvironmentSpec{ Spec: fission.EnvironmentSpec{
Runtime: fission.Runtime{ Runtime: fission.Runtime{
@@ -154,7 +157,7 @@ func environmentTests(tprClient *rest.RESTClient) {
} }
// Test environment CRUD // Test environment CRUD
ei := MakeEnvironmentInterface(tprClient, api.NamespaceDefault) ei := MakeEnvironmentInterface(crdClient, metav1.NamespaceDefault)
// cleanup from old crashed tests, ignore errors // cleanup from old crashed tests, ignore errors
ei.Delete(environment.Metadata.Name, nil) ei.Delete(environment.Metadata.Name, nil)
@@ -174,17 +177,18 @@ func environmentTests(tprClient *rest.RESTClient) {
} }
// update // update
environment.Metadata.ResourceVersion = e.Metadata.ResourceVersion
environment.Spec.Runtime.Image = "www" environment.Spec.Runtime.Image = "www"
e, err = ei.Update(environment) e, err = ei.Update(environment)
panicIf(err) panicIf(err)
// list // list
el, err := ei.List(api.ListOptions{}) el, err := ei.List(metav1.ListOptions{})
panicIf(err) panicIf(err)
if len(el.Items) != 1 { if len(el.Items) != 1 {
log.Panicf("wrong count from list: %v", len(el.Items)) log.Panicf("wrong count from environment list: %v", len(el.Items))
} }
if el.Items[0].Spec.Runtime.Image != environment.Spec.Runtime.Image { if el.Items[0].Spec.Runtime.Image != e.Spec.Runtime.Image {
log.Panicf("bad object from list: %v", el.Items[0]) log.Panicf("bad object from list: %v", el.Items[0])
} }
@@ -193,10 +197,11 @@ func environmentTests(tprClient *rest.RESTClient) {
panicIf(err) panicIf(err)
// start a watch // start a watch
wi, err := ei.Watch(api.ListOptions{}) wi, err := ei.Watch(metav1.ListOptions{})
panicIf(err) panicIf(err)
start := time.Now() start := time.Now()
environment.Metadata.ResourceVersion = ""
e, err = ei.Create(environment) e, err = ei.Create(environment)
panicIf(err) panicIf(err)
defer ei.Delete(e.Metadata.Name, nil) defer ei.Delete(e.Metadata.Name, nil)
@@ -222,15 +227,16 @@ func environmentTests(tprClient *rest.RESTClient) {
} }
func httpTriggerTests(tprClient *rest.RESTClient) { func httpTriggerTests(crdClient *rest.RESTClient) {
// sample httpTrigger object // sample httpTrigger object
httpTrigger := &Httptrigger{ httpTrigger := &HTTPTrigger{
TypeMeta: unversioned.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: "Httptrigger", Kind: "HTTPTrigger",
APIVersion: "fission.io/v1", APIVersion: "fission.io/v1",
}, },
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.HTTPTriggerSpec{ Spec: fission.HTTPTriggerSpec{
RelativeURL: "/hi", RelativeURL: "/hi",
@@ -243,7 +249,7 @@ func httpTriggerTests(tprClient *rest.RESTClient) {
} }
// Test httpTrigger CRUD // Test httpTrigger CRUD
ei := MakeHttptriggerInterface(tprClient, api.NamespaceDefault) ei := MakeHTTPTriggerInterface(crdClient, metav1.NamespaceDefault)
// cleanup from old crashed tests, ignore errors // cleanup from old crashed tests, ignore errors
ei.Delete(httpTrigger.Metadata.Name, nil) ei.Delete(httpTrigger.Metadata.Name, nil)
@@ -263,17 +269,18 @@ func httpTriggerTests(tprClient *rest.RESTClient) {
} }
// update // update
httpTrigger.Metadata.ResourceVersion = e.Metadata.ResourceVersion
httpTrigger.Spec.Method = "POST" httpTrigger.Spec.Method = "POST"
e, err = ei.Update(httpTrigger) e, err = ei.Update(httpTrigger)
panicIf(err) panicIf(err)
// list // list
el, err := ei.List(api.ListOptions{}) el, err := ei.List(metav1.ListOptions{})
panicIf(err) panicIf(err)
if len(el.Items) != 1 { if len(el.Items) != 1 {
log.Panicf("wrong count from list: %v", el) log.Panicf("wrong count from http trigger list: %v", len(el.Items))
} }
if el.Items[0].Spec.Method != httpTrigger.Spec.Method { if el.Items[0].Spec.Method != e.Spec.Method {
log.Panicf("bad object from list: %v", el.Items[0]) log.Panicf("bad object from list: %v", el.Items[0])
} }
@@ -282,10 +289,11 @@ func httpTriggerTests(tprClient *rest.RESTClient) {
panicIf(err) panicIf(err)
// start a watch // start a watch
wi, err := ei.Watch(api.ListOptions{}) wi, err := ei.Watch(metav1.ListOptions{})
panicIf(err) panicIf(err)
start := time.Now() start := time.Now()
httpTrigger.Metadata.ResourceVersion = ""
e, err = ei.Create(httpTrigger) e, err = ei.Create(httpTrigger)
panicIf(err) panicIf(err)
defer ei.Delete(e.Metadata.Name, nil) defer ei.Delete(e.Metadata.Name, nil)
@@ -298,9 +306,9 @@ func httpTriggerTests(tprClient *rest.RESTClient) {
log.Panicf("Didn't get watch event") log.Panicf("Didn't get watch event")
} }
case ev := <-wi.ResultChan(): case ev := <-wi.ResultChan():
obj, ok := ev.Object.(*Httptrigger) obj, ok := ev.Object.(*HTTPTrigger)
if !ok { if !ok {
log.Panicf("Can't cast to Httptrigger") log.Panicf("Can't cast to HTTPTrigger")
} }
if obj.Spec.Method != httpTrigger.Spec.Method { if obj.Spec.Method != httpTrigger.Spec.Method {
log.Panicf("Bad object from watch: %#v", obj) log.Panicf("Bad object from watch: %#v", obj)
@@ -311,15 +319,16 @@ func httpTriggerTests(tprClient *rest.RESTClient) {
} }
func kubernetesWatchTriggerTests(tprClient *rest.RESTClient) { func kubernetesWatchTriggerTests(crdClient *rest.RESTClient) {
// sample kubernetesWatchTrigger object // sample kubernetesWatchTrigger object
kubernetesWatchTrigger := &Kuberneteswatchtrigger{ kubernetesWatchTrigger := &KubernetesWatchTrigger{
TypeMeta: unversioned.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: "Kuberneteswatchtrigger", Kind: "KubernetesWatchTrigger",
APIVersion: "fission.io/v1", APIVersion: "fission.io/v1",
}, },
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.KubernetesWatchTriggerSpec{ Spec: fission.KubernetesWatchTriggerSpec{
Namespace: "foo", Namespace: "foo",
@@ -335,7 +344,7 @@ func kubernetesWatchTriggerTests(tprClient *rest.RESTClient) {
} }
// Test kubernetesWatchTrigger CRUD // Test kubernetesWatchTrigger CRUD
ei := MakeKuberneteswatchtriggerInterface(tprClient, api.NamespaceDefault) ei := MakeKubernetesWatchTriggerInterface(crdClient, metav1.NamespaceDefault)
// cleanup from old crashed tests, ignore errors // cleanup from old crashed tests, ignore errors
ei.Delete(kubernetesWatchTrigger.Metadata.Name, nil) ei.Delete(kubernetesWatchTrigger.Metadata.Name, nil)
@@ -355,17 +364,18 @@ func kubernetesWatchTriggerTests(tprClient *rest.RESTClient) {
} }
// update // update
kubernetesWatchTrigger.Metadata.ResourceVersion = e.Metadata.ResourceVersion
kubernetesWatchTrigger.Spec.Type = "service" kubernetesWatchTrigger.Spec.Type = "service"
e, err = ei.Update(kubernetesWatchTrigger) e, err = ei.Update(kubernetesWatchTrigger)
panicIf(err) panicIf(err)
// list // list
el, err := ei.List(api.ListOptions{}) el, err := ei.List(metav1.ListOptions{})
panicIf(err) panicIf(err)
if len(el.Items) != 1 { if len(el.Items) != 1 {
log.Panicf("wrong count from list: %v", el) log.Panicf("wrong count from kubeWatcher list: %v", len(el.Items))
} }
if el.Items[0].Spec.Type != kubernetesWatchTrigger.Spec.Type { if el.Items[0].Spec.Type != e.Spec.Type {
log.Panicf("bad object from list: %v", el.Items[0]) log.Panicf("bad object from list: %v", el.Items[0])
} }
@@ -374,10 +384,11 @@ func kubernetesWatchTriggerTests(tprClient *rest.RESTClient) {
panicIf(err) panicIf(err)
// start a watch // start a watch
wi, err := ei.Watch(api.ListOptions{}) wi, err := ei.Watch(metav1.ListOptions{})
panicIf(err) panicIf(err)
start := time.Now() start := time.Now()
kubernetesWatchTrigger.Metadata.ResourceVersion = ""
e, err = ei.Create(kubernetesWatchTrigger) e, err = ei.Create(kubernetesWatchTrigger)
panicIf(err) panicIf(err)
defer ei.Delete(e.Metadata.Name, nil) defer ei.Delete(e.Metadata.Name, nil)
@@ -390,9 +401,9 @@ func kubernetesWatchTriggerTests(tprClient *rest.RESTClient) {
log.Panicf("Didn't get watch event") log.Panicf("Didn't get watch event")
} }
case ev := <-wi.ResultChan(): case ev := <-wi.ResultChan():
obj, ok := ev.Object.(*Kuberneteswatchtrigger) obj, ok := ev.Object.(*KubernetesWatchTrigger)
if !ok { if !ok {
log.Panicf("Can't cast to Kuberneteswatchtrigger") log.Panicf("Can't cast to KubernetesWatchTrigger")
} }
if obj.Spec.Type != kubernetesWatchTrigger.Spec.Type { if obj.Spec.Type != kubernetesWatchTrigger.Spec.Type {
log.Panicf("Bad object from watch: %#v", obj) log.Panicf("Bad object from watch: %#v", obj)
@@ -403,7 +414,7 @@ func kubernetesWatchTriggerTests(tprClient *rest.RESTClient) {
} }
func TestTpr(t *testing.T) { func TestCrd(t *testing.T) {
// skip test if no cluster available for testing // skip test if no cluster available for testing
kubeconfig := os.Getenv("KUBECONFIG") kubeconfig := os.Getenv("KUBECONFIG")
if len(kubeconfig) == 0 { if len(kubeconfig) == 0 {
@@ -413,22 +424,22 @@ func TestTpr(t *testing.T) {
// Create the client config. Needs the KUBECONFIG env var to // Create the client config. Needs the KUBECONFIG env var to
// point at a valid kubeconfig. // point at a valid kubeconfig.
config, clientset, err := GetKubernetesClient() config, _, apiExtClient, err := GetKubernetesClient()
panicIf(err) panicIf(err)
// init our types // init our types
err = EnsureFissionTPRs(clientset) err = EnsureFissionCRDs(apiExtClient)
panicIf(err) panicIf(err)
// rest client with knowledge about our tpr types // rest client with knowledge about our crd types
tprClient, err := GetTprClient(config) crdClient, err := GetCrdClient(config)
panicIf(err) panicIf(err)
err = waitForTPRs(tprClient) err = waitForCRDs(crdClient)
panicIf(err) panicIf(err)
functionTests(tprClient) functionTests(crdClient)
environmentTests(tprClient) environmentTests(crdClient)
httpTriggerTests(tprClient) httpTriggerTests(crdClient)
kubernetesWatchTriggerTests(tprClient) kubernetesWatchTriggerTests(crdClient)
} }
+15 -14
View File
@@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/rest" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
) )
type ( type (
@@ -27,9 +28,9 @@ type (
Create(*Environment) (*Environment, error) Create(*Environment) (*Environment, error)
Get(name string) (*Environment, error) Get(name string) (*Environment, error)
Update(*Environment) (*Environment, error) Update(*Environment) (*Environment, error)
Delete(name string, options *api.DeleteOptions) error Delete(name string, options *metav1.DeleteOptions) error
List(opts api.ListOptions) (*EnvironmentList, error) List(opts metav1.ListOptions) (*EnvironmentList, error)
Watch(opts api.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
} }
environmentClient struct { environmentClient struct {
@@ -38,9 +39,9 @@ type (
} }
) )
func MakeEnvironmentInterface(tprClient *rest.RESTClient, namespace string) EnvironmentInterface { func MakeEnvironmentInterface(crdClient *rest.RESTClient, namespace string) EnvironmentInterface {
return &environmentClient{ return &environmentClient{
client: tprClient, client: crdClient,
namespace: namespace, namespace: namespace,
} }
} }
@@ -85,7 +86,7 @@ func (ec *environmentClient) Update(e *Environment) (*Environment, error) {
return &result, nil return &result, nil
} }
func (ec *environmentClient) Delete(name string, opts *api.DeleteOptions) error { func (ec *environmentClient) Delete(name string, opts *metav1.DeleteOptions) error {
return ec.client.Delete(). return ec.client.Delete().
Namespace(ec.namespace). Namespace(ec.namespace).
Resource("environments"). Resource("environments").
@@ -95,12 +96,12 @@ func (ec *environmentClient) Delete(name string, opts *api.DeleteOptions) error
Error() Error()
} }
func (ec *environmentClient) List(opts api.ListOptions) (*EnvironmentList, error) { func (ec *environmentClient) List(opts metav1.ListOptions) (*EnvironmentList, error) {
var result EnvironmentList var result EnvironmentList
err := ec.client.Get(). err := ec.client.Get().
Namespace(ec.namespace). Namespace(ec.namespace).
Resource("environments"). Resource("environments").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Do(). Do().
Into(&result) Into(&result)
if err != nil { if err != nil {
@@ -109,11 +110,11 @@ func (ec *environmentClient) List(opts api.ListOptions) (*EnvironmentList, error
return &result, nil return &result, nil
} }
func (ec *environmentClient) Watch(opts api.ListOptions) (watch.Interface, error) { func (ec *environmentClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return ec.client.Get(). return ec.client.Get().
Prefix("watch"). Prefix("watch").
Namespace(ec.namespace). Namespace(ec.namespace).
Resource("environments"). Resource("environments").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Watch() Watch()
} }
+15 -14
View File
@@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/rest" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
) )
type ( type (
@@ -27,9 +28,9 @@ type (
Create(*Function) (*Function, error) Create(*Function) (*Function, error)
Get(name string) (*Function, error) Get(name string) (*Function, error)
Update(*Function) (*Function, error) Update(*Function) (*Function, error)
Delete(name string, options *api.DeleteOptions) error Delete(name string, options *metav1.DeleteOptions) error
List(opts api.ListOptions) (*FunctionList, error) List(opts metav1.ListOptions) (*FunctionList, error)
Watch(opts api.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
} }
functionClient struct { functionClient struct {
@@ -38,9 +39,9 @@ type (
} }
) )
func MakeFunctionInterface(tprClient *rest.RESTClient, namespace string) FunctionInterface { func MakeFunctionInterface(crdClient *rest.RESTClient, namespace string) FunctionInterface {
return &functionClient{ return &functionClient{
client: tprClient, client: crdClient,
namespace: namespace, namespace: namespace,
} }
} }
@@ -85,7 +86,7 @@ func (fc *functionClient) Update(f *Function) (*Function, error) {
return &result, nil return &result, nil
} }
func (fc *functionClient) Delete(name string, opts *api.DeleteOptions) error { func (fc *functionClient) Delete(name string, opts *metav1.DeleteOptions) error {
return fc.client.Delete(). return fc.client.Delete().
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("functions"). Resource("functions").
@@ -95,12 +96,12 @@ func (fc *functionClient) Delete(name string, opts *api.DeleteOptions) error {
Error() Error()
} }
func (fc *functionClient) List(opts api.ListOptions) (*FunctionList, error) { func (fc *functionClient) List(opts metav1.ListOptions) (*FunctionList, error) {
var result FunctionList var result FunctionList
err := fc.client.Get(). err := fc.client.Get().
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("functions"). Resource("functions").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Do(). Do().
Into(&result) Into(&result)
if err != nil { if err != nil {
@@ -109,11 +110,11 @@ func (fc *functionClient) List(opts api.ListOptions) (*FunctionList, error) {
return &result, nil return &result, nil
} }
func (fc *functionClient) Watch(opts api.ListOptions) (watch.Interface, error) { func (fc *functionClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return fc.client.Get(). return fc.client.Get().
Prefix("watch"). Prefix("watch").
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("functions"). Resource("functions").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Watch() Watch()
} }
+26 -25
View File
@@ -14,22 +14,23 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/rest" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
) )
type ( type (
HttptriggerInterface interface { HTTPTriggerInterface interface {
Create(*Httptrigger) (*Httptrigger, error) Create(*HTTPTrigger) (*HTTPTrigger, error)
Get(name string) (*Httptrigger, error) Get(name string) (*HTTPTrigger, error)
Update(*Httptrigger) (*Httptrigger, error) Update(*HTTPTrigger) (*HTTPTrigger, error)
Delete(name string, options *api.DeleteOptions) error Delete(name string, options *metav1.DeleteOptions) error
List(opts api.ListOptions) (*HttptriggerList, error) List(opts metav1.ListOptions) (*HTTPTriggerList, error)
Watch(opts api.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
} }
httpTriggerClient struct { httpTriggerClient struct {
@@ -38,15 +39,15 @@ type (
} }
) )
func MakeHttptriggerInterface(tprClient *rest.RESTClient, namespace string) HttptriggerInterface { func MakeHTTPTriggerInterface(crdClient *rest.RESTClient, namespace string) HTTPTriggerInterface {
return &httpTriggerClient{ return &httpTriggerClient{
client: tprClient, client: crdClient,
namespace: namespace, namespace: namespace,
} }
} }
func (c *httpTriggerClient) Create(obj *Httptrigger) (*Httptrigger, error) { func (c *httpTriggerClient) Create(obj *HTTPTrigger) (*HTTPTrigger, error) {
var result Httptrigger var result HTTPTrigger
err := c.client.Post(). err := c.client.Post().
Resource("httptriggers"). Resource("httptriggers").
Namespace(c.namespace). Namespace(c.namespace).
@@ -58,8 +59,8 @@ func (c *httpTriggerClient) Create(obj *Httptrigger) (*Httptrigger, error) {
return &result, nil return &result, nil
} }
func (c *httpTriggerClient) Get(name string) (*Httptrigger, error) { func (c *httpTriggerClient) Get(name string) (*HTTPTrigger, error) {
var result Httptrigger var result HTTPTrigger
err := c.client.Get(). err := c.client.Get().
Resource("httptriggers"). Resource("httptriggers").
Namespace(c.namespace). Namespace(c.namespace).
@@ -71,8 +72,8 @@ func (c *httpTriggerClient) Get(name string) (*Httptrigger, error) {
return &result, nil return &result, nil
} }
func (c *httpTriggerClient) Update(obj *Httptrigger) (*Httptrigger, error) { func (c *httpTriggerClient) Update(obj *HTTPTrigger) (*HTTPTrigger, error) {
var result Httptrigger var result HTTPTrigger
err := c.client.Put(). err := c.client.Put().
Resource("httptriggers"). Resource("httptriggers").
Namespace(c.namespace). Namespace(c.namespace).
@@ -85,7 +86,7 @@ func (c *httpTriggerClient) Update(obj *Httptrigger) (*Httptrigger, error) {
return &result, nil return &result, nil
} }
func (c *httpTriggerClient) Delete(name string, opts *api.DeleteOptions) error { func (c *httpTriggerClient) Delete(name string, opts *metav1.DeleteOptions) error {
return c.client.Delete(). return c.client.Delete().
Namespace(c.namespace). Namespace(c.namespace).
Resource("httptriggers"). Resource("httptriggers").
@@ -95,12 +96,12 @@ func (c *httpTriggerClient) Delete(name string, opts *api.DeleteOptions) error {
Error() Error()
} }
func (c *httpTriggerClient) List(opts api.ListOptions) (*HttptriggerList, error) { func (c *httpTriggerClient) List(opts metav1.ListOptions) (*HTTPTriggerList, error) {
var result HttptriggerList var result HTTPTriggerList
err := c.client.Get(). err := c.client.Get().
Namespace(c.namespace). Namespace(c.namespace).
Resource("httptriggers"). Resource("httptriggers").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Do(). Do().
Into(&result) Into(&result)
if err != nil { if err != nil {
@@ -109,11 +110,11 @@ func (c *httpTriggerClient) List(opts api.ListOptions) (*HttptriggerList, error)
return &result, nil return &result, nil
} }
func (c *httpTriggerClient) Watch(opts api.ListOptions) (watch.Interface, error) { func (c *httpTriggerClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return c.client.Get(). return c.client.Get().
Prefix("watch"). Prefix("watch").
Namespace(c.namespace). Namespace(c.namespace).
Resource("httptriggers"). Resource("httptriggers").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Watch() Watch()
} }
+3 -3
View File
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"fmt" "fmt"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// Given metadata, create a key that uniquely identifies the contents // Given metadata, create a key that uniquely identifies the contents
@@ -28,6 +28,6 @@ import (
// content. (ResourceVersion may also update on status updates, so // content. (ResourceVersion may also update on status updates, so
// this will result in some unnecessary cache misses. That should be // this will result in some unnecessary cache misses. That should be
// ok.) // ok.)
func CacheKey(metadata *api.ObjectMeta) string { func CacheKey(metadata *metav1.ObjectMeta) string {
return fmt.Sprintf("%v_%v", metadata.UID, metadata.ResourceVersion) return fmt.Sprintf("%v_%v", metadata.UID, metadata.ResourceVersion)
} }
@@ -14,22 +14,23 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/rest" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
) )
type ( type (
KuberneteswatchtriggerInterface interface { KubernetesWatchTriggerInterface interface {
Create(*Kuberneteswatchtrigger) (*Kuberneteswatchtrigger, error) Create(*KubernetesWatchTrigger) (*KubernetesWatchTrigger, error)
Get(name string) (*Kuberneteswatchtrigger, error) Get(name string) (*KubernetesWatchTrigger, error)
Update(*Kuberneteswatchtrigger) (*Kuberneteswatchtrigger, error) Update(*KubernetesWatchTrigger) (*KubernetesWatchTrigger, error)
Delete(name string, options *api.DeleteOptions) error Delete(name string, options *metav1.DeleteOptions) error
List(opts api.ListOptions) (*KuberneteswatchtriggerList, error) List(opts metav1.ListOptions) (*KubernetesWatchTriggerList, error)
Watch(opts api.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
} }
kubernetesWatchTriggerClient struct { kubernetesWatchTriggerClient struct {
@@ -38,15 +39,15 @@ type (
} }
) )
func MakeKuberneteswatchtriggerInterface(tprClient *rest.RESTClient, namespace string) KuberneteswatchtriggerInterface { func MakeKubernetesWatchTriggerInterface(crdClient *rest.RESTClient, namespace string) KubernetesWatchTriggerInterface {
return &kubernetesWatchTriggerClient{ return &kubernetesWatchTriggerClient{
client: tprClient, client: crdClient,
namespace: namespace, namespace: namespace,
} }
} }
func (c *kubernetesWatchTriggerClient) Create(obj *Kuberneteswatchtrigger) (*Kuberneteswatchtrigger, error) { func (c *kubernetesWatchTriggerClient) Create(obj *KubernetesWatchTrigger) (*KubernetesWatchTrigger, error) {
var result Kuberneteswatchtrigger var result KubernetesWatchTrigger
err := c.client.Post(). err := c.client.Post().
Resource("kuberneteswatchtriggers"). Resource("kuberneteswatchtriggers").
Namespace(c.namespace). Namespace(c.namespace).
@@ -58,8 +59,8 @@ func (c *kubernetesWatchTriggerClient) Create(obj *Kuberneteswatchtrigger) (*Kub
return &result, nil return &result, nil
} }
func (c *kubernetesWatchTriggerClient) Get(name string) (*Kuberneteswatchtrigger, error) { func (c *kubernetesWatchTriggerClient) Get(name string) (*KubernetesWatchTrigger, error) {
var result Kuberneteswatchtrigger var result KubernetesWatchTrigger
err := c.client.Get(). err := c.client.Get().
Resource("kuberneteswatchtriggers"). Resource("kuberneteswatchtriggers").
Namespace(c.namespace). Namespace(c.namespace).
@@ -71,8 +72,8 @@ func (c *kubernetesWatchTriggerClient) Get(name string) (*Kuberneteswatchtrigger
return &result, nil return &result, nil
} }
func (c *kubernetesWatchTriggerClient) Update(obj *Kuberneteswatchtrigger) (*Kuberneteswatchtrigger, error) { func (c *kubernetesWatchTriggerClient) Update(obj *KubernetesWatchTrigger) (*KubernetesWatchTrigger, error) {
var result Kuberneteswatchtrigger var result KubernetesWatchTrigger
err := c.client.Put(). err := c.client.Put().
Resource("kuberneteswatchtriggers"). Resource("kuberneteswatchtriggers").
Namespace(c.namespace). Namespace(c.namespace).
@@ -85,7 +86,7 @@ func (c *kubernetesWatchTriggerClient) Update(obj *Kuberneteswatchtrigger) (*Kub
return &result, nil return &result, nil
} }
func (c *kubernetesWatchTriggerClient) Delete(name string, opts *api.DeleteOptions) error { func (c *kubernetesWatchTriggerClient) Delete(name string, opts *metav1.DeleteOptions) error {
return c.client.Delete(). return c.client.Delete().
Namespace(c.namespace). Namespace(c.namespace).
Resource("kuberneteswatchtriggers"). Resource("kuberneteswatchtriggers").
@@ -95,12 +96,12 @@ func (c *kubernetesWatchTriggerClient) Delete(name string, opts *api.DeleteOptio
Error() Error()
} }
func (c *kubernetesWatchTriggerClient) List(opts api.ListOptions) (*KuberneteswatchtriggerList, error) { func (c *kubernetesWatchTriggerClient) List(opts metav1.ListOptions) (*KubernetesWatchTriggerList, error) {
var result KuberneteswatchtriggerList var result KubernetesWatchTriggerList
err := c.client.Get(). err := c.client.Get().
Namespace(c.namespace). Namespace(c.namespace).
Resource("kuberneteswatchtriggers"). Resource("kuberneteswatchtriggers").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Do(). Do().
Into(&result) Into(&result)
if err != nil { if err != nil {
@@ -109,11 +110,11 @@ func (c *kubernetesWatchTriggerClient) List(opts api.ListOptions) (*Kuberneteswa
return &result, nil return &result, nil
} }
func (c *kubernetesWatchTriggerClient) Watch(opts api.ListOptions) (watch.Interface, error) { func (c *kubernetesWatchTriggerClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return c.client.Get(). return c.client.Get().
Prefix("watch"). Prefix("watch").
Namespace(c.namespace). Namespace(c.namespace).
Resource("kuberneteswatchtriggers"). Resource("kuberneteswatchtriggers").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Watch() Watch()
} }
@@ -14,39 +14,40 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/rest" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
) )
type ( type (
MessagequeuetriggerInterface interface { MessageQueueTriggerInterface interface {
Create(*Messagequeuetrigger) (*Messagequeuetrigger, error) Create(*MessageQueueTrigger) (*MessageQueueTrigger, error)
Get(name string) (*Messagequeuetrigger, error) Get(name string) (*MessageQueueTrigger, error)
Update(*Messagequeuetrigger) (*Messagequeuetrigger, error) Update(*MessageQueueTrigger) (*MessageQueueTrigger, error)
Delete(name string, options *api.DeleteOptions) error Delete(name string, options *metav1.DeleteOptions) error
List(opts api.ListOptions) (*MessagequeuetriggerList, error) List(opts metav1.ListOptions) (*MessageQueueTriggerList, error)
Watch(opts api.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
} }
messagequeuetriggerClient struct { messageQueueTriggerClient struct {
client *rest.RESTClient client *rest.RESTClient
namespace string namespace string
} }
) )
func MakeMessagequeuetriggerInterface(tprClient *rest.RESTClient, namespace string) MessagequeuetriggerInterface { func MakeMessageQueueTriggerInterface(crdClient *rest.RESTClient, namespace string) MessageQueueTriggerInterface {
return &messagequeuetriggerClient{ return &messageQueueTriggerClient{
client: tprClient, client: crdClient,
namespace: namespace, namespace: namespace,
} }
} }
func (fc *messagequeuetriggerClient) Create(f *Messagequeuetrigger) (*Messagequeuetrigger, error) { func (fc *messageQueueTriggerClient) Create(f *MessageQueueTrigger) (*MessageQueueTrigger, error) {
var result Messagequeuetrigger var result MessageQueueTrigger
err := fc.client.Post(). err := fc.client.Post().
Resource("messagequeuetriggers"). Resource("messagequeuetriggers").
Namespace(fc.namespace). Namespace(fc.namespace).
@@ -58,8 +59,8 @@ func (fc *messagequeuetriggerClient) Create(f *Messagequeuetrigger) (*Messageque
return &result, nil return &result, nil
} }
func (fc *messagequeuetriggerClient) Get(name string) (*Messagequeuetrigger, error) { func (fc *messageQueueTriggerClient) Get(name string) (*MessageQueueTrigger, error) {
var result Messagequeuetrigger var result MessageQueueTrigger
err := fc.client.Get(). err := fc.client.Get().
Resource("messagequeuetriggers"). Resource("messagequeuetriggers").
Namespace(fc.namespace). Namespace(fc.namespace).
@@ -71,8 +72,8 @@ func (fc *messagequeuetriggerClient) Get(name string) (*Messagequeuetrigger, err
return &result, nil return &result, nil
} }
func (fc *messagequeuetriggerClient) Update(f *Messagequeuetrigger) (*Messagequeuetrigger, error) { func (fc *messageQueueTriggerClient) Update(f *MessageQueueTrigger) (*MessageQueueTrigger, error) {
var result Messagequeuetrigger var result MessageQueueTrigger
err := fc.client.Put(). err := fc.client.Put().
Resource("messagequeuetriggers"). Resource("messagequeuetriggers").
Namespace(fc.namespace). Namespace(fc.namespace).
@@ -85,7 +86,7 @@ func (fc *messagequeuetriggerClient) Update(f *Messagequeuetrigger) (*Messageque
return &result, nil return &result, nil
} }
func (fc *messagequeuetriggerClient) Delete(name string, opts *api.DeleteOptions) error { func (fc *messageQueueTriggerClient) Delete(name string, opts *metav1.DeleteOptions) error {
return fc.client.Delete(). return fc.client.Delete().
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("messagequeuetriggers"). Resource("messagequeuetriggers").
@@ -95,12 +96,12 @@ func (fc *messagequeuetriggerClient) Delete(name string, opts *api.DeleteOptions
Error() Error()
} }
func (fc *messagequeuetriggerClient) List(opts api.ListOptions) (*MessagequeuetriggerList, error) { func (fc *messageQueueTriggerClient) List(opts metav1.ListOptions) (*MessageQueueTriggerList, error) {
var result MessagequeuetriggerList var result MessageQueueTriggerList
err := fc.client.Get(). err := fc.client.Get().
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("messagequeuetriggers"). Resource("messagequeuetriggers").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Do(). Do().
Into(&result) Into(&result)
if err != nil { if err != nil {
@@ -109,11 +110,11 @@ func (fc *messagequeuetriggerClient) List(opts api.ListOptions) (*Messagequeuetr
return &result, nil return &result, nil
} }
func (fc *messagequeuetriggerClient) Watch(opts api.ListOptions) (watch.Interface, error) { func (fc *messageQueueTriggerClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return fc.client.Get(). return fc.client.Get().
Prefix("watch"). Prefix("watch").
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("messagequeuetriggers"). Resource("messagequeuetriggers").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Watch() Watch()
} }
+15 -14
View File
@@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/rest" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
) )
type ( type (
@@ -27,9 +28,9 @@ type (
Create(*Package) (*Package, error) Create(*Package) (*Package, error)
Get(name string) (*Package, error) Get(name string) (*Package, error)
Update(*Package) (*Package, error) Update(*Package) (*Package, error)
Delete(name string, options *api.DeleteOptions) error Delete(name string, options *metav1.DeleteOptions) error
List(opts api.ListOptions) (*PackageList, error) List(opts metav1.ListOptions) (*PackageList, error)
Watch(opts api.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
} }
packageClient struct { packageClient struct {
@@ -38,9 +39,9 @@ type (
} }
) )
func MakePackageInterface(tprClient *rest.RESTClient, namespace string) PackageInterface { func MakePackageInterface(crdClient *rest.RESTClient, namespace string) PackageInterface {
return &packageClient{ return &packageClient{
client: tprClient, client: crdClient,
namespace: namespace, namespace: namespace,
} }
} }
@@ -85,7 +86,7 @@ func (c *packageClient) Update(f *Package) (*Package, error) {
return &result, nil return &result, nil
} }
func (c *packageClient) Delete(name string, opts *api.DeleteOptions) error { func (c *packageClient) Delete(name string, opts *metav1.DeleteOptions) error {
return c.client.Delete(). return c.client.Delete().
Namespace(c.namespace). Namespace(c.namespace).
Resource("packages"). Resource("packages").
@@ -95,12 +96,12 @@ func (c *packageClient) Delete(name string, opts *api.DeleteOptions) error {
Error() Error()
} }
func (c *packageClient) List(opts api.ListOptions) (*PackageList, error) { func (c *packageClient) List(opts metav1.ListOptions) (*PackageList, error) {
var result PackageList var result PackageList
err := c.client.Get(). err := c.client.Get().
Namespace(c.namespace). Namespace(c.namespace).
Resource("packages"). Resource("packages").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Do(). Do().
Into(&result) Into(&result)
if err != nil { if err != nil {
@@ -109,11 +110,11 @@ func (c *packageClient) List(opts api.ListOptions) (*PackageList, error) {
return &result, nil return &result, nil
} }
func (c *packageClient) Watch(opts api.ListOptions) (watch.Interface, error) { func (c *packageClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return c.client.Get(). return c.client.Get().
Prefix("watch"). Prefix("watch").
Namespace(c.namespace). Namespace(c.namespace).
Resource("packages"). Resource("packages").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Watch() Watch()
} }
+28 -27
View File
@@ -14,39 +14,40 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tpr package crd
import ( import (
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/1.5/rest" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
) )
type ( type (
TimetriggerInterface interface { TimeTriggerInterface interface {
Create(*Timetrigger) (*Timetrigger, error) Create(*TimeTrigger) (*TimeTrigger, error)
Get(name string) (*Timetrigger, error) Get(name string) (*TimeTrigger, error)
Update(*Timetrigger) (*Timetrigger, error) Update(*TimeTrigger) (*TimeTrigger, error)
Delete(name string, options *api.DeleteOptions) error Delete(name string, options *metav1.DeleteOptions) error
List(opts api.ListOptions) (*TimetriggerList, error) List(opts metav1.ListOptions) (*TimeTriggerList, error)
Watch(opts api.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
} }
timetriggerClient struct { timeTriggerClient struct {
client *rest.RESTClient client *rest.RESTClient
namespace string namespace string
} }
) )
func MakeTimetriggerInterface(tprClient *rest.RESTClient, namespace string) TimetriggerInterface { func MakeTimeTriggerInterface(crdClient *rest.RESTClient, namespace string) TimeTriggerInterface {
return &timetriggerClient{ return &timeTriggerClient{
client: tprClient, client: crdClient,
namespace: namespace, namespace: namespace,
} }
} }
func (fc *timetriggerClient) Create(f *Timetrigger) (*Timetrigger, error) { func (fc *timeTriggerClient) Create(f *TimeTrigger) (*TimeTrigger, error) {
var result Timetrigger var result TimeTrigger
err := fc.client.Post(). err := fc.client.Post().
Resource("timetriggers"). Resource("timetriggers").
Namespace(fc.namespace). Namespace(fc.namespace).
@@ -58,8 +59,8 @@ func (fc *timetriggerClient) Create(f *Timetrigger) (*Timetrigger, error) {
return &result, nil return &result, nil
} }
func (fc *timetriggerClient) Get(name string) (*Timetrigger, error) { func (fc *timeTriggerClient) Get(name string) (*TimeTrigger, error) {
var result Timetrigger var result TimeTrigger
err := fc.client.Get(). err := fc.client.Get().
Resource("timetriggers"). Resource("timetriggers").
Namespace(fc.namespace). Namespace(fc.namespace).
@@ -71,8 +72,8 @@ func (fc *timetriggerClient) Get(name string) (*Timetrigger, error) {
return &result, nil return &result, nil
} }
func (fc *timetriggerClient) Update(f *Timetrigger) (*Timetrigger, error) { func (fc *timeTriggerClient) Update(f *TimeTrigger) (*TimeTrigger, error) {
var result Timetrigger var result TimeTrigger
err := fc.client.Put(). err := fc.client.Put().
Resource("timetriggers"). Resource("timetriggers").
Namespace(fc.namespace). Namespace(fc.namespace).
@@ -85,7 +86,7 @@ func (fc *timetriggerClient) Update(f *Timetrigger) (*Timetrigger, error) {
return &result, nil return &result, nil
} }
func (fc *timetriggerClient) Delete(name string, opts *api.DeleteOptions) error { func (fc *timeTriggerClient) Delete(name string, opts *metav1.DeleteOptions) error {
return fc.client.Delete(). return fc.client.Delete().
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("timetriggers"). Resource("timetriggers").
@@ -95,12 +96,12 @@ func (fc *timetriggerClient) Delete(name string, opts *api.DeleteOptions) error
Error() Error()
} }
func (fc *timetriggerClient) List(opts api.ListOptions) (*TimetriggerList, error) { func (fc *timeTriggerClient) List(opts metav1.ListOptions) (*TimeTriggerList, error) {
var result TimetriggerList var result TimeTriggerList
err := fc.client.Get(). err := fc.client.Get().
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("timetriggers"). Resource("timetriggers").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Do(). Do().
Into(&result) Into(&result)
if err != nil { if err != nil {
@@ -109,11 +110,11 @@ func (fc *timetriggerClient) List(opts api.ListOptions) (*TimetriggerList, error
return &result, nil return &result, nil
} }
func (fc *timetriggerClient) Watch(opts api.ListOptions) (watch.Interface, error) { func (fc *timeTriggerClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return fc.client.Get(). return fc.client.Get().
Prefix("watch"). Prefix("watch").
Namespace(fc.namespace). Namespace(fc.namespace).
Resource("timetriggers"). Resource("timetriggers").
VersionedParams(&opts, api.ParameterCodec). VersionedParams(&opts, scheme.ParameterCodec).
Watch() Watch()
} }
+228
View File
@@ -0,0 +1,228 @@
/*
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 crd
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/fission/fission"
)
//
// To add a Fission CRD type:
// 1. Create a "spec" type, for everything in the type except metadata
// 2. Create the type with metadata + the spec
// 3. Create a list type (for example see FunctionList and Function, below)
// 4. Add methods at the bottom of this file for satisfying Object and List interfaces
// 5. Add the type to configureClient in client.go
// 6. Add the type to EnsureFissionCRDs in crd.go
// 7. Add tests to crd_test.go
// 8. Add a CRUD Interface type (analogous to FunctionInterface in function.go)
// 9. Add a getter method for your interface type to FissionClient in client.go
//
type (
// Packages. Think of these as function-level images.
Package struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ObjectMeta `json:"metadata"`
Spec fission.PackageSpec `json:"spec"`
Status fission.PackageStatus `json:"status"`
}
PackageList struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ListMeta `json:"metadata"`
Items []Package `json:"items"`
}
// Functions.
Function struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ObjectMeta `json:"metadata"`
Spec fission.FunctionSpec `json:"spec"`
}
FunctionList struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ListMeta `json:"metadata"`
Items []Function `json:"items"`
}
// Environments.
Environment struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ObjectMeta `json:"metadata"`
Spec fission.EnvironmentSpec `json:"spec"`
}
EnvironmentList struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ListMeta `json:"metadata"`
Items []Environment `json:"items"`
}
HTTPTrigger struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ObjectMeta `json:"metadata"`
Spec fission.HTTPTriggerSpec `json:"spec"`
}
HTTPTriggerList struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ListMeta `json:"metadata"`
Items []HTTPTrigger `json:"items"`
}
// Kubernetes Watches as triggers
KubernetesWatchTrigger struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ObjectMeta `json:"metadata"`
Spec fission.KubernetesWatchTriggerSpec `json:"spec"`
}
KubernetesWatchTriggerList struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ListMeta `json:"metadata"`
Items []KubernetesWatchTrigger `json:"items"`
}
// Time triggers
TimeTrigger struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ObjectMeta `json:"metadata"`
Spec fission.TimeTriggerSpec `json:"spec"`
}
TimeTriggerList struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ListMeta `json:"metadata"`
Items []TimeTrigger `json:"items"`
}
// Message Queue triggers
MessageQueueTrigger struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ObjectMeta `json:"metadata"`
Spec fission.MessageQueueTriggerSpec `json:"spec"`
}
MessageQueueTriggerList struct {
metav1.TypeMeta `json:",inline"`
Metadata metav1.ListMeta `json:"metadata"`
Items []MessageQueueTrigger `json:"items"`
}
)
// Each CRD type needs:
// GetObjectKind (to satisfy the Object interface)
//
// In addition, each singular CRD type needs:
// GetObjectMeta (to satisfy the ObjectMetaAccessor interface)
//
// And each list CRD type needs:
// GetListMeta (to satisfy the ListMetaAccessor interface)
func (f *Function) GetObjectKind() schema.ObjectKind {
return &f.TypeMeta
}
func (e *Environment) GetObjectKind() schema.ObjectKind {
return &e.TypeMeta
}
func (ht *HTTPTrigger) GetObjectKind() schema.ObjectKind {
return &ht.TypeMeta
}
func (w *KubernetesWatchTrigger) GetObjectKind() schema.ObjectKind {
return &w.TypeMeta
}
func (w *TimeTrigger) GetObjectKind() schema.ObjectKind {
return &w.TypeMeta
}
func (w *MessageQueueTrigger) GetObjectKind() schema.ObjectKind {
return &w.TypeMeta
}
func (w *Package) GetObjectKind() schema.ObjectKind {
return &w.TypeMeta
}
func (f *Function) GetObjectMeta() metav1.Object {
return &f.Metadata
}
func (e *Environment) GetObjectMeta() metav1.Object {
return &e.Metadata
}
func (ht *HTTPTrigger) GetObjectMeta() metav1.Object {
return &ht.Metadata
}
func (w *KubernetesWatchTrigger) GetObjectMeta() metav1.Object {
return &w.Metadata
}
func (w *TimeTrigger) GetObjectMeta() metav1.Object {
return &w.Metadata
}
func (w *MessageQueueTrigger) GetObjectMeta() metav1.Object {
return &w.Metadata
}
func (w *Package) GetObjectMeta() metav1.Object {
return &w.Metadata
}
func (fl *FunctionList) GetObjectKind() schema.ObjectKind {
return &fl.TypeMeta
}
func (el *EnvironmentList) GetObjectKind() schema.ObjectKind {
return &el.TypeMeta
}
func (hl *HTTPTriggerList) GetObjectKind() schema.ObjectKind {
return &hl.TypeMeta
}
func (wl *KubernetesWatchTriggerList) GetObjectKind() schema.ObjectKind {
return &wl.TypeMeta
}
func (wl *TimeTriggerList) GetObjectKind() schema.ObjectKind {
return &wl.TypeMeta
}
func (wl *MessageQueueTriggerList) GetObjectKind() schema.ObjectKind {
return &wl.TypeMeta
}
func (wl *PackageList) GetObjectKind() schema.ObjectKind {
return &wl.TypeMeta
}
func (fl *FunctionList) GetListMeta() metav1.List {
return &fl.Metadata
}
func (el *EnvironmentList) GetListMeta() metav1.List {
return &el.Metadata
}
func (hl *HTTPTriggerList) GetListMeta() metav1.List {
return &hl.Metadata
}
func (wl *KubernetesWatchTriggerList) GetListMeta() metav1.List {
return &wl.Metadata
}
func (wl *TimeTriggerList) GetListMeta() metav1.List {
return &wl.Metadata
}
func (wl *MessageQueueTriggerList) GetListMeta() metav1.List {
return &wl.Metadata
}
func (wl *PackageList) GetListMeta() metav1.List {
return &wl.Metadata
}
+2 -22
View File
@@ -37,10 +37,6 @@ function maybe_first_prompt() {
fi 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() { function run() {
maybe_first_prompt maybe_first_prompt
rate=25 rate=25
@@ -52,27 +48,11 @@ function run() {
sleep 0.5 sleep 0.5
fi fi
$1 $1
echo; echo -n ">" echo
echo -n ">"
read -s read -s
echo -e "\b " 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' ') SSH_NODE=$(kubectl get nodes | tail -1 | cut -f1 -d' ')
#trap "echo" EXIT
+31 -18
View File
@@ -5,12 +5,13 @@ import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"log" "log"
"net"
"net/http" "net/http"
//"time" "net/url"
"time"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/environments/fetcher" "github.com/fission/fission/environments/fetcher"
//"github.com/fission/fission/router"
) )
type ( type (
@@ -31,21 +32,35 @@ func (c *Client) Fetch(fr *fetcher.FetchRequest) error {
return err return err
} }
// client := http.Client{ maxRetries := 20
// Transport: router.MakeRetryingRoundTripper(10, 50*time.Millisecond), var resp *http.Response
// }
resp, err := http.Post(c.url, "application/json", bytes.NewReader(body)) for i := 0; i < maxRetries; i++ {
if err != nil { resp, err = http.Post(c.url, "application/json", bytes.NewReader(body))
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 { if err == nil && resp.StatusCode == 200 {
return fission.MakeErrorFromHTTP(resp) defer resp.Body.Close()
return nil
}
// Only retry for the specific case of a connection error.
if urlErr, ok := err.(*url.Error); ok {
if netErr, ok := urlErr.Err.(*net.OpError); ok {
if netErr.Op == "dial" {
if i < maxRetries-1 {
time.Sleep(50 * time.Duration(2*i) * time.Millisecond)
continue
}
}
}
}
} }
return nil if err == nil {
err = fission.MakeErrorFromHTTP(resp)
}
log.Printf("Failed to fetch: %v", err)
return err
} }
func (c *Client) Upload(fr *fetcher.UploadRequest) (*fetcher.UploadResponse, error) { func (c *Client) Upload(fr *fetcher.UploadRequest) (*fetcher.UploadResponse, error) {
@@ -68,13 +83,11 @@ func (c *Client) Upload(fr *fetcher.UploadRequest) (*fetcher.UploadResponse, err
return nil, err return nil, err
} }
log.Printf("Received upload response: %v", string(rBody)) uploadResp := fetcher.UploadResponse{}
err = json.Unmarshal([]byte(rBody), &uploadResp)
uploadReq := fetcher.UploadResponse{}
err = json.Unmarshal([]byte(rBody), &uploadReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &uploadReq, nil return &uploadResp, nil
} }
+19 -20
View File
@@ -16,23 +16,22 @@ import (
"github.com/mholt/archiver" "github.com/mholt/archiver"
"github.com/satori/go.uuid" "github.com/satori/go.uuid"
"k8s.io/client-go/1.5/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/crd"
storageSvcClient "github.com/fission/fission/storagesvc/client" storageSvcClient "github.com/fission/fission/storagesvc/client"
"github.com/fission/fission/tpr"
) )
type ( type (
FetchRequestType int FetchRequestType int
FetchRequest struct { FetchRequest struct {
FetchType FetchRequestType `json:"fetchType"` FetchType FetchRequestType `json:"fetchType"`
Package api.ObjectMeta `json:"package"` Package metav1.ObjectMeta `json:"package"`
Url string `json:"url"` Url string `json:"url"`
StorageSvcUrl string `json:"storagesvcurl"` StorageSvcUrl string `json:"storagesvcurl"`
Filename string `json:"filename"` Filename string `json:"filename"`
} }
// UploadRequest send from builder manager describes which // UploadRequest send from builder manager describes which
@@ -51,8 +50,7 @@ type (
Fetcher struct { Fetcher struct {
sharedVolumePath string sharedVolumePath string
fissionClient *tpr.FissionClient fissionClient *crd.FissionClient
kubeClient *kubernetes.Clientset
} }
) )
@@ -63,14 +61,13 @@ const (
) )
func MakeFetcher(sharedVolumePath string) *Fetcher { func MakeFetcher(sharedVolumePath string) *Fetcher {
fissionClient, kubeClient, err := tpr.MakeFissionClient() fissionClient, _, _, err := crd.MakeFissionClient()
if err != nil { if err != nil {
return nil return nil
} }
return &Fetcher{ return &Fetcher{
sharedVolumePath: sharedVolumePath, sharedVolumePath: sharedVolumePath,
fissionClient: fissionClient, fissionClient: fissionClient,
kubeClient: kubeClient,
} }
} }
@@ -81,12 +78,15 @@ func downloadUrl(url string, localPath string) error {
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) w, err := os.Create(localPath)
if err != nil { if err != nil {
return err return err
} }
_, err = io.Copy(w, resp.Body)
err = ioutil.WriteFile(localPath, body, 0600) if err != nil {
return err
}
err = os.Chmod(localPath, 0600)
if err != nil { if err != nil {
return err return err
} }
@@ -155,13 +155,11 @@ func (fetcher *Fetcher) FetchHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), 400) http.Error(w, err.Error(), 400)
return return
} }
log.Printf("fetcher received fetch request: %v", req) log.Printf("fetcher received fetch request and started downloading: %v", req)
tmpFile := req.Filename + ".tmp" tmpFile := req.Filename + ".tmp"
tmpPath := filepath.Join(fetcher.sharedVolumePath, tmpFile) tmpPath := filepath.Join(fetcher.sharedVolumePath, tmpFile)
log.Printf("Start downloading...")
if req.FetchType == FETCH_URL { if req.FetchType == FETCH_URL {
// fetch the file and save it to the tmp path // fetch the file and save it to the tmp path
err := downloadUrl(req.Url, tmpPath) err := downloadUrl(req.Url, tmpPath)
@@ -286,7 +284,7 @@ func (fetcher *Fetcher) UploadHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
log.Println("Start uploading...") log.Println("Starting upload...")
ssClient := storageSvcClient.MakeClient(req.StorageSvcUrl) ssClient := storageSvcClient.MakeClient(req.StorageSvcUrl)
fileID, err := ssClient.Upload(dstFilepath, nil) fileID, err := ssClient.Upload(dstFilepath, nil)
@@ -332,7 +330,8 @@ func (fetcher *Fetcher) rename(src string, dst string) error {
return nil return nil
} }
// archive is a function that zips directory into a zip file // archive zips the contents of directory at src into a new zip file
// at dst (note that the contents are zipped, not the directory itself).
func (fetcher *Fetcher) archive(src string, dst string) error { func (fetcher *Fetcher) archive(src string, dst string) error {
var files []string var files []string
target, err := os.Stat(src) target, err := os.Stat(src)
+3 -1
View File
@@ -1,4 +1,6 @@
FROM golang:1.8.1 ARG GO_VERSION=1.9.2
FROM golang:${GO_VERSION}
ENV GOPATH /usr ENV GOPATH /usr
ENV APP ${GOPATH}/src/github.com/fission/fission/environments/go ENV APP ${GOPATH}/src/github.com/fission/fission/environments/go
+10 -1
View File
@@ -1,5 +1,14 @@
FROM golang:1.8.1 ARG BUILDER_IMAGE=fission/builder
ARG GO_VERSION=1.9.2
FROM ${BUILDER_IMAGE}
FROM golang:${GO_VERSION}
COPY --from=0 /builder /builder
ENV GOPATH /usr ENV GOPATH /usr
RUN go get github.com/fission/fission RUN go get github.com/fission/fission
ADD build.sh /usr/local/bin/build
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
cd ${SRC_PKG}
go build -buildmode=plugin -i -o ${DEPLOY_PKG} .
-11
View File
@@ -1,11 +0,0 @@
#!/bin/sh
if [ $# -eq 0 ]
then
echo "No arguments, building current dir"
filename='.'
else
filename=$1
fi
docker run --rm -v $(pwd):/build -w /build fission/go-builder:1.8.1 go build -buildmode=plugin -o function.so $filename && echo "Compiled plugin: function.so. Use 'fission function create' to upload to fission."
+82 -5
View File
@@ -1,9 +1,12 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath"
"plugin" "plugin"
"github.com/fission/fission/environments/go/context" "github.com/fission/fission/environments/go/context"
@@ -13,14 +16,52 @@ const (
CODE_PATH = "/userfunc/user" CODE_PATH = "/userfunc/user"
) )
type (
FunctionLoadRequest struct {
// FilePath is an absolute filesystem path to the
// function. What exactly is stored here is
// env-specific. Optional.
FilePath string `json:"filepath"`
// FunctionName has an environment-specific meaning;
// usually, it defines a function within a module
// containing multiple functions. Optional; default is
// environment-specific.
FunctionName string `json:"functionName"`
// URL to expose this function at. Optional; defaults
// to "/".
URL string `json:"url"`
}
)
var userFunc http.HandlerFunc var userFunc http.HandlerFunc
func loadPlugin() http.HandlerFunc { func loadPlugin(codePath, entrypoint string) http.HandlerFunc {
p, err := plugin.Open(CODE_PATH)
// if codepath's a directory, load the file inside it
info, err := os.Stat(codePath)
if err != nil { if err != nil {
panic(err) panic(err)
} }
sym, err := p.Lookup("Handler") if info.IsDir() {
files, err := ioutil.ReadDir(codePath)
if err != nil {
panic(err)
}
if len(files) == 0 {
panic("No files to load")
}
fi := files[0]
codePath = filepath.Join(codePath, fi.Name())
}
fmt.Printf("loading plugin from %v\n", codePath)
p, err := plugin.Open(codePath)
if err != nil {
panic(err)
}
sym, err := p.Lookup(entrypoint)
if err != nil { if err != nil {
panic("Entry point not found") panic("Entry point not found")
} }
@@ -44,7 +85,7 @@ func loadPlugin() http.HandlerFunc {
func specializeHandler(w http.ResponseWriter, r *http.Request) { func specializeHandler(w http.ResponseWriter, r *http.Request) {
if userFunc != nil { if userFunc != nil {
w.WriteHeader(400) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Not a generic container")) w.Write([]byte("Not a generic container"))
return return
} }
@@ -61,12 +102,48 @@ func specializeHandler(w http.ResponseWriter, r *http.Request) {
} }
fmt.Println("Specializing ...") fmt.Println("Specializing ...")
userFunc = loadPlugin() userFunc = loadPlugin(CODE_PATH, "Handler")
fmt.Println("Done")
}
func specializeHandlerV2(w http.ResponseWriter, r *http.Request) {
if userFunc != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Not a generic container"))
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
var loadreq FunctionLoadRequest
err = json.Unmarshal(body, &loadreq)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
_, err = os.Stat(loadreq.FilePath)
if err != nil {
if os.IsNotExist(err) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(CODE_PATH + ": not found"))
return
} else {
panic(err)
}
}
fmt.Println("Specializing ...")
userFunc = loadPlugin(loadreq.FilePath, loadreq.FunctionName)
fmt.Println("Done") fmt.Println("Done")
} }
func main() { func main() {
http.HandleFunc("/specialize", specializeHandler) http.HandleFunc("/specialize", specializeHandler)
http.HandleFunc("/v2/specialize", specializeHandlerV2)
// Generic route -- all http requests go to the user function. // Generic route -- all http requests go to the user function.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+14 -3
View File
@@ -1,7 +1,18 @@
# A docker image for the func container. # A docker image for the func container.
FROM node:8-onbuild # default variant is the official alpine node image (much smaller than the standard image)
FROM node:8-alpine
ADD server.js /usr/src/app/server.js ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
EXPOSE 8888 RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install && npm cache clean --force
COPY server.js /usr/src/app/server.js
CMD [ "npm", "start" ]
EXPOSE 8888
+18
View File
@@ -0,0 +1,18 @@
# A docker image for the func container.
# debian variant is the official standard node image (larger than the alpine image)
FROM node:8
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install && npm cache clean --force
COPY server.js /usr/src/app/server.js
CMD [ "npm", "start" ]
EXPOSE 8888
+14
View File
@@ -0,0 +1,14 @@
FROM alpine:3.5
RUN apk update
RUN apk add --no-cache python python-dev build-base py-pip
RUN pip install --upgrade pip
RUN rm -r /root/.cache
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["server.py"]
@@ -1,4 +1,5 @@
FROM alpine:3.5 ARG BUILDER_IMAGE=fission/builder:latest
FROM ${BUILDER_IMAGE}
RUN apk update RUN apk update
RUN apk add --no-cache python3 python3-dev build-base RUN apk add --no-cache python3 python3-dev build-base
@@ -6,6 +7,5 @@ RUN pip3 install --upgrade pip
RUN rm -r /root/.cache RUN rm -r /root/.cache
ADD defaultBuildCmd /usr/local/bin/build ADD defaultBuildCmd /usr/local/bin/build
ADD builder /builder
EXPOSE 8001 EXPOSE 8001
@@ -24,12 +24,41 @@ def loadv2():
global userfunc global userfunc
body = request.get_json() body = request.get_json()
filepath = body['filepath'] filepath = body['filepath']
functionName = body['functionName'] handler = body['functionName']
# add filepath into syspath for module import
sys.path.append(filepath) # The value of "functionName" is consist of `<module-name>.<function-name>`.
fn, path, desc = imp.find_module('user', [filepath]) moduleName, funcName = handler.split(".")
mod = imp.load_module('user', fn, path, desc)
userfunc = getattr(mod, functionName) # check whether the destination is a directory or a file
if os.path.isdir(filepath):
# add package directory path into module search path
sys.path.append(filepath)
# find module from package path we append previously.
# Python will try to find module from the same name file under
# the package directory. If search is successful, the return
# value is a 3-element tuple; otherwise, an exception "ImportError"
# is raised.
# Second parameter of find_module enforces python to find same
# name module from the given list of directories to prevent name
# confliction with built-in modules.
f, path, desc = imp.find_module(moduleName, [filepath])
# load module
# Return module object is the load is successful; otherwise,
# an exception is raised.
try:
mod = imp.load_module(moduleName, f, path, desc)
finally:
if f:
f.close()
else:
# load source from destination python file
mod = imp.load_source(moduleName, filepath)
# load user function from module
userfunc = getattr(mod, funcName)
return "" return ""
@app.route('/', methods=['GET', 'POST', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']) @app.route('/', methods=['GET', 'POST', 'PUT', 'HEAD', 'OPTIONS', 'DELETE'])
@@ -66,4 +95,4 @@ def setup_logger(loglevel):
# #
setup_logger(logging.DEBUG) setup_logger(logging.DEBUG)
app.logger.info("Starting server") app.logger.info("Starting server")
app.run(host='0.0.0.0', port='8888') app.run(host='0.0.0.0', port=8888)
+27 -1
View File
@@ -2,12 +2,19 @@
This directory contains several examples to get you started using Node.js with Fission. This directory contains several examples to get you started using Node.js with Fission.
## Environment
Before running any of these functions, make sure you have created a `nodejs` Fission environment: Before running any of these functions, make sure you have created a `nodejs` Fission environment:
``` ```
$ fission env create --name nodejs --image fission/node-env $ fission env create --name nodejs --image fission/node-env
``` ```
Note: The default `fission/node-env` image is based on Alpine, which is much smaller than the main Debian Node image (65MB vs 680MB) while still being suitable for most use cases.
If you need to use the full Debian image use the `fission/node-env-debian` image instead.
See the [official Node docker hub repo](https://hub.docker.com/_/node/) for considerations
relating to this choice.
## Function signature ## Function signature
Every Node.js function has the same basic form: Every Node.js function has the same basic form:
@@ -75,7 +82,7 @@ This is a basic example of how you can easily use asynchronous requests in your
$ fission function create --name stock --env nodejs --code stock.js $ fission function create --name stock --env nodejs --code stock.js
# Map GET /stock to your new function # Map GET /stock to your new function
$ fission route create --method GET --url /stock --function stock $ fission route create --method POST --url /stock --function stock
# Run the function. # Run the function.
$ curl -H "Content-Type: application/json" -X POST -d '{"symbol":"AAPL"}' http://$FISSION_ROUTER/stock $ curl -H "Content-Type: application/json" -X POST -d '{"symbol":"AAPL"}' http://$FISSION_ROUTER/stock
@@ -96,3 +103,22 @@ $ fission fn create --name kubeEventsSlack --env nodejs --code kubeEventsSlack.j
# Watch all services in the default namespace: # Watch all services in the default namespace:
$ fission watch create --function kubeEventsSlack --type service --ns default $ fission watch create --function kubeEventsSlack --type service --ns default
``` ```
## weather.js
In this example, the Yahoo Weather API is used to current weather at a given location.
### Usage
```bash
# Upload your function code to fission
$ fission function create --name weather --env nodejs --code weather.js
# Map GET /stock to your new function
$ fission route create --method POST --url /weather --function weather
# Run the function.
$ curl -H "Content-Type: application/json" -X POST -d '{"location":"Sieteiglesias, Spain"}' http://$FISSION_ROUTER/weather
{"text":"It is 2 celsius degrees in Sieteiglesias, Spain and Mostly Clear"}
```
+40
View File
@@ -0,0 +1,40 @@
'use strict';
const rp = require('request-promise-native');
module.exports = async function (context) {
const stringBody = JSON.stringify(context.request.body);
const body = JSON.parse(stringBody);
const location = body.location;
if (!location) {
return {
status: 400,
body: {
text: 'You must provide a location.'
}
};
}
try {
const response = await rp(`https://query.yahooapis.com/v1/public/yql?q=select item.condition from weather.forecast where woeid in (select woeid from geo.places(1) where text="${location}") and u="c"&format=json`);
const condition = JSON.parse(response).query.results.channel.item.condition;
const text = condition.text;
const temperature = condition.temp;
return {
status: 200,
body: {
text: `It is ${temperature} celsius degrees in ${location} and ${text}`
},
headers: {
'Content-Type': 'application/json'
}
};
} catch (e) {
console.error(e);
return {
status: 500,
body: e
};
}
}
+62
View File
@@ -0,0 +1,62 @@
This is an example of creating a deployment package with multiple
files including some static data in text file.
### Create an environment
```
fission env create --name python --image fission/python-env:0.4.0rc --version 2
```
### Create a zip file with all your files
```
zip -jr multifile.zip *.py *.txt
```
### Create a function
Since there are multiple files, you have to specify an _entrypoint_ to
for the function. Its format is `<file name>.<function name>`. In our
example, that's `main.main`, to run function `main` in `main.py`.
```
fission function create --name multifile --env python --code multifile.zip --entrypoint main.main
```
### Test it
```
fission function test --name multifile
```
You should see the "Hello, world" message.
## Updating the function
### Edit a file
```
echo "I said hellooooo!" > message.txt
```
### Update the deployment package
```
zip -jr multifile.zip *.py *.txt
```
### Update the function
```
fission function update --name multifile --code multifile.zip
```
### Test it
```
fission function test --name multifile
```
You should now see your new, edited message.
+11
View File
@@ -0,0 +1,11 @@
from flask import current_app
import sys
import readfile
import os
def main():
current_app.logger.info("Hi")
current_dir = os.path.dirname(__file__)
return readfile.readFile(os.path.join(current_dir, "message.txt"))
+1
View File
@@ -0,0 +1 @@
Hello, world!
+3
View File
@@ -0,0 +1,3 @@
def readFile(name):
with open(name) as f:
return f.read()
+4
View File
@@ -0,0 +1,4 @@
def main():
# You can return any http status code you like, simply place a comma after
# your return statement, and typing in the status code.
return "Not Found\n", 404
+108
View File
@@ -0,0 +1,108 @@
/*
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 executor
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission"
)
func (executor *Executor) getServiceForFunctionApi(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request", 500)
return
}
// get function metadata
m := metav1.ObjectMeta{}
err = json.Unmarshal(body, &m)
if err != nil {
http.Error(w, "Failed to parse request", 400)
return
}
serviceName, err := executor.getServiceForFunction(&m)
if err != nil {
code, msg := fission.GetHTTPError(err)
log.Printf("Error: %v: %v", code, msg)
http.Error(w, msg, code)
return
}
w.Write([]byte(serviceName))
}
func (executor *Executor) getServiceForFunction(m *metav1.ObjectMeta) (string, error) {
// Check function -> svc cache
log.Printf("[%v] Checking for cached function service", m.Name)
fsvc, err := executor.fsCache.GetByFunction(m)
if err == nil {
// Cached, return svc address
return fsvc.Address, nil
}
respChan := make(chan *createFuncServiceResponse)
executor.requestChan <- &createFuncServiceRequest{
funcMeta: m,
respChan: respChan,
}
resp := <-respChan
if resp.err != nil {
return "", resp.err
}
return resp.funcSvc.Address, resp.err
}
// find funcSvc and update its atime
func (executor *Executor) tapService(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request", 500)
return
}
svcName := string(body)
svcHost := strings.TrimPrefix(svcName, "http://")
err = executor.fsCache.TouchByAddress(svcHost)
if err != nil {
log.Printf("funcSvc tap error: %v", err)
http.Error(w, "Not found", 404)
return
}
w.WriteHeader(http.StatusOK)
}
func (executor *Executor) Serve(port int) {
r := mux.NewRouter()
r.HandleFunc("/v2/getServiceForFunction", executor.getServiceForFunctionApi).Methods("POST")
r.HandleFunc("/v2/tapService", executor.tapService).Methods("POST")
address := fmt.Sprintf(":%v", port)
log.Printf("starting executor at port %v", port)
log.Fatal(http.ListenAndServe(address, handlers.LoggingHandler(os.Stdout, r)))
}
@@ -26,20 +26,20 @@ import (
"strings" "strings"
"time" "time"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
) )
type Client struct { type Client struct {
poolmgrUrl string executorUrl string
tappedByUrl map[string]bool tappedByUrl map[string]bool
requestChan chan string requestChan chan string
} }
func MakeClient(poolmgrUrl string) *Client { func MakeClient(executorUrl string) *Client {
c := &Client{ c := &Client{
poolmgrUrl: strings.TrimSuffix(poolmgrUrl, "/"), executorUrl: strings.TrimSuffix(executorUrl, "/"),
tappedByUrl: make(map[string]bool), tappedByUrl: make(map[string]bool),
requestChan: make(chan string), requestChan: make(chan string),
} }
@@ -47,15 +47,15 @@ func MakeClient(poolmgrUrl string) *Client {
return c return c
} }
func (c *Client) GetServiceForFunction(metadata *api.ObjectMeta) (string, error) { func (c *Client) GetServiceForFunction(metadata *metav1.ObjectMeta) (string, error) {
poolmgrUrl := c.poolmgrUrl + "/v2/getServiceForFunction" executorUrl := c.executorUrl + "/v2/getServiceForFunction"
body, err := json.Marshal(metadata) body, err := json.Marshal(metadata)
if err != nil { if err != nil {
return "", err return "", err
} }
resp, err := http.Post(poolmgrUrl, "application/json", bytes.NewReader(body)) resp, err := http.Post(executorUrl, "application/json", bytes.NewReader(body))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -100,9 +100,9 @@ func (c *Client) TapService(serviceUrl *url.URL) {
} }
func (c *Client) _tapService(serviceUrlStr string) error { func (c *Client) _tapService(serviceUrlStr string) error {
poolmgrUrl := c.poolmgrUrl + "/v2/tapService" executorUrl := c.executorUrl + "/v2/tapService"
resp, err := http.Post(poolmgrUrl, "application/octet-stream", bytes.NewReader([]byte(serviceUrlStr))) resp, err := http.Post(executorUrl, "application/octet-stream", bytes.NewReader([]byte(serviceUrlStr)))
if err != nil { if err != nil {
return err return err
} }
+22
View File
@@ -0,0 +1,22 @@
/*
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 deploymgr
func GetFuncSvc() (*funcSvc, error) {
return nil, nil
}
+195
View File
@@ -0,0 +1,195 @@
/*
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 executor
import (
"log"
"os"
"sync"
"time"
"github.com/dchest/uniuri"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/cache"
"github.com/fission/fission/crd"
"github.com/fission/fission/executor/fscache"
"github.com/fission/fission/executor/poolmgr"
)
type (
Executor struct {
gpm *poolmgr.GenericPoolManager
functionEnv *cache.Cache
fissionClient *crd.FissionClient
fsCache *fscache.FunctionServiceCache
requestChan chan *createFuncServiceRequest
fsCreateWg map[string]*sync.WaitGroup
}
createFuncServiceRequest struct {
funcMeta *metav1.ObjectMeta
respChan chan *createFuncServiceResponse
}
createFuncServiceResponse struct {
funcSvc *fscache.FuncSvc
err error
}
)
func MakeExecutor(gpm *poolmgr.GenericPoolManager, fissionClient *crd.FissionClient, fsCache *fscache.FunctionServiceCache) *Executor {
executor := &Executor{
gpm: gpm,
functionEnv: cache.MakeCache(10*time.Second, 0),
fissionClient: fissionClient,
fsCache: fsCache,
requestChan: make(chan *createFuncServiceRequest),
fsCreateWg: make(map[string]*sync.WaitGroup),
}
go executor.serveCreateFuncServices()
return executor
}
// All non-cached function service requests go through this goroutine
// serially. It parallelizes requests for different functions, and
// ensures that for a given function, only one request causes a pod to
// get specialized. In other words, it ensures that when there's an
// ongoing request for a certain function, all other requests wait for
// that request to complete.
func (executor *Executor) serveCreateFuncServices() {
for {
req := <-executor.requestChan
m := req.funcMeta
// Cache miss -- is this first one to request the func?
wg, found := executor.fsCreateWg[crd.CacheKey(m)]
if !found {
// create a waitgroup for other requests for
// the same function to wait on
wg := &sync.WaitGroup{}
wg.Add(1)
executor.fsCreateWg[crd.CacheKey(m)] = wg
// launch a goroutine for each request, to parallelize
// the specialization of different functions
go func() {
fsvc, err := executor.createServiceForFunction(m)
req.respChan <- &createFuncServiceResponse{
funcSvc: fsvc,
err: err,
}
delete(executor.fsCreateWg, crd.CacheKey(m))
wg.Done()
}()
} else {
// There's an existing request for this function, wait for it to finish
go func() {
log.Printf("Waiting for concurrent request for the same function: %v", m)
wg.Wait()
// get the function service from the cache
fsvc, err := executor.fsCache.GetByFunction(m)
req.respChan <- &createFuncServiceResponse{
funcSvc: fsvc,
err: err,
}
}()
}
}
}
func (executor *Executor) createServiceForFunction(m *metav1.ObjectMeta) (*fscache.FuncSvc, error) {
log.Printf("[%v] No cached function service found, creating one", m.Name)
env, err := executor.getFunctionEnv(m)
if err != nil {
return nil, err
}
// Appropriate backend handles the service creation
backend := os.Getenv("EXECUTOR_BACKEND")
switch backend {
case "DEPLOY":
return nil, nil
default:
pool, err := executor.gpm.GetPool(env)
if err != nil {
return nil, err
}
// from GenericPool -> get one function container
// (this also adds to the cache)
log.Printf("[%v] getting function service from pool", m.Name)
fsvc, err := pool.GetFuncSvc(m)
if err != nil {
return nil, err
}
return fsvc, nil
}
}
func (executor *Executor) getFunctionEnv(m *metav1.ObjectMeta) (*crd.Environment, error) {
var env *crd.Environment
// Cached ?
result, err := executor.functionEnv.Get(crd.CacheKey(m))
if err == nil {
env = result.(*crd.Environment)
return env, nil
}
// Cache miss -- get func from controller
f, err := executor.fissionClient.Functions(m.Namespace).Get(m.Name)
if err != nil {
return nil, err
}
// Get env from metadata
log.Printf("[%v] getting env", m)
env, err = executor.fissionClient.Environments(f.Spec.Environment.Namespace).Get(f.Spec.Environment.Name)
if err != nil {
return nil, err
}
// cache for future lookups
executor.functionEnv.Set(crd.CacheKey(m), env)
return env, nil
}
// StartExecutor Starts executor and the backend components that executor uses such as Poolmgr,
// deploymgr and potential future backends
func StartExecutor(fissionNamespace string, functionNamespace string, port int) error {
fissionClient, kubernetesClient, _, err := crd.MakeFissionClient()
if err != nil {
log.Printf("Failed to get kubernetes client: %v", err)
return err
}
instanceID := uniuri.NewLen(8)
poolmgr.CleanupOldPoolmgrResources(kubernetesClient, functionNamespace, instanceID)
fsCache := fscache.MakeFunctionServiceCache()
gpm := poolmgr.MakeGenericPoolManager(
fissionClient, kubernetesClient, fissionNamespace,
functionNamespace, fsCache, instanceID)
api := MakeExecutor(gpm, fissionClient, fsCache)
go api.Serve(port)
return nil
}
@@ -20,10 +20,11 @@
// Here's how I run this on my setup, with minikube: // Here's how I run this on my setup, with minikube:
// TEST_SPECIALIZE_URL=http://192.168.99.100:30002/specialize TEST_FETCHER_URL=http://192.168.99.100:30001 FETCHER_IMAGE=minikube/fetcher:testing KUBECONFIG=/Users/soam/.kube/config go test -v . // TEST_SPECIALIZE_URL=http://192.168.99.100:30002/specialize TEST_FETCHER_URL=http://192.168.99.100:30001 FETCHER_IMAGE=minikube/fetcher:testing KUBECONFIG=/Users/soam/.kube/config go test -v .
package poolmgr package executor
import ( import (
"fmt" "fmt"
"io/ioutil"
"log" "log"
"math/rand" "math/rand"
"net/http" "net/http"
@@ -31,22 +32,21 @@ import (
"testing" "testing"
"time" "time"
"k8s.io/client-go/1.5/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api" "k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/1.5/pkg/api/v1" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/1.5/pkg/labels" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/1.5/pkg/util/intstr" apiv1 "k8s.io/client-go/pkg/api/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/poolmgr/client" "github.com/fission/fission/crd"
"github.com/fission/fission/tpr" "github.com/fission/fission/executor/client"
"io/ioutil"
) )
// return the number of pods in the given namespace matching the given labels // return the number of pods in the given namespace matching the given labels
func countPods(kubeClient *kubernetes.Clientset, ns string, labelz map[string]string) int { func countPods(kubeClient *kubernetes.Clientset, ns string, labelz map[string]string) int {
pods, err := kubeClient.Pods(ns).List(api.ListOptions{ pods, err := kubeClient.Pods(ns).List(metav1.ListOptions{
LabelSelector: labels.Set(labelz).AsSelector(), LabelSelector: labels.Set(labelz).AsSelector().String(),
}) })
if err != nil { if err != nil {
log.Panicf("Failed to list pods: %v", err) log.Panicf("Failed to list pods: %v", err)
@@ -55,8 +55,8 @@ func countPods(kubeClient *kubernetes.Clientset, ns string, labelz map[string]st
} }
func createTestNamespace(kubeClient *kubernetes.Clientset, ns string) { func createTestNamespace(kubeClient *kubernetes.Clientset, ns string) {
_, err := kubeClient.Namespaces().Create(&v1.Namespace{ _, err := kubeClient.Namespaces().Create(&apiv1.Namespace{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: ns, Name: ns,
}, },
}) })
@@ -67,16 +67,16 @@ func createTestNamespace(kubeClient *kubernetes.Clientset, ns string) {
} }
// create a nodeport service // create a nodeport service
func createSvc(kubeClient *kubernetes.Clientset, ns string, name string, targetPort int, nodePort int32, labels map[string]string) *v1.Service { func createSvc(kubeClient *kubernetes.Clientset, ns string, name string, targetPort int, nodePort int32, labels map[string]string) *apiv1.Service {
svc, err := kubeClient.Services(ns).Create(&v1.Service{ svc, err := kubeClient.Services(ns).Create(&apiv1.Service{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
}, },
Spec: v1.ServiceSpec{ Spec: apiv1.ServiceSpec{
Type: v1.ServiceTypeNodePort, Type: apiv1.ServiceTypeNodePort,
Ports: []v1.ServicePort{ Ports: []apiv1.ServicePort{
{ {
Protocol: v1.ProtocolTCP, Protocol: apiv1.ProtocolTCP,
Port: 80, Port: 80,
TargetPort: intstr.FromInt(targetPort), TargetPort: intstr.FromInt(targetPort),
NodePort: nodePort, NodePort: nodePort,
@@ -104,7 +104,7 @@ func httpGet(url string) string {
return string(body) return string(body)
} }
func TestPoolmgr(t *testing.T) { func TestExecutor(t *testing.T) {
// run in a random namespace so we can have concurrent tests // run in a random namespace so we can have concurrent tests
// on a given cluster // on a given cluster
rand.Seed(time.Now().UTC().UnixNano()) rand.Seed(time.Now().UTC().UnixNano())
@@ -120,8 +120,8 @@ func TestPoolmgr(t *testing.T) {
} }
// connect to k8s // connect to k8s
// and get TPR client // and get CRD client
fissionClient, kubeClient, err := tpr.MakeFissionClient() fissionClient, kubeClient, apiExtClient, err := crd.MakeFissionClient()
if err != nil { if err != nil {
log.Panicf("failed to connect: %v", err) log.Panicf("failed to connect: %v", err)
} }
@@ -133,16 +133,16 @@ func TestPoolmgr(t *testing.T) {
createTestNamespace(kubeClient, functionNs) createTestNamespace(kubeClient, functionNs)
defer kubeClient.Namespaces().Delete(functionNs, nil) defer kubeClient.Namespaces().Delete(functionNs, nil)
// make sure TPR types exist on cluster // make sure CRD types exist on cluster
err = tpr.EnsureFissionTPRs(kubeClient) err = crd.EnsureFissionCRDs(apiExtClient)
if err != nil { if err != nil {
log.Panicf("failed to ensure tprs: %v", err) log.Panicf("failed to ensure crds: %v", err)
} }
fissionClient.WaitForTPRs() fissionClient.WaitForCRDs()
// create an env on the cluster // create an env on the cluster
env, err := fissionClient.Environments(fissionNs).Create(&tpr.Environment{ env, err := fissionClient.Environments(fissionNs).Create(&crd.Environment{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "nodejs", Name: "nodejs",
Namespace: fissionNs, Namespace: fissionNs,
}, },
@@ -160,7 +160,7 @@ func TestPoolmgr(t *testing.T) {
// create poolmgr // create poolmgr
port := 9999 port := 9999
err = StartPoolmgr(fissionNs, functionNs, port) err = StartExecutor(fissionNs, functionNs, port)
if err != nil { if err != nil {
log.Panicf("failed to start poolmgr: %v", err) log.Panicf("failed to start poolmgr: %v", err)
} }
@@ -173,37 +173,47 @@ func TestPoolmgr(t *testing.T) {
// waitForPool(functionNs, "nodejs") // waitForPool(functionNs, "nodejs")
time.Sleep(6 * time.Second) time.Sleep(6 * time.Second)
envRef := fission.EnvironmentReference{
Namespace: env.Metadata.Namespace,
Name: env.Metadata.Name,
}
deployment := fission.Archive{
Type: fission.ArchiveTypeLiteral,
Literal: []byte(`module.exports = async function(context) { return { status: 200, body: "Hello, world!\n" }; }`),
}
// create a package // create a package
p := &tpr.Package{ p := &crd.Package{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: fissionNs, Namespace: fissionNs,
}, },
Spec: fission.PackageSpec{ Spec: fission.PackageSpec{
Type: fission.PackageTypeLiteral, Environment: envRef,
Literal: []byte(`module.exports = async function(context) { return { status: 200, body: "Hello, world!\n" }; }`), Deployment: deployment,
}, },
} }
_, err = fissionClient.Packages(fissionNs).Create(p) p, err = fissionClient.Packages(fissionNs).Create(p)
if err != nil { if err != nil {
log.Panicf("failed to create package: %v", err) log.Panicf("failed to create package: %v", err)
} }
// create a function // create a function
f := &tpr.Function{ f := &crd.Function{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: fissionNs, Namespace: fissionNs,
}, },
Spec: fission.FunctionSpec{ Spec: fission.FunctionSpec{
Source: fission.FunctionPackageRef{}, Environment: envRef,
Deployment: fission.FunctionPackageRef{ Package: fission.FunctionPackageRef{
PackageRef: fission.PackageRef{ PackageRef: fission.PackageRef{
Name: p.Metadata.Name, Namespace: p.Metadata.Namespace,
Namespace: p.Metadata.Namespace, Name: p.Metadata.Name,
ResourceVersion: p.Metadata.ResourceVersion,
}, },
}, },
EnvironmentName: env.Metadata.Name,
}, },
} }
_, err = fissionClient.Functions(fissionNs).Create(f) _, err = fissionClient.Functions(fissionNs).Create(f)
+230
View File
@@ -0,0 +1,230 @@
/*
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 fscache
import (
"log"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/cache"
"github.com/fission/fission/crd"
)
type fscRequestType int
type backendType int
const (
TOUCH fscRequestType = iota
LISTOLD
LOG
)
const (
POOLMGR backendType = iota
NEWDEPLOY
)
type (
FuncSvc struct {
Function *metav1.ObjectMeta // function this pod/service is for
Environment *crd.Environment // function's environment
Address string // Host:Port or IP:Port that the function's service can be reached at.
KubernetesObjects []api.ObjectReference // Kubernetes Objects (within the function namespace)
Backend backendType
Ctime time.Time
Atime time.Time
}
FunctionServiceCache struct {
byFunction *cache.Cache // function-key -> funcSvc : map[string]*funcSvc
byAddress *cache.Cache // address -> function : map[string]metav1.ObjectMeta
requestChannel chan *fscRequest
}
fscRequest struct {
requestType fscRequestType
address string
kubernetesObjects []api.ObjectReference
age time.Duration
env *metav1.ObjectMeta // used for ListOld
responseChannel chan *fscResponse
}
fscResponse struct {
objects []*FuncSvc
deleted bool
error
}
)
func MakeFunctionServiceCache() *FunctionServiceCache {
fsc := &FunctionServiceCache{
byFunction: cache.MakeCache(0, 0),
byAddress: cache.MakeCache(0, 0),
requestChannel: make(chan *fscRequest),
}
go fsc.service()
return fsc
}
func (fsc *FunctionServiceCache) service() {
for {
req := <-fsc.requestChannel
resp := &fscResponse{}
switch req.requestType {
case TOUCH:
// update atime for this function svc
resp.error = fsc._touchByAddress(req.address)
case LISTOLD:
// get svcs idle for > req.age
fscs := fsc.byFunction.Copy()
funcObjects := make([]*FuncSvc, 0)
for _, funcSvc := range fscs {
fsvc := funcSvc.(*FuncSvc)
if fsvc.Environment.Metadata.UID == req.env.UID &&
time.Now().Sub(fsvc.Atime) > req.age {
funcObjects = append(funcObjects, fsvc)
}
}
resp.objects = funcObjects
case LOG:
funcCopy := fsc.byFunction.Copy()
log.Printf("Cache has %v entries", len(funcCopy))
for key, fsvcI := range funcCopy {
fsvc := fsvcI.(*FuncSvc)
for _, kubeObj := range fsvc.KubernetesObjects {
log.Printf("%v\t%v\t%v", key, kubeObj.Kind, kubeObj.Name)
}
}
}
req.responseChannel <- resp
}
}
func (fsc *FunctionServiceCache) GetByFunction(m *metav1.ObjectMeta) (*FuncSvc, error) {
key := crd.CacheKey(m)
fsvcI, err := fsc.byFunction.Get(key)
if err != nil {
return nil, err
}
// update atime
fsvc := fsvcI.(*FuncSvc)
fsvc.Atime = time.Now()
fsvcCopy := *fsvc
return &fsvcCopy, nil
}
// TODO: error should be second return
func (fsc *FunctionServiceCache) Add(fsvc FuncSvc) (error, *FuncSvc) {
err, existing := fsc.byFunction.Set(crd.CacheKey(fsvc.Function), &fsvc)
if err != nil {
if existing != nil {
f := existing.(*FuncSvc)
err2 := fsc.TouchByAddress(f.Address)
if err2 != nil {
return err2, nil
}
fCopy := *f
return err, &fCopy
}
return err, nil
}
now := time.Now()
fsvc.Ctime = now
fsvc.Atime = now
// Add to byAddress cache. Ignore NameExists errors
// because of multiple-specialization. See issue #331.
err, _ = fsc.byAddress.Set(fsvc.Address, *fsvc.Function)
if err != nil {
if fe, ok := err.(fission.Error); ok {
if fe.Code == fission.ErrorNameExists {
err = nil
}
}
log.Printf("error caching fsvc: %v", err)
return err, nil
}
return nil, nil
}
func (fsc *FunctionServiceCache) TouchByAddress(address string) error {
responseChannel := make(chan *fscResponse)
fsc.requestChannel <- &fscRequest{
requestType: TOUCH,
address: address,
responseChannel: responseChannel,
}
resp := <-responseChannel
return resp.error
}
func (fsc *FunctionServiceCache) _touchByAddress(address string) error {
mI, err := fsc.byAddress.Get(address)
if err != nil {
return err
}
m := mI.(metav1.ObjectMeta)
fsvcI, err := fsc.byFunction.Get(crd.CacheKey(&m))
if err != nil {
return err
}
fsvc := fsvcI.(*FuncSvc)
fsvc.Atime = time.Now()
return nil
}
func (fsc *FunctionServiceCache) DeleteOld(fsvc *FuncSvc, minAge time.Duration) (bool, error) {
if time.Now().Sub(fsvc.Atime) < minAge {
return false, nil
}
fsc.byFunction.Delete(crd.CacheKey(fsvc.Function))
fsc.byAddress.Delete(fsvc.Address)
return true, nil
}
func (fsc *FunctionServiceCache) ListOld(env *metav1.ObjectMeta, age time.Duration) ([]*FuncSvc, error) {
responseChannel := make(chan *fscResponse)
fsc.requestChannel <- &fscRequest{
requestType: LISTOLD,
age: age,
env: env,
responseChannel: responseChannel,
}
resp := <-responseChannel
return resp.objects, resp.error
}
func (fsc *FunctionServiceCache) Log() {
log.Printf("--- FunctionService Cache Contents")
responseChannel := make(chan *fscResponse)
fsc.requestChannel <- &fscRequest{
requestType: LOG,
responseChannel: responseChannel,
}
<-responseChannel
log.Printf("--- FunctionService Cache Contents End")
}
@@ -1,14 +1,15 @@
package poolmgr package fscache
import ( import (
"log" "log"
"testing" "testing"
"time" "time"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/pkg/api"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func TestFunctionServiceCache(t *testing.T) { func TestFunctionServiceCache(t *testing.T) {
@@ -17,16 +18,31 @@ func TestFunctionServiceCache(t *testing.T) {
log.Panicf("error creating cache") log.Panicf("error creating cache")
} }
var fsvc *funcSvc var fsvc *FuncSvc
now := time.Now() now := time.Now()
fsvc = &funcSvc{ objects := []api.ObjectReference{
function: &api.ObjectMeta{ {
Kind: "pod",
Name: "xxx",
APIVersion: "v1",
Namespace: "fission-function",
},
{
Kind: "pod",
Name: "xxx2",
APIVersion: "v1",
Namespace: "fission-function",
},
}
fsvc = &FuncSvc{
Function: &metav1.ObjectMeta{
Name: "foo", Name: "foo",
UID: "1212", UID: "1212",
}, },
environment: &tpr.Environment{ Environment: &crd.Environment{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: "foo-env", Name: "foo-env",
UID: "2323", UID: "2323",
}, },
@@ -38,10 +54,10 @@ func TestFunctionServiceCache(t *testing.T) {
Builder: fission.Builder{}, Builder: fission.Builder{},
}, },
}, },
address: "xxx", Address: "xxx",
podName: "yyy", KubernetesObjects: objects,
ctime: now, Ctime: now,
atime: now, Atime: now,
} }
err, _ := fsc.Add(*fsvc) err, _ := fsc.Add(*fsvc)
if err != nil { if err != nil {
@@ -49,25 +65,25 @@ func TestFunctionServiceCache(t *testing.T) {
log.Panicf("Failed to add fsvc: %v", err) log.Panicf("Failed to add fsvc: %v", err)
} }
f, err := fsc.GetByFunction(fsvc.function) f, err := fsc.GetByFunction(fsvc.Function)
if err != nil { if err != nil {
fsc.Log() fsc.Log()
log.Panicf("Failed to get fsvc: %v", err) log.Panicf("Failed to get fsvc: %v", err)
} }
fsvc.atime = f.atime fsvc.Atime = f.Atime
fsvc.ctime = f.ctime fsvc.Ctime = f.Ctime
if *f != *fsvc { if f.Address != fsvc.Address {
fsc.Log() fsc.Log()
log.Panicf("Incorrect fsvc \n(expected: %#v)\n (found: %#v)", fsvc, f) log.Panicf("Incorrect fsvc \n(expected: %#v)\n (found: %#v)", fsvc, f)
} }
err = fsc.TouchByAddress(fsvc.address) err = fsc.TouchByAddress(fsvc.Address)
if err != nil { if err != nil {
fsc.Log() fsc.Log()
log.Panicf("Failed to touch fsvc: %v", err) log.Panicf("Failed to touch fsvc: %v", err)
} }
deleted, err := fsc.DeleteByPod(fsvc.podName, 0) deleted, err := fsc.DeleteOld(fsvc, 0)
if err != nil { if err != nil {
fsc.Log() fsc.Log()
log.Panicf("Failed to delete fsvc: %v", err) log.Panicf("Failed to delete fsvc: %v", err)
@@ -77,7 +93,7 @@ func TestFunctionServiceCache(t *testing.T) {
log.Panicf("Did not delete fsvc") log.Panicf("Did not delete fsvc")
} }
_, err = fsc.GetByFunction(fsvc.function) _, err = fsc.GetByFunction(fsvc.Function)
if err == nil { if err == nil {
fsc.Log() fsc.Log()
log.Panicf("found fsvc while expecting empty cache: %v", err) log.Panicf("found fsvc while expecting empty cache: %v", err)
@@ -20,13 +20,13 @@ import (
"log" "log"
"time" "time"
"k8s.io/client-go/1.5/kubernetes" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api" "k8s.io/client-go/kubernetes"
) )
// cleanupOldPoolmgrResources looks for resources created by an old // cleanupOldPoolmgrResources looks for resources created by an old
// poolmgr instance and cleans them up. // poolmgr instance and cleans them up.
func cleanupOldPoolmgrResources(client *kubernetes.Clientset, namespace string, instanceId string) { func CleanupOldPoolmgrResources(client *kubernetes.Clientset, namespace string, instanceId string) {
go func() { go func() {
err := cleanup(client, namespace, instanceId) err := cleanup(client, namespace, instanceId)
if err != nil { if err != nil {
@@ -74,7 +74,7 @@ func cleanup(client *kubernetes.Clientset, namespace string, instanceId string)
} }
func cleanupDeployments(client *kubernetes.Clientset, namespace string, instanceId string) error { func cleanupDeployments(client *kubernetes.Clientset, namespace string, instanceId string) error {
deploymentList, err := client.Extensions().Deployments(namespace).List(api.ListOptions{}) deploymentList, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@@ -82,7 +82,7 @@ func cleanupDeployments(client *kubernetes.Clientset, namespace string, instance
id, ok := dep.ObjectMeta.Labels[POOLMGR_INSTANCEID_LABEL] id, ok := dep.ObjectMeta.Labels[POOLMGR_INSTANCEID_LABEL]
if ok && id != instanceId { if ok && id != instanceId {
log.Printf("Cleaning up deployment %v", dep.ObjectMeta.Name) log.Printf("Cleaning up deployment %v", dep.ObjectMeta.Name)
err := client.Extensions().Deployments(namespace).Delete(dep.ObjectMeta.Name, nil) err := client.ExtensionsV1beta1().Deployments(namespace).Delete(dep.ObjectMeta.Name, nil)
logErr("cleaning up deployment", err) logErr("cleaning up deployment", err)
// ignore err // ignore err
} }
@@ -91,7 +91,7 @@ func cleanupDeployments(client *kubernetes.Clientset, namespace string, instance
} }
func cleanupReplicaSets(client *kubernetes.Clientset, namespace string, instanceId string) error { func cleanupReplicaSets(client *kubernetes.Clientset, namespace string, instanceId string) error {
rsList, err := client.Extensions().ReplicaSets(namespace).List(api.ListOptions{}) rsList, err := client.ExtensionsV1beta1().ReplicaSets(namespace).List(meta_v1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@@ -99,7 +99,7 @@ func cleanupReplicaSets(client *kubernetes.Clientset, namespace string, instance
id, ok := rs.ObjectMeta.Labels[POOLMGR_INSTANCEID_LABEL] id, ok := rs.ObjectMeta.Labels[POOLMGR_INSTANCEID_LABEL]
if ok && id != instanceId { if ok && id != instanceId {
log.Printf("Cleaning up replicaset %v", rs.ObjectMeta.Name) log.Printf("Cleaning up replicaset %v", rs.ObjectMeta.Name)
err := client.Extensions().ReplicaSets(namespace).Delete(rs.ObjectMeta.Name, nil) err := client.ExtensionsV1beta1().ReplicaSets(namespace).Delete(rs.ObjectMeta.Name, nil)
logErr("cleaning up replicaset", err) logErr("cleaning up replicaset", err)
} }
} }
@@ -107,7 +107,7 @@ func cleanupReplicaSets(client *kubernetes.Clientset, namespace string, instance
} }
func cleanupPods(client *kubernetes.Clientset, namespace string, instanceId string) error { func cleanupPods(client *kubernetes.Clientset, namespace string, instanceId string) error {
podList, err := client.Core().Pods(namespace).List(api.ListOptions{}) podList, err := client.CoreV1().Pods(namespace).List(meta_v1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@@ -115,7 +115,7 @@ func cleanupPods(client *kubernetes.Clientset, namespace string, instanceId stri
id, ok := pod.ObjectMeta.Labels[POOLMGR_INSTANCEID_LABEL] id, ok := pod.ObjectMeta.Labels[POOLMGR_INSTANCEID_LABEL]
if ok && id != instanceId { if ok && id != instanceId {
log.Printf("Cleaning up pod %v", pod.ObjectMeta.Name) log.Printf("Cleaning up pod %v", pod.ObjectMeta.Name)
err := client.Core().Pods(namespace).Delete(pod.ObjectMeta.Name, nil) err := client.CoreV1().Pods(namespace).Delete(pod.ObjectMeta.Name, nil)
logErr("cleaning up pod", err) logErr("cleaning up pod", err)
// ignore err // ignore err
} }
@@ -124,7 +124,7 @@ func cleanupPods(client *kubernetes.Clientset, namespace string, instanceId stri
} }
func cleanupServices(client *kubernetes.Clientset, namespace string, instanceId string) error { func cleanupServices(client *kubernetes.Clientset, namespace string, instanceId string) error {
svcList, err := client.Core().Services(namespace).List(api.ListOptions{}) svcList, err := client.CoreV1().Services(namespace).List(meta_v1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@@ -132,7 +132,7 @@ func cleanupServices(client *kubernetes.Clientset, namespace string, instanceId
id, ok := svc.ObjectMeta.Labels[POOLMGR_INSTANCEID_LABEL] id, ok := svc.ObjectMeta.Labels[POOLMGR_INSTANCEID_LABEL]
if ok && id != instanceId { if ok && id != instanceId {
log.Printf("Cleaning up svc %v", svc.ObjectMeta.Name) log.Printf("Cleaning up svc %v", svc.ObjectMeta.Name)
err := client.Core().Services(namespace).Delete(svc.ObjectMeta.Name, nil) err := client.CoreV1().Services(namespace).Delete(svc.ObjectMeta.Name, nil)
logErr("cleaning up svc", err) logErr("cleaning up svc", err)
// ignore err // ignore err
} }
+157 -152
View File
@@ -32,18 +32,19 @@ import (
"time" "time"
"github.com/dchest/uniuri" "github.com/dchest/uniuri"
"k8s.io/client-go/1.5/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api" "k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/1.5/pkg/api/v1" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/1.5/pkg/labels" "k8s.io/client-go/pkg/api"
"k8s.io/client-go/1.5/pkg/util/intstr" apiv1 "k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/crd"
"github.com/fission/fission/environments/fetcher" "github.com/fission/fission/environments/fetcher"
fetcherClient "github.com/fission/fission/environments/fetcher/client" fetcherClient "github.com/fission/fission/environments/fetcher/client"
"github.com/fission/fission/logger" "github.com/fission/fission/executor/fscache"
"github.com/fission/fission/tpr"
) )
const POOLMGR_INSTANCEID_LABEL string = "poolmgrInstanceId" const POOLMGR_INSTANCEID_LABEL string = "poolmgrInstanceId"
@@ -51,24 +52,24 @@ const POD_PHASE_RUNNING string = "Running"
type ( type (
GenericPool struct { GenericPool struct {
env *tpr.Environment env *crd.Environment
replicas int32 // num idle pods replicas int32 // num idle pods
deployment *v1beta1.Deployment // kubernetes deployment deployment *v1beta1.Deployment // kubernetes deployment
namespace string // namespace to keep our resources namespace string // namespace to keep our resources
podReadyTimeout time.Duration // timeout for generic pods to become ready podReadyTimeout time.Duration // timeout for generic pods to become ready
idlePodReapTime time.Duration // pods unused for idlePodReapTime are deleted idlePodReapTime time.Duration // pods unused for idlePodReapTime are deleted
fsCache *functionServiceCache // cache funcSvc's by function, address and podname fsCache *fscache.FunctionServiceCache // cache funcSvc's by function, address and podname
useSvc bool // create k8s service for specialized pods useSvc bool // create k8s service for specialized pods
poolInstanceId string // small random string to uniquify pod names poolInstanceId string // small random string to uniquify pod names
fetcherImage string fetcherImage string
fetcherImagePullPolicy v1.PullPolicy fetcherImagePullPolicy apiv1.PullPolicy
runtimeImagePullPolicy v1.PullPolicy // pull policy for generic pool to created env deployment runtimeImagePullPolicy apiv1.PullPolicy // pull policy for generic pool to created env deployment
kubernetesClient *kubernetes.Clientset kubernetesClient *kubernetes.Clientset
fissionClient *tpr.FissionClient fissionClient *crd.FissionClient
instanceId string // poolmgr instance id instanceId string // poolmgr instance id
labelsForPool map[string]string labelsForPool map[string]string
requestChannel chan *choosePodRequest requestChannel chan *choosePodRequest
sharedMountPath string sharedMountPath string // used by generic pool when creating env deployment to specify the share volume path for fetcher & env
} }
// serialize the choosing of pods so that choices don't conflict // serialize the choosing of pods so that choices don't conflict
@@ -77,29 +78,29 @@ type (
responseChannel chan *choosePodResponse responseChannel chan *choosePodResponse
} }
choosePodResponse struct { choosePodResponse struct {
pod *v1.Pod pod *apiv1.Pod
error error
} }
) )
func getImagePullPolicy(policy string) v1.PullPolicy { func getImagePullPolicy(policy string) apiv1.PullPolicy {
switch policy { switch policy {
case "Always": case "Always":
return v1.PullAlways return apiv1.PullAlways
case "Never": case "Never":
return v1.PullNever return apiv1.PullNever
default: default:
return v1.PullIfNotPresent return apiv1.PullIfNotPresent
} }
} }
func MakeGenericPool( func MakeGenericPool(
fissionClient *tpr.FissionClient, fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, kubernetesClient *kubernetes.Clientset,
env *tpr.Environment, env *crd.Environment,
initialReplicas int32, initialReplicas int32,
namespace string, namespace string,
fsCache *functionServiceCache, fsCache *fscache.FunctionServiceCache,
instanceId string) (*GenericPool, error) { instanceId string) (*GenericPool, error) {
log.Printf("Creating pool for environment %v", env.Metadata) log.Printf("Creating pool for environment %v", env.Metadata)
@@ -133,7 +134,7 @@ func MakeGenericPool(
instanceId: instanceId, instanceId: instanceId,
fetcherImage: fetcherImage, fetcherImage: fetcherImage,
useSvc: false, // defaults off -- svc takes a second or more to become routable, slowing cold start useSvc: false, // defaults off -- svc takes a second or more to become routable, slowing cold start
sharedMountPath: "/userfunc", // used by generic pool when creating env deployment to specify the share volume path for fetcher & env sharedMountPath: "/userfunc", // change this may break v1 compatibility, since most of the v1 environments have hard-coded "/userfunc" in loading path
} }
gp.runtimeImagePullPolicy = getImagePullPolicy(runtimeImagePullPolicy) gp.runtimeImagePullPolicy = getImagePullPolicy(runtimeImagePullPolicy)
@@ -182,7 +183,7 @@ func (gp *GenericPool) choosePodService() {
// choosePod picks a ready pod from the pool and relabels it, waiting if necessary. // choosePod picks a ready pod from the pool and relabels it, waiting if necessary.
// returns the pod API object. // returns the pod API object.
func (gp *GenericPool) choosePod(newLabels map[string]string) (*v1.Pod, error) { func (gp *GenericPool) choosePod(newLabels map[string]string) (*apiv1.Pod, error) {
req := &choosePodRequest{ req := &choosePodRequest{
newLabels: newLabels, newLabels: newLabels,
responseChannel: make(chan *choosePodResponse), responseChannel: make(chan *choosePodResponse),
@@ -193,7 +194,7 @@ func (gp *GenericPool) choosePod(newLabels map[string]string) (*v1.Pod, error) {
} }
// _choosePod is called serially by choosePodService // _choosePod is called serially by choosePodService
func (gp *GenericPool) _choosePod(newLabels map[string]string) (*v1.Pod, error) { func (gp *GenericPool) _choosePod(newLabels map[string]string) (*apiv1.Pod, error) {
startTime := time.Now() startTime := time.Now()
for { for {
// Retries took too long, error out. // Retries took too long, error out.
@@ -203,14 +204,15 @@ func (gp *GenericPool) _choosePod(newLabels map[string]string) (*v1.Pod, error)
} }
// Get pods; filter the ones that are ready // Get pods; filter the ones that are ready
podList, err := gp.kubernetesClient.Core().Pods(gp.namespace).List( podList, err := gp.kubernetesClient.CoreV1().Pods(gp.namespace).List(
api.ListOptions{ metav1.ListOptions{
LabelSelector: labels.Set(gp.deployment.Spec.Selector.MatchLabels).AsSelector(), LabelSelector: labels.Set(
gp.deployment.Spec.Selector.MatchLabels).AsSelector().String(),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
readyPods := make([]*v1.Pod, 0, len(podList.Items)) readyPods := make([]*apiv1.Pod, 0, len(podList.Items))
for i := range podList.Items { for i := range podList.Items {
pod := podList.Items[i] pod := podList.Items[i]
@@ -252,7 +254,7 @@ func (gp *GenericPool) _choosePod(newLabels map[string]string) (*v1.Pod, error)
// retry. // retry.
chosenPod.ObjectMeta.Labels = newLabels chosenPod.ObjectMeta.Labels = newLabels
log.Printf("relabeling pod: [%v]", chosenPod.ObjectMeta.Name) log.Printf("relabeling pod: [%v]", chosenPod.ObjectMeta.Name)
_, err = gp.kubernetesClient.Core().Pods(gp.namespace).Update(chosenPod) _, err = gp.kubernetesClient.CoreV1().Pods(gp.namespace).Update(chosenPod)
if err != nil { if err != nil {
log.Printf("failed to relabel pod [%v]: %v", chosenPod.ObjectMeta.Name, err) log.Printf("failed to relabel pod [%v]: %v", chosenPod.ObjectMeta.Name, err)
continue continue
@@ -263,7 +265,7 @@ func (gp *GenericPool) _choosePod(newLabels map[string]string) (*v1.Pod, error)
} }
} }
func (gp *GenericPool) labelsForFunction(metadata *api.ObjectMeta) map[string]string { func (gp *GenericPool) labelsForFunction(metadata *metav1.ObjectMeta) map[string]string {
return map[string]string{ return map[string]string{
"functionName": metadata.Name, "functionName": metadata.Name,
"functionUid": string(metadata.UID), "functionUid": string(metadata.UID),
@@ -279,10 +281,15 @@ func (gp *GenericPool) scheduleDeletePod(name string) {
// aggregation and storage will help.) // aggregation and storage will help.)
log.Printf("Error in pod '%v', scheduling cleanup", name) log.Printf("Error in pod '%v', scheduling cleanup", name)
time.Sleep(5 * time.Minute) time.Sleep(5 * time.Minute)
gp.kubernetesClient.Core().Pods(gp.namespace).Delete(name, nil) gp.kubernetesClient.CoreV1().Pods(gp.namespace).Delete(name, nil)
}() }()
} }
func IsIPv6(podIP string) bool {
ip := net.ParseIP(podIP)
return ip != nil && strings.Contains(podIP, ":")
}
func (gp *GenericPool) getFetcherUrl(podIP string) string { func (gp *GenericPool) getFetcherUrl(podIP string) string {
testUrl := os.Getenv("TEST_FETCHER_URL") testUrl := os.Getenv("TEST_FETCHER_URL")
if len(testUrl) != 0 { if len(testUrl) != 0 {
@@ -292,24 +299,41 @@ func (gp *GenericPool) getFetcherUrl(podIP string) string {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
return testUrl return testUrl
} }
return fmt.Sprintf("http://%v:8000/", podIP) isv6 := IsIPv6(podIP)
var baseUrl string
if isv6 == false {
baseUrl = fmt.Sprintf("http://%v:8000/", podIP)
} else if isv6 == true { // We use bracket if the IP is in IPv6.
baseUrl = fmt.Sprintf("http://[%v]:8000/", podIP)
}
return baseUrl
} }
func (gp *GenericPool) getSpecializeUrl(podIP string, version int) string { func (gp *GenericPool) getSpecializeUrl(podIP string, version int) string {
u := os.Getenv("TEST_SPECIALIZE_URL") u := os.Getenv("TEST_SPECIALIZE_URL")
isv6 := IsIPv6(podIP)
var baseUrl string
if len(u) != 0 { if len(u) != 0 {
return u return u
} }
if version == 1 { if isv6 == false {
return fmt.Sprintf("http://%v:8888/specialize", podIP) baseUrl = fmt.Sprintf("http://%v:8888", podIP)
} else if isv6 == true { // We use bracket if the IP is in IPv6.
baseUrl = fmt.Sprintf("http://[%v]:8888", podIP)
}
if version == 1 {
return fmt.Sprintf("%v/specialize", baseUrl)
} else {
return fmt.Sprintf("%v/v%v/specialize", baseUrl, version)
} }
return fmt.Sprintf("http://%v:8888/v%v/specialize", podIP, version)
} }
// specializePod chooses a pod, copies the required user-defined function to that pod // specializePod chooses a pod, copies the required user-defined function to that pod
// (via fetcher), and calls the function-run container to load it, resulting in a // (via fetcher), and calls the function-run container to load it, resulting in a
// specialized pod. // specialized pod.
func (gp *GenericPool) specializePod(pod *v1.Pod, metadata *api.ObjectMeta) error { func (gp *GenericPool) specializePod(pod *apiv1.Pod, metadata *metav1.ObjectMeta) error {
// for fetcher we don't need to create a service, just talk to the pod directly // for fetcher we don't need to create a service, just talk to the pod directly
podIP := pod.Status.PodIP podIP := pod.Status.PodIP
if len(podIP) == 0 { if len(podIP) == 0 {
@@ -327,23 +351,26 @@ func (gp *GenericPool) specializePod(pod *v1.Pod, metadata *api.ObjectMeta) erro
return err return err
} }
// for backward compatibility, since most v1 env
// still try to load user function from hard coded
// path /userfunc/user
targetFilename := "user" targetFilename := "user"
if gp.env.Spec.Version == 2 {
targetFilename = string(fn.Metadata.UID)
}
err = fetcherClient.MakeClient(fetcherUrl).Fetch(&fetcher.FetchRequest{ err = fetcherClient.MakeClient(fetcherUrl).Fetch(&fetcher.FetchRequest{
FetchType: fetcher.FETCH_DEPLOYMENT, FetchType: fetcher.FETCH_DEPLOYMENT,
Package: api.ObjectMeta{ Package: metav1.ObjectMeta{
Namespace: fn.Spec.Package.PackageRef.Namespace, Namespace: fn.Spec.Package.PackageRef.Namespace,
Name: fn.Spec.Package.PackageRef.Name, Name: fn.Spec.Package.PackageRef.Name,
}, },
Filename: targetFilename, // XXX use function id instead Filename: targetFilename,
}) })
if err != nil { if err != nil {
return err return err
} }
// Tell logging helper about this function invocation
gp.setupLogging(pod, metadata)
// get function run container to specialize // get function run container to specialize
log.Printf("[%v] specializing pod", metadata.Name) log.Printf("[%v] specializing pod", metadata.Name)
@@ -406,35 +433,35 @@ func (gp *GenericPool) createPool() error {
gp.env.Metadata.Name, gp.env.Metadata.UID, strings.ToLower(gp.poolInstanceId)) gp.env.Metadata.Name, gp.env.Metadata.UID, strings.ToLower(gp.poolInstanceId))
deployment := &v1beta1.Deployment{ deployment := &v1beta1.Deployment{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: poolDeploymentName, Name: poolDeploymentName,
Labels: gp.labelsForPool, Labels: gp.labelsForPool,
}, },
Spec: v1beta1.DeploymentSpec{ Spec: v1beta1.DeploymentSpec{
Replicas: &gp.replicas, Replicas: &gp.replicas,
Selector: &v1beta1.LabelSelector{ Selector: &metav1.LabelSelector{
MatchLabels: gp.labelsForPool, MatchLabels: gp.labelsForPool,
}, },
Template: v1.PodTemplateSpec{ Template: apiv1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Labels: gp.labelsForPool, Labels: gp.labelsForPool,
}, },
Spec: v1.PodSpec{ Spec: apiv1.PodSpec{
Volumes: []v1.Volume{ Volumes: []apiv1.Volume{
{ {
Name: "userfunc", Name: "userfunc",
VolumeSource: v1.VolumeSource{ VolumeSource: apiv1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{}, EmptyDir: &apiv1.EmptyDirVolumeSource{},
}, },
}, },
}, },
Containers: []v1.Container{ Containers: []apiv1.Container{
{ {
Name: gp.env.Metadata.Name, Name: gp.env.Metadata.Name,
Image: gp.env.Spec.Runtime.Image, Image: gp.env.Spec.Runtime.Image,
ImagePullPolicy: gp.runtimeImagePullPolicy, ImagePullPolicy: gp.runtimeImagePullPolicy,
TerminationMessagePath: "/dev/termination-log", TerminationMessagePath: "/dev/termination-log",
VolumeMounts: []v1.VolumeMount{ VolumeMounts: []apiv1.VolumeMount{
{ {
Name: "userfunc", Name: "userfunc",
MountPath: gp.sharedMountPath, MountPath: gp.sharedMountPath,
@@ -446,7 +473,7 @@ func (gp *GenericPool) createPool() error {
Image: gp.fetcherImage, Image: gp.fetcherImage,
ImagePullPolicy: gp.fetcherImagePullPolicy, ImagePullPolicy: gp.fetcherImagePullPolicy,
TerminationMessagePath: "/dev/termination-log", TerminationMessagePath: "/dev/termination-log",
VolumeMounts: []v1.VolumeMount{ VolumeMounts: []apiv1.VolumeMount{
{ {
Name: "userfunc", Name: "userfunc",
MountPath: gp.sharedMountPath, MountPath: gp.sharedMountPath,
@@ -460,7 +487,7 @@ func (gp *GenericPool) createPool() error {
}, },
}, },
} }
depl, err := gp.kubernetesClient.Extensions().Deployments(gp.namespace).Create(deployment) depl, err := gp.kubernetesClient.ExtensionsV1beta1().Deployments(gp.namespace).Create(deployment)
if err != nil { if err != nil {
return err return err
} }
@@ -472,7 +499,8 @@ func (gp *GenericPool) waitForReadyPod() error {
startTime := time.Now() startTime := time.Now()
for { for {
// TODO: for now we just poll; use a watch instead // TODO: for now we just poll; use a watch instead
depl, err := gp.kubernetesClient.Extensions().Deployments(gp.namespace).Get(gp.deployment.ObjectMeta.Name) depl, err := gp.kubernetesClient.ExtensionsV1beta1().Deployments(gp.namespace).Get(
gp.deployment.ObjectMeta.Name, metav1.GetOptions{})
if err != nil { if err != nil {
log.Printf("err: %v", err) log.Printf("err: %v", err)
return err return err
@@ -489,16 +517,16 @@ func (gp *GenericPool) waitForReadyPod() error {
} }
} }
func (gp *GenericPool) createSvc(name string, labels map[string]string) (*v1.Service, error) { func (gp *GenericPool) createSvc(name string, labels map[string]string) (*apiv1.Service, error) {
service := v1.Service{ service := apiv1.Service{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
}, },
Spec: v1.ServiceSpec{ Spec: apiv1.ServiceSpec{
Type: v1.ServiceTypeClusterIP, Type: apiv1.ServiceTypeClusterIP,
Ports: []v1.ServicePort{ Ports: []apiv1.ServicePort{
{ {
Protocol: v1.ProtocolTCP, Protocol: apiv1.ProtocolTCP,
Port: 80, Port: 80,
TargetPort: intstr.FromInt(8888), TargetPort: intstr.FromInt(8888),
}, },
@@ -506,11 +534,11 @@ func (gp *GenericPool) createSvc(name string, labels map[string]string) (*v1.Ser
Selector: labels, Selector: labels,
}, },
} }
svc, err := gp.kubernetesClient.Core().Services(gp.namespace).Create(&service) svc, err := gp.kubernetesClient.CoreV1().Services(gp.namespace).Create(&service)
return svc, err return svc, err
} }
func (gp *GenericPool) GetFuncSvc(m *api.ObjectMeta) (*funcSvc, error) { func (gp *GenericPool) GetFuncSvc(m *metav1.ObjectMeta) (*fscache.FuncSvc, error) {
log.Printf("[%v] Choosing pod from pool", m.Name) log.Printf("[%v] Choosing pod from pool", m.Name)
newLabels := gp.labelsForFunction(m) newLabels := gp.labelsForFunction(m)
@@ -552,66 +580,69 @@ func (gp *GenericPool) GetFuncSvc(m *api.ObjectMeta) (*funcSvc, error) {
svcHost = fmt.Sprintf("%v:8888", pod.Status.PodIP) svcHost = fmt.Sprintf("%v:8888", pod.Status.PodIP)
} }
fsvc := &funcSvc{ kubeObjRefs := []api.ObjectReference{
function: m, {
environment: gp.env, Kind: pod.TypeMeta.Kind,
address: svcHost, Name: pod.ObjectMeta.Name,
podName: pod.ObjectMeta.Name, APIVersion: pod.TypeMeta.APIVersion,
ctime: time.Now(), Namespace: pod.ObjectMeta.Namespace,
atime: time.Now(), ResourceVersion: pod.ObjectMeta.ResourceVersion,
UID: pod.ObjectMeta.UID,
},
} }
err, existingFsvc := gp.fsCache.Add(*fsvc) fsvc := &fscache.FuncSvc{
Function: m,
Environment: gp.env,
Address: svcHost,
KubernetesObjects: kubeObjRefs,
Backend: fscache.POOLMGR,
Ctime: time.Now(),
Atime: time.Now(),
}
err, _ = gp.fsCache.Add(*fsvc)
if err != nil { if err != nil {
if fe, ok := err.(fission.Error); ok {
if fe.Code == fission.ErrorNameExists {
// Some other thread beat us to it -- return the other thread's fsvc and clean up
// our own.
log.Printf("func svc already exists: %v", existingFsvc.podName)
go func() {
gp.kubernetesClient.Core().Pods(gp.namespace).Delete(fsvc.podName, nil)
}()
return existingFsvc, nil
}
}
return nil, err return nil, err
} }
return fsvc, nil return fsvc, nil
} }
func (gp *GenericPool) CleanupFunctionService(podName string) error { func (gp *GenericPool) CleanupFunctionService(obj *fscache.FuncSvc) error {
// remove ourselves from fsCache (only if we're still old) // remove ourselves from fsCache (only if we're still old)
deleted, err := gp.fsCache.DeleteByPod(podName, gp.idlePodReapTime) deleted, err := gp.fsCache.DeleteOld(obj, gp.idlePodReapTime)
if err != nil { if err != nil {
return err return err
} }
if !deleted { if !deleted {
log.Printf("Not deleting %v, in use", podName) log.Printf("Not deleting %v, in use", obj.Function)
return nil return nil
} }
pod, err := gp.kubernetesClient.Core().Pods(gp.namespace).Get(podName) for _, kubeobj := range obj.KubernetesObjects {
if err != nil { pod, err := gp.kubernetesClient.CoreV1().Pods(gp.namespace).Get(kubeobj.Name, metav1.GetOptions{})
return err
}
loggerUrl := fmt.Sprintf("http://%s:1234/v1/log/%s", pod.Spec.NodeName, pod.Name) loggerUrl := fmt.Sprintf("http://%s:1234/v1/log/%s", pod.Spec.NodeName, pod.Name)
req, err := http.NewRequest("DELETE", loggerUrl, nil) req, err := http.NewRequest("DELETE", loggerUrl, nil)
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
log.Printf("Error from %s daemonset logger: %v", pod.Spec.NodeName, err) log.Printf("Error from %s daemonset logger: %v", pod.Spec.NodeName, err)
} else { } else {
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
log.Printf("Received not http 200(OK) status from %s daemonset logger: %s", pod.Spec.NodeName, resp.Status) log.Printf("Received not http 200(OK) status from %s daemonset logger: %s", pod.Spec.NodeName, resp.Status)
}
resp.Body.Close()
} }
resp.Body.Close()
}
// delete pod // delete pod
err = gp.kubernetesClient.Core().Pods(gp.namespace).Delete(podName, nil) err = gp.kubernetesClient.CoreV1().Pods(gp.namespace).Delete(kubeobj.Name, nil)
if err != nil { if err != nil {
return err return err
}
if err != nil {
return err
}
} }
return nil return nil
@@ -620,16 +651,19 @@ func (gp *GenericPool) CleanupFunctionService(podName string) error {
func (gp *GenericPool) idlePodReaper() { func (gp *GenericPool) idlePodReaper() {
for { for {
time.Sleep(time.Minute) time.Sleep(time.Minute)
podNames, err := gp.fsCache.ListOld(&gp.env.Metadata, gp.idlePodReapTime) funcSvcs, err := gp.fsCache.ListOld(&gp.env.Metadata, gp.idlePodReapTime)
if err != nil { if err != nil {
log.Printf("Error reaping idle pods: %v", err) log.Printf("Error reaping idle pods: %v", err)
continue continue
} }
for _, podName := range podNames { for _, obj := range funcSvcs {
log.Printf("Reaping idle pod '%v'", podName) err := gp.CleanupFunctionService(obj)
err := gp.CleanupFunctionService(podName)
if err != nil { if err != nil {
log.Printf("Error deleting idle pod '%v': %v", podName, err) log.Printf("Error deleting Kubernetes objects for fsvc '%v': %v", obj, err)
log.Printf("Object Name| Object Kind | Object Space")
for _, kubeobj := range obj.KubernetesObjects {
log.Printf("%v | %v | %v", kubeobj.Name, kubeobj.Kind, kubeobj.Namespace)
}
} }
} }
} }
@@ -638,7 +672,7 @@ func (gp *GenericPool) idlePodReaper() {
// destroys the pool -- the deployment, replicaset and pods // destroys the pool -- the deployment, replicaset and pods
func (gp *GenericPool) destroy() error { func (gp *GenericPool) destroy() error {
// Destroy deployment // Destroy deployment
err := gp.kubernetesClient.Extensions().Deployments(gp.namespace).Delete(gp.deployment.ObjectMeta.Name, nil) err := gp.kubernetesClient.ExtensionsV1beta1().Deployments(gp.namespace).Delete(gp.deployment.ObjectMeta.Name, nil)
if err != nil { if err != nil {
log.Printf("Error destroying deployment: %v", err) log.Printf("Error destroying deployment: %v", err)
return err return err
@@ -647,12 +681,12 @@ func (gp *GenericPool) destroy() error {
// Destroy ReplicaSet. Pre-1.6 K8s versions don't do this // Destroy ReplicaSet. Pre-1.6 K8s versions don't do this
// automatically but post-1.6 K8s will, and may beat us to it, // automatically but post-1.6 K8s will, and may beat us to it,
// so don't error out if we fail. // so don't error out if we fail.
rsList, err := gp.kubernetesClient.Extensions().ReplicaSets(gp.namespace).List(api.ListOptions{ rsList, err := gp.kubernetesClient.ExtensionsV1beta1().ReplicaSets(gp.namespace).List(metav1.ListOptions{
LabelSelector: labels.Set(gp.labelsForPool).AsSelector(), LabelSelector: labels.Set(gp.labelsForPool).AsSelector().String(),
}) })
if len(rsList.Items) >= 0 { if len(rsList.Items) >= 0 {
for _, rs := range rsList.Items { for _, rs := range rsList.Items {
err = gp.kubernetesClient.Extensions().ReplicaSets(gp.namespace).Delete(rs.ObjectMeta.Name, nil) err = gp.kubernetesClient.ExtensionsV1beta1().ReplicaSets(gp.namespace).Delete(rs.ObjectMeta.Name, nil)
if err != nil { if err != nil {
log.Printf("Error deleting replicaset, ignoring: %v", err) log.Printf("Error deleting replicaset, ignoring: %v", err)
} }
@@ -660,12 +694,12 @@ func (gp *GenericPool) destroy() error {
} }
// Destroy Pods. See note above. // Destroy Pods. See note above.
podList, err := gp.kubernetesClient.Core().Pods(gp.namespace).List(api.ListOptions{ podList, err := gp.kubernetesClient.CoreV1().Pods(gp.namespace).List(metav1.ListOptions{
LabelSelector: labels.Set(gp.labelsForPool).AsSelector(), LabelSelector: labels.Set(gp.labelsForPool).AsSelector().String(),
}) })
if len(podList.Items) >= 0 { if len(podList.Items) >= 0 {
for _, pod := range podList.Items { for _, pod := range podList.Items {
err = gp.kubernetesClient.Core().Pods(gp.namespace).Delete(pod.ObjectMeta.Name, nil) err = gp.kubernetesClient.CoreV1().Pods(gp.namespace).Delete(pod.ObjectMeta.Name, nil)
if err != nil { if err != nil {
log.Printf("Error deleting pod, ignoring: %v", err) log.Printf("Error deleting pod, ignoring: %v", err)
} }
@@ -674,32 +708,3 @@ func (gp *GenericPool) destroy() error {
return nil return nil
} }
// Calls the logging daemonset pod on the node where the given pod is
// running.
func (gp *GenericPool) setupLogging(pod *v1.Pod, metadata *api.ObjectMeta) {
logReq := logger.LogRequest{
Namespace: pod.Namespace,
Pod: pod.Name,
Container: gp.env.Metadata.Name,
FuncName: metadata.Name,
FuncUid: string(metadata.UID),
}
reqbody, err := json.Marshal(logReq)
if err != nil {
log.Printf("Error creating log request")
return
}
go func() {
loggerUrl := fmt.Sprintf("http://%s:1234/v1/log", pod.Status.HostIP)
resp, err := http.Post(loggerUrl, "application/json", bytes.NewReader(reqbody))
if err != nil {
log.Printf("Error connecting to %s log daemonset pod: %v", pod.Spec.NodeName, err)
} else {
if resp.StatusCode != 200 {
log.Printf("Error from %s log daemonset pod: %s", pod.Spec.NodeName, resp.Status)
}
resp.Body.Close()
}
}()
}
+19 -17
View File
@@ -20,11 +20,12 @@ import (
"log" "log"
"time" "time"
"k8s.io/client-go/1.5/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/1.5/pkg/api" "k8s.io/client-go/kubernetes"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
"github.com/fission/fission/executor/fscache"
) )
type requestType int type requestType int
@@ -39,15 +40,16 @@ type (
pools map[string]*GenericPool pools map[string]*GenericPool
kubernetesClient *kubernetes.Clientset kubernetesClient *kubernetes.Clientset
namespace string namespace string
fissionClient *tpr.FissionClient
fsCache *functionServiceCache fissionClient *crd.FissionClient
instanceId string fsCache *fscache.FunctionServiceCache
requestChannel chan *request instanceId string
requestChannel chan *request
} }
request struct { request struct {
requestType requestType
env *tpr.Environment env *crd.Environment
envList []tpr.Environment envList []crd.Environment
responseChannel chan *response responseChannel chan *response
} }
response struct { response struct {
@@ -57,11 +59,11 @@ type (
) )
func MakeGenericPoolManager( func MakeGenericPoolManager(
fissionClient *tpr.FissionClient, fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, kubernetesClient *kubernetes.Clientset,
fissionNamespace string, fissionNamespace string,
functionNamespace string, functionNamespace string,
fsCache *functionServiceCache, fsCache *fscache.FunctionServiceCache,
instanceId string) *GenericPoolManager { instanceId string) *GenericPoolManager {
gpm := &GenericPoolManager{ gpm := &GenericPoolManager{
@@ -85,7 +87,7 @@ func (gpm *GenericPoolManager) service() {
switch req.requestType { switch req.requestType {
case GET_POOL: case GET_POOL:
var err error var err error
pool, ok := gpm.pools[tpr.CacheKey(&req.env.Metadata)] pool, ok := gpm.pools[crd.CacheKey(&req.env.Metadata)]
if !ok { if !ok {
var poolSize int32 = 3 // TODO configurable/autoscalable var poolSize int32 = 3 // TODO configurable/autoscalable
switch req.env.Spec.AllowedFunctionsPerContainer { switch req.env.Spec.AllowedFunctionsPerContainer {
@@ -100,13 +102,13 @@ func (gpm *GenericPoolManager) service() {
req.responseChannel <- &response{error: err} req.responseChannel <- &response{error: err}
continue continue
} }
gpm.pools[tpr.CacheKey(&req.env.Metadata)] = pool gpm.pools[crd.CacheKey(&req.env.Metadata)] = pool
} }
req.responseChannel <- &response{pool: pool} req.responseChannel <- &response{pool: pool}
case CLEANUP_POOLS: case CLEANUP_POOLS:
latestEnvSet := make(map[string]bool) latestEnvSet := make(map[string]bool)
for _, env := range req.envList { for _, env := range req.envList {
latestEnvSet[tpr.CacheKey(&env.Metadata)] = true latestEnvSet[crd.CacheKey(&env.Metadata)] = true
} }
for key, pool := range gpm.pools { for key, pool := range gpm.pools {
_, ok := latestEnvSet[key] _, ok := latestEnvSet[key]
@@ -124,7 +126,7 @@ func (gpm *GenericPoolManager) service() {
} }
} }
func (gpm *GenericPoolManager) GetPool(env *tpr.Environment) (*GenericPool, error) { func (gpm *GenericPoolManager) GetPool(env *crd.Environment) (*GenericPool, error) {
c := make(chan *response) c := make(chan *response)
gpm.requestChannel <- &request{ gpm.requestChannel <- &request{
requestType: GET_POOL, requestType: GET_POOL,
@@ -135,7 +137,7 @@ func (gpm *GenericPoolManager) GetPool(env *tpr.Environment) (*GenericPool, erro
return resp.pool, resp.error return resp.pool, resp.error
} }
func (gpm *GenericPoolManager) CleanupPools(envs []tpr.Environment) { func (gpm *GenericPoolManager) CleanupPools(envs []crd.Environment) {
gpm.requestChannel <- &request{ gpm.requestChannel <- &request{
requestType: CLEANUP_POOLS, requestType: CLEANUP_POOLS,
envList: envs, envList: envs,
@@ -148,7 +150,7 @@ func (gpm *GenericPoolManager) eagerPoolCreator() {
time.Sleep(pollSleep) time.Sleep(pollSleep)
// get list of envs from controller // get list of envs from controller
envs, err := gpm.fissionClient.Environments(api.NamespaceAll).List(api.ListOptions{}) envs, err := gpm.fissionClient.Environments(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil { if err != nil {
log.Fatalf("Failed to get environment list: %v", err) log.Fatalf("Failed to get environment list: %v", err)
} }
+19 -30
View File
@@ -6,12 +6,12 @@ import (
"strconv" "strconv"
"github.com/docopt/docopt-go" "github.com/docopt/docopt-go"
"github.com/fission/fission/buildermgr" "github.com/fission/fission/buildermgr"
"github.com/fission/fission/controller" "github.com/fission/fission/controller"
"github.com/fission/fission/executor"
"github.com/fission/fission/kubewatcher" "github.com/fission/fission/kubewatcher"
"github.com/fission/fission/logger"
"github.com/fission/fission/mqtrigger" "github.com/fission/fission/mqtrigger"
"github.com/fission/fission/poolmgr"
"github.com/fission/fission/router" "github.com/fission/fission/router"
"github.com/fission/fission/storagesvc" "github.com/fission/fission/storagesvc"
"github.com/fission/fission/timer" "github.com/fission/fission/timer"
@@ -22,15 +22,15 @@ func runController(port int) {
log.Fatalf("Error: Controller exited.") log.Fatalf("Error: Controller exited.")
} }
func runRouter(port int, poolmgrUrl string) { func runRouter(port int, executorUrl string) {
router.Start(port, poolmgrUrl) router.Start(port, executorUrl)
log.Fatalf("Error: Router exited.") log.Fatalf("Error: Router exited.")
} }
func runPoolmgr(port int, fissionNamespace, functionNamespace string) { func runExecutor(port int, fissionNamespace, functionNamespace string) {
err := poolmgr.StartPoolmgr(fissionNamespace, functionNamespace, port) err := executor.StartExecutor(fissionNamespace, functionNamespace, port)
if err != nil { if err != nil {
log.Fatalf("Error starting poolmgr: %v", err) log.Fatalf("Error starting executor: %v", err)
} }
} }
@@ -41,11 +41,6 @@ func runKubeWatcher(routerUrl string) {
} }
} }
func runLogger() {
logger.Start()
log.Fatalf("Error: Logger exited.")
}
func runTimer(routerUrl string) { func runTimer(routerUrl string) {
err := timer.Start(routerUrl) err := timer.Start(routerUrl)
if err != nil { if err != nil {
@@ -94,18 +89,18 @@ func getStringArgWithDefault(arg interface{}, defaultValue string) string {
} }
func main() { func main() {
usage := `fission-bundle: Package of all fission microservices: controller, router, poolmgr. usage := `fission-bundle: Package of all fission microservices: controller, router, executor.
Use it to start one or more of the fission servers: Use it to start one or more of the fission servers:
Controller is a stateless API frontend for fission resources. Controller is a stateless API frontend for fission resources.
Pool manager maintains a pool of generalized function containers, and Pool manager maintains a pool of generalized function containers, and
specializes them on-demand. Poolmgr must be run from a pod in a specializes them on-demand. Executor must be run from a pod in a
Kubernetes cluster. Kubernetes cluster.
Router implements HTTP triggers: it routes to running instances, Router implements HTTP triggers: it routes to running instances,
working with the controller and poolmgr. working with the controller and executor.
Kubewatcher implements Kubernetes Watch triggers: it watches Kubewatcher implements Kubernetes Watch triggers: it watches
Kubernetes resources and invokes functions described in the Kubernetes resources and invokes functions described in the
@@ -117,28 +112,26 @@ Use it to start one or more of the fission servers:
Usage: Usage:
fission-bundle --controllerPort=<port> fission-bundle --controllerPort=<port>
fission-bundle --routerPort=<port> [--poolmgrUrl=<url>] fission-bundle --routerPort=<port> [--executorUrl=<url>]
fission-bundle --poolmgrPort=<port> [--namespace=<namespace>] [--fission-namespace=<namespace>] fission-bundle --executorPort=<port> [--namespace=<namespace>] [--fission-namespace=<namespace>]
fission-bundle --kubewatcher [--routerUrl=<url>] fission-bundle --kubewatcher [--routerUrl=<url>]
fission-bundle --storageServicePort=<port> --filePath=<filePath> fission-bundle --storageServicePort=<port> --filePath=<filePath>
fission-bundle --builderMgrPort=<port> [--storageSvcUrl=<url>] [--envbuilder-namespace=<namespace>] fission-bundle --builderMgrPort=<port> [--storageSvcUrl=<url>] [--envbuilder-namespace=<namespace>]
fission-bundle --logger
fission-bundle --timer [--routerUrl=<url>] fission-bundle --timer [--routerUrl=<url>]
fission-bundle --mqt [--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. --executorPort=<port> Port that the executor should listen on.
--storageServicePort=<port> Port that the storage service should listen on. --storageServicePort=<port> Port that the storage service should listen on.
--builderMgrPort=<port> Port that the buildermgr should listen on. --builderMgrPort=<port> Port that the buildermgr should listen on.
--poolmgrUrl=<url> Poolmgr URL. Not required if --poolmgrPort is specified. --executorUrl=<url> Executor URL. Not required if --executorPort is specified.
--routerUrl=<url> Router URL. --routerUrl=<url> Router URL.
--etcdUrl=<etcdUrl> Etcd URL. --etcdUrl=<etcdUrl> Etcd URL.
--storageSvcUrl=<url> StorageService URL. --storageSvcUrl=<url> StorageService 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.
--timer Start Timer. --timer Start Timer.
--mqt Start message queue trigger. --mqt Start message queue trigger.
` `
@@ -151,7 +144,7 @@ Options:
fissionNs := getStringArgWithDefault(arguments["--fission-namespace"], "fission") fissionNs := getStringArgWithDefault(arguments["--fission-namespace"], "fission")
envBuilderNs := getStringArgWithDefault(arguments["--envbuilder-namespace"], "fission-builder") envBuilderNs := getStringArgWithDefault(arguments["--envbuilder-namespace"], "fission-builder")
poolmgrUrl := getStringArgWithDefault(arguments["--poolmgrUrl"], "http://poolmgr.fission") executorUrl := getStringArgWithDefault(arguments["--executorUrl"], "http://executor.fission")
routerUrl := getStringArgWithDefault(arguments["--routerUrl"], "http://router.fission") routerUrl := getStringArgWithDefault(arguments["--routerUrl"], "http://router.fission")
storageSvcUrl := getStringArgWithDefault(arguments["--storageSvcUrl"], "http://storagesvc.fission") storageSvcUrl := getStringArgWithDefault(arguments["--storageSvcUrl"], "http://storagesvc.fission")
@@ -162,22 +155,18 @@ Options:
if arguments["--routerPort"] != nil { if arguments["--routerPort"] != nil {
port := getPort(arguments["--routerPort"]) port := getPort(arguments["--routerPort"])
runRouter(port, poolmgrUrl) runRouter(port, executorUrl)
} }
if arguments["--poolmgrPort"] != nil { if arguments["--executorPort"] != nil {
port := getPort(arguments["--poolmgrPort"]) port := getPort(arguments["--executorPort"])
runPoolmgr(port, fissionNs, functionNs) runExecutor(port, fissionNs, functionNs)
} }
if arguments["--kubewatcher"] == true { if arguments["--kubewatcher"] == true {
runKubeWatcher(routerUrl) runKubeWatcher(routerUrl)
} }
if arguments["--logger"] == true {
runLogger()
}
if arguments["--timer"] == true { if arguments["--timer"] == true {
runTimer(routerUrl) runTimer(routerUrl)
} }
+213
View File
@@ -17,11 +17,24 @@ limitations under the License.
package main package main
import ( import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt" "fmt"
"io"
"io/ioutil"
"net/http"
"os" "os"
"path/filepath"
"strings" "strings"
uuid "github.com/satori/go.uuid"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission"
"github.com/fission/fission/controller/client" "github.com/fission/fission/controller/client"
"github.com/fission/fission/crd"
storageSvcClient "github.com/fission/fission/storagesvc/client"
) )
func fatal(msg string) { func fatal(msg string) {
@@ -29,6 +42,10 @@ func fatal(msg string) {
os.Exit(1) os.Exit(1)
} }
func warn(msg string) {
os.Stderr.WriteString(msg + "\n")
}
func getClient(serverUrl string) *client.Client { func getClient(serverUrl string) *client.Client {
if len(serverUrl) == 0 { if len(serverUrl) == 0 {
@@ -50,3 +67,199 @@ func checkErr(err error, msg string) {
fatal(fmt.Sprintf("Failed to %v: %v", msg, err)) fatal(fmt.Sprintf("Failed to %v: %v", msg, err))
} }
} }
func httpRequest(method, url, body string, headers []string) *http.Response {
if method == "" {
method = "GET"
}
if method != http.MethodGet &&
method != http.MethodDelete &&
method != http.MethodPost &&
method != http.MethodPut {
fatal(fmt.Sprintf("Invalid HTTP method '%s'.", method))
}
req, err := http.NewRequest(method, url, strings.NewReader(body))
checkErr(err, "create HTTP request")
for _, header := range headers {
headerKeyValue := strings.SplitN(header, ":", 2)
if len(headerKeyValue) != 2 {
checkErr(errors.New(""), "create request without appropriate headers")
}
req.Header.Set(headerKeyValue[0], headerKeyValue[1])
}
client := &http.Client{}
resp, err := client.Do(req)
checkErr(err, "execute HTTP request")
return resp
}
func fileSize(filePath string) int64 {
info, err := os.Stat(filePath)
checkErr(err, fmt.Sprintf("stat %v", filePath))
return info.Size()
}
// upload a file and return a fission.Archive
func createArchive(client *client.Client, fileName string) *fission.Archive {
var archive fission.Archive
// fetch archive from arbitrary url if fileName is a url
if strings.HasPrefix(fileName, "http://") || strings.HasPrefix(fileName, "https://") {
fileName = downloadToTempFile(fileName)
}
if fileSize(fileName) < fission.ArchiveLiteralSizeLimit {
contents := getContents(fileName)
archive.Type = fission.ArchiveTypeLiteral
archive.Literal = contents
} else {
u := strings.TrimSuffix(client.Url, "/") + "/proxy/storage"
ssClient := storageSvcClient.MakeClient(u)
// TODO add a progress bar
id, err := ssClient.Upload(fileName, nil)
checkErr(err, fmt.Sprintf("upload file %v", fileName))
archiveUrl := ssClient.GetUrl(id)
archive.Type = fission.ArchiveTypeUrl
archive.URL = archiveUrl
f, err := os.Open(fileName)
if err != nil {
checkErr(err, fmt.Sprintf("find file %v", fileName))
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
checkErr(err, fmt.Sprintf("calculate checksum for file %v", fileName))
}
archive.Checksum = fission.Checksum{
Type: fission.ChecksumTypeSHA256,
Sum: hex.EncodeToString(h.Sum(nil)),
}
}
return &archive
}
func createPackage(client *client.Client, envName, srcArchiveName, deployArchiveName, buildcmd string) *metav1.ObjectMeta {
pkgSpec := fission.PackageSpec{
Environment: fission.EnvironmentReference{
Namespace: metav1.NamespaceDefault,
Name: envName,
},
}
var pkgStatus fission.BuildStatus = fission.BuildStatusSucceeded
if len(deployArchiveName) > 0 {
pkgSpec.Deployment = *createArchive(client, deployArchiveName)
}
if len(srcArchiveName) > 0 {
pkgSpec.Source = *createArchive(client, srcArchiveName)
// set pending status to package
pkgStatus = fission.BuildStatusPending
}
if len(buildcmd) > 0 {
pkgSpec.BuildCommand = buildcmd
}
pkgName := strings.ToLower(uuid.NewV4().String())
pkg := &crd.Package{
Metadata: metav1.ObjectMeta{
Name: pkgName,
Namespace: metav1.NamespaceDefault,
},
Spec: pkgSpec,
Status: fission.PackageStatus{
BuildStatus: pkgStatus,
},
}
pkgMetadata, err := client.PackageCreate(pkg)
checkErr(err, "create package")
return pkgMetadata
}
func getContents(filePath string) []byte {
var code []byte
var err error
code, err = ioutil.ReadFile(filePath)
checkErr(err, fmt.Sprintf("read %v", filePath))
return code
}
func getTempDir() (string, error) {
tmpDir := uuid.NewV4().String()
tmpPath := filepath.Join(os.TempDir(), tmpDir)
err := os.Mkdir(tmpPath, 0744)
return tmpPath, err
}
func writeArchiveToFile(fileName string, reader io.Reader) error {
tmpDir, err := getTempDir()
if err != nil {
return err
}
path := filepath.Join(tmpDir, fileName+".tmp")
w, err := os.Create(path)
if err != nil {
return err
}
_, err = io.Copy(w, reader)
if err != nil {
return err
}
err = os.Chmod(path, 0644)
if err != nil {
return err
}
err = os.Rename(path, fileName)
if err != nil {
return err
}
return nil
}
// downloadToTempFile fetches archive file from arbitrary url
// and write it to temp file for further usage
func downloadToTempFile(fileUrl string) string {
reader, err := downloadURL(fileUrl)
defer reader.Close()
checkErr(err, fmt.Sprintf("download from url: %v", fileUrl))
tmpDir, err := getTempDir()
checkErr(err, "create temp directory")
tmpFilename := uuid.NewV4().String()
destination := filepath.Join(tmpDir, tmpFilename)
err = os.Mkdir(tmpDir, 0744)
checkErr(err, "create temp directory")
err = writeArchiveToFile(destination, reader)
checkErr(err, "write archive to file")
return destination
}
// downloadURL downloads file from given url
func downloadURL(fileUrl string) (io.ReadCloser, error) {
resp, err := http.Get(fileUrl)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%v - HTTP response returned non 200 status", resp.StatusCode)
}
return resp.Body, nil
}
+12 -13
View File
@@ -22,10 +22,10 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/urfave/cli" "github.com/urfave/cli"
"k8s.io/client-go/1.5/pkg/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission" "github.com/fission/fission"
"github.com/fission/fission/tpr" "github.com/fission/fission/crd"
) )
func envCreate(c *cli.Context) error { func envCreate(c *cli.Context) error {
@@ -55,14 +55,13 @@ func envCreate(c *cli.Context) error {
// Environment API interface version is not specified and // Environment API interface version is not specified and
// builder image is empty, set default interface version // builder image is empty, set default interface version
if envVersion == 0 { if envVersion == 0 {
fmt.Println("Use default environment v1 API interface")
envVersion = 1 envVersion = 1
} }
env := &tpr.Environment{ env := &crd.Environment{
Metadata: api.ObjectMeta{ Metadata: metav1.ObjectMeta{
Name: envName, Name: envName,
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}, },
Spec: fission.EnvironmentSpec{ Spec: fission.EnvironmentSpec{
Version: envVersion, Version: envVersion,
@@ -91,9 +90,9 @@ func envGet(c *cli.Context) error {
fatal("Need a name, use --name.") fatal("Need a name, use --name.")
} }
m := &api.ObjectMeta{ m := &metav1.ObjectMeta{
Name: envName, Name: envName,
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
} }
env, err := client.EnvironmentGet(m) env, err := client.EnvironmentGet(m)
checkErr(err, "get environment") checkErr(err, "get environment")
@@ -121,9 +120,9 @@ func envUpdate(c *cli.Context) error {
fatal("Need --image to specify env image, or use --builder to specify env builder, or use --buildcmd to specify new build command.") fatal("Need --image to specify env image, or use --builder to specify env builder, or use --buildcmd to specify new build command.")
} }
env, err := client.EnvironmentGet(&api.ObjectMeta{ env, err := client.EnvironmentGet(&metav1.ObjectMeta{
Name: envName, Name: envName,
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
}) })
checkErr(err, "find environment") checkErr(err, "find environment")
@@ -132,7 +131,7 @@ func envUpdate(c *cli.Context) error {
} }
if env.Spec.Version == 1 && (len(envBuilderImg) > 0 || len(envBuildCmd) > 0) { if env.Spec.Version == 1 && (len(envBuilderImg) > 0 || len(envBuildCmd) > 0) {
fatal("Environment v1 API interface doesn't supported environment builder.") fatal("Version 1 Environments do not support builders. Must specify --version=2.")
} }
if len(envBuilderImg) > 0 { if len(envBuilderImg) > 0 {
@@ -157,9 +156,9 @@ func envDelete(c *cli.Context) error {
fatal("Need a name , use --name.") fatal("Need a name , use --name.")
} }
m := &api.ObjectMeta{ m := &metav1.ObjectMeta{
Name: envName, Name: envName,
Namespace: api.NamespaceDefault, Namespace: metav1.NamespaceDefault,
} }
err := client.EnvironmentDelete(m) err := client.EnvironmentDelete(m)
checkErr(err, "delete environment") checkErr(err, "delete environment")

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