Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95eef49cf8 | ||
|
|
2d01382a82 | ||
|
|
d443f8b2ef | ||
|
|
dffb5cdf8b | ||
|
|
5c1d8bc90c | ||
|
|
ad28922871 | ||
|
|
9d7355cbaa | ||
|
|
74a3a54543 | ||
|
|
07ca5df8d0 | ||
|
|
e730376e3e | ||
|
|
ee61c8c333 | ||
|
|
27b74a7cf2 | ||
|
|
088e665d28 | ||
|
|
9438f3c4ec | ||
|
|
b5a906b2f4 | ||
|
|
0e40cb5f4f | ||
|
|
1a1ac49ce0 | ||
|
|
4356378301 | ||
|
|
6123873d2a | ||
|
|
9a7ffc2f5b | ||
|
|
f3857abdf5 | ||
|
|
e977c7caca | ||
|
|
f40688435d | ||
|
|
e8b2a26a3b | ||
|
|
27c3a50d23 | ||
|
|
ec495fbd1d | ||
|
|
80fadcc7b8 | ||
|
|
4b37b9ac03 | ||
|
|
16d0f6a94f | ||
|
|
cc40d1ed2e | ||
|
|
425e447e5c | ||
|
|
3707b95edb | ||
|
|
2982efebb8 | ||
|
|
14db705891 | ||
|
|
9c885ef6f5 | ||
|
|
34e8d0ee3d | ||
|
|
728979636f | ||
|
|
462db2f00c | ||
|
|
2504408803 | ||
|
|
61569d1c38 | ||
|
|
5b6582698c | ||
|
|
81f0f1df6d | ||
|
|
44be0f6859 | ||
|
|
9daf97a9ab | ||
|
|
032d1a8b9a | ||
|
|
65fd1d9fbb | ||
|
|
a35cd7f4a4 | ||
|
|
74212f1810 | ||
|
|
1ed59f0789 | ||
|
|
71d8b769d5 | ||
|
|
c863616b38 | ||
|
|
09b62e514e | ||
|
|
c705ba355e | ||
|
|
7642dcf05f | ||
|
|
58a5f1cb0b | ||
|
|
1b86c367a0 | ||
|
|
6d08e6e0bc | ||
|
|
ef7d4ae385 | ||
|
|
cf1f944069 | ||
|
|
ddff7100e9 | ||
|
|
a5bc2a6d4c | ||
|
|
952b48bd68 | ||
|
|
2ad07d68b6 | ||
|
|
8c96d9c6a0 | ||
|
|
46d9808173 | ||
|
|
2e37611da8 | ||
|
|
0057270551 | ||
|
|
7a7d15b50c | ||
|
|
202cc3cb7e | ||
|
|
a761393816 | ||
|
|
fedf6b6e5b | ||
|
|
6eec55223d | ||
|
|
940b9adabd | ||
|
|
8984e4916e | ||
|
|
c9d2757760 | ||
|
|
d846aed612 | ||
|
|
d9b5b9b8a8 | ||
|
|
9c91f00cb4 | ||
|
|
0a648d1d75 |
@@ -1,5 +1,6 @@
|
|||||||
fission-bundle/fission-bundle
|
fission-bundle/fission-bundle
|
||||||
fission/fission
|
fission/fission
|
||||||
|
environments/fetcher/cmd/fetcher
|
||||||
|
|
||||||
# Pycharm IDE
|
# Pycharm IDE
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
+9
-4
@@ -9,7 +9,7 @@ branches:
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.8
|
- 1.10.2
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
@@ -31,13 +31,18 @@ install:
|
|||||||
env:
|
env:
|
||||||
- KUBECONFIG=${HOME}/.kube/config PATH=$HOME/k8scli:${PATH}
|
- KUBECONFIG=${HOME}/.kube/config PATH=$HOME/k8scli:${PATH}
|
||||||
|
|
||||||
script:
|
before_script:
|
||||||
- cd ${TRAVIS_BUILD_DIR}
|
- cd ${TRAVIS_BUILD_DIR}
|
||||||
- glide install -v
|
|
||||||
- ./fission-bundle/build.sh
|
|
||||||
- hack/verify-gofmt.sh
|
- hack/verify-gofmt.sh
|
||||||
- hack/verify-govet.sh
|
- hack/verify-govet.sh
|
||||||
|
- helm lint charts/fission-all/ charts/fission-core/
|
||||||
|
- glide install -v
|
||||||
|
- go build -o fission/fission fission/*.go
|
||||||
|
- ./fission-bundle/build.sh
|
||||||
- hack/runtests.sh
|
- hack/runtests.sh
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cd ${TRAVIS_BUILD_DIR}
|
||||||
- test/build_and_test.sh
|
- test/build_and_test.sh
|
||||||
- test/upgrade/fission_upgrade_test.sh
|
- test/upgrade/fission_upgrade_test.sh
|
||||||
|
|
||||||
|
|||||||
+111
-9
@@ -1,18 +1,120 @@
|
|||||||
# 0.7.1
|
# 0.9.2
|
||||||
[Documentation](http://fission.io/docs/0.7.1/)
|
[Documentation](https://docs.fission.io/0.9.2/)
|
||||||
## Downloads for 0.7.1
|
## Downloads for 0.9.2
|
||||||
|
|
||||||
|
|
||||||
filename | sha256 hash
|
filename | sha256 hash
|
||||||
-------- | -----------
|
-------- | -----------
|
||||||
[fission-core-0.7.1.tgz](https://github.com/fission/fission/releases/download/0.7.1/fission-core-0.7.1.tgz) | `5d5606c79b5d83c2e88c371b6bea4a49a599f1a1e26674db3388a63278a47c2c`
|
|
||||||
[fission-all-0.7.1.tgz](https://github.com/fission/fission/releases/download/0.7.1/fission-all-0.7.1.tgz) | `0ea6887f39c0f59463b49e632294152c926b62b5afc65202c80045ff71735b05`
|
|
||||||
[fission-cli-osx](https://github.com/fission/fission/releases/download/0.7.1/fission-cli-osx) | `765b35bd1e4565b9819bed157ca0206a0e5217394d0b253a0cdd9a318ef4d9a5`
|
|
||||||
[fission-cli-linux](https://github.com/fission/fission/releases/download/0.7.1/fission-cli-linux) | `5eacdff0a0f3d6d3a1c2aa8d6164bc6524b1f001298cb2e82b60cf1c762a5a9e`
|
|
||||||
[fission-cli-windows.exe](https://github.com/fission/fission/releases/download/0.7.1/fission-cli-windows.exe) | `c237b1ee1a8f2b26038555388a438e4fff5725c90d4128baaa32a77ccf990d2e`
|
|
||||||
|
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [0.9.2](https://github.com/fission/fission/tree/0.9.2) (2018-07-25)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/0.9.1...0.9.2)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Helm lint check in Travis CI [\#799](https://github.com/fission/fission/pull/799) ([erwinvaneyk](https://github.com/erwinvaneyk))
|
||||||
|
- Spelling. [\#797](https://github.com/fission/fission/pull/797) ([WrathZA](https://github.com/WrathZA))
|
||||||
|
- change image pull policy of builder manager [\#793](https://github.com/fission/fission/pull/793) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- Delete namespace in background to reduce build time [\#791](https://github.com/fission/fission/pull/791) ([life1347](https://github.com/life1347))
|
||||||
|
- Break & Stop the build immediately if a non-zero exit code was returned [\#790](https://github.com/fission/fission/pull/790) ([life1347](https://github.com/life1347))
|
||||||
|
- Add changelog. [\#789](https://github.com/fission/fission/pull/789) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- changes needed for 0.9.1 [\#788](https://github.com/fission/fission/pull/788) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Working version of Java builder with Maven [\#783](https://github.com/fission/fission/pull/783) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Add query options to `fission function test` [\#782](https://github.com/fission/fission/pull/782) ([erwinvaneyk](https://github.com/erwinvaneyk))
|
||||||
|
- Add go environment vendor directory support [\#781](https://github.com/fission/fission/pull/781) ([life1347](https://github.com/life1347))
|
||||||
|
- Added readme for JVM environment [\#768](https://github.com/fission/fission/pull/768) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Fix spec command overrides existing archive's url of a package [\#764](https://github.com/fission/fission/pull/764) ([life1347](https://github.com/life1347))
|
||||||
|
- Fixed typos from from goreportcard [\#760](https://github.com/fission/fission/pull/760) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Updating releasing notes with details and structure [\#738](https://github.com/fission/fission/pull/738) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Allow router round-trip to be configurable [\#713](https://github.com/fission/fission/pull/713) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
|
||||||
|
## [0.9.1](https://github.com/fission/fission/tree/0.9.1) (2018-07-07)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/0.9.0...0.9.1)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Committing changelog. [\#780](https://github.com/fission/fission/pull/780) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Changes in charts for release 0.9.0 [\#778](https://github.com/fission/fission/pull/778) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Change flag name to KeepArchive for backward compatibility [\#787](https://github.com/fission/fission/pull/787) ([life1347](https://github.com/life1347))
|
||||||
|
- Fix go env plugin [\#784](https://github.com/fission/fission/pull/784) ([life1347](https://github.com/life1347))
|
||||||
|
- Fix “rm: missing operand” in release script [\#779](https://github.com/fission/fission/pull/779) ([life1347](https://github.com/life1347))
|
||||||
|
|
||||||
|
## [0.9.0](https://github.com/fission/fission/tree/0.9.0) (2018-07-04)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/0.8.0...0.9.0)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Fix executor wrongly passes loop variable reference to function [\#751](https://github.com/fission/fission/pull/751) ([life1347](https://github.com/life1347))
|
||||||
|
- Python Environment: add gevent based WSGI server framework [\#750](https://github.com/fission/fission/pull/750) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- Temporarily disabling the tests so that other PRs can be worked on [\#737](https://github.com/fission/fission/pull/737) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- add build exe to gitignore [\#736](https://github.com/fission/fission/pull/736) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- ArchiveLiteralSizeLimit: Use Constant Instead Hard Code [\#731](https://github.com/fission/fission/pull/731) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- Environment warning message bugfix [\#725](https://github.com/fission/fission/pull/725) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- V0.8.0 [\#722](https://github.com/fission/fission/pull/722) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Make fetcher resource requests and limits configurable [\#708](https://github.com/fission/fission/pull/708) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- Add steps to render & upload fission installation YAML [\#745](https://github.com/fission/fission/pull/745) ([life1347](https://github.com/life1347))
|
||||||
|
- Fix executor not reaps idle function pods for functions with executortype newdeploy [\#744](https://github.com/fission/fission/pull/744) ([life1347](https://github.com/life1347))
|
||||||
|
- Testing with keep alive settings for connections [\#742](https://github.com/fission/fission/pull/742) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- instead hard code by variable in error message [\#735](https://github.com/fission/fission/pull/735) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- envns should be availabe in message line [\#734](https://github.com/fission/fission/pull/734) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- Support annotations in environment specs [\#733](https://github.com/fission/fission/pull/733) ([erwinvaneyk](https://github.com/erwinvaneyk))
|
||||||
|
- Extract portforward to separate package [\#728](https://github.com/fission/fission/pull/728) ([erwinvaneyk](https://github.com/erwinvaneyk))
|
||||||
|
- Push NATS error messages to error queue [\#724](https://github.com/fission/fission/pull/724) ([Amusement](https://github.com/Amusement))
|
||||||
|
- Fix for Windows CLI Port Forwarding [\#715](https://github.com/fission/fission/pull/715) ([thejosephstevens](https://github.com/thejosephstevens))
|
||||||
|
- Router liveness [\#701](https://github.com/fission/fission/pull/701) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Archives bigger than 256K size need env variable for uploading [\#697](https://github.com/fission/fission/pull/697) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Convert go-env Dockerfile into a multi-stage build [\#683](https://github.com/fission/fission/pull/683) ([jgall](https://github.com/jgall))
|
||||||
|
- Move build process from host to docker container during release process [\#682](https://github.com/fission/fission/pull/682) ([life1347](https://github.com/life1347))
|
||||||
|
- Added a flag to control the extraction of archive based on user input [\#675](https://github.com/fission/fission/pull/675) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Java env alpha [\#656](https://github.com/fission/fission/pull/656) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
|
||||||
|
## [0.8.0](https://github.com/fission/fission/tree/0.8.0) (2018-06-05)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/0.7.2...0.8.0)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Pre-install/pre-upgrade hooks to verify func references and assign restricted role bindings [\#717](https://github.com/fission/fission/pull/717) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Logger daemonset's update strategy [\#714](https://github.com/fission/fission/pull/714) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Check spec directory exists before reading spec files [\#709](https://github.com/fission/fission/pull/709) ([life1347](https://github.com/life1347))
|
||||||
|
- Formatted specifiers are not compatible with variables [\#706](https://github.com/fission/fission/pull/706) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- Indicate HTTP status code by library const [\#703](https://github.com/fission/fission/pull/703) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- docker-distribution version bump for windows compatibility [\#691](https://github.com/fission/fission/pull/691) ([thejosephstevens](https://github.com/thejosephstevens))
|
||||||
|
- Version -\> 0.7.2 [\#670](https://github.com/fission/fission/pull/670) ([life1347](https://github.com/life1347))
|
||||||
|
- Java environment Design & considerations [\#642](https://github.com/fission/fission/pull/642) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Working version of Ingress integration [\#688](https://github.com/fission/fission/pull/688) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Update k8s dependencies to 1.10 [\#687](https://github.com/fission/fission/pull/687) ([life1347](https://github.com/life1347))
|
||||||
|
- Add time trigger cron spec examination tool [\#680](https://github.com/fission/fission/pull/680) ([life1347](https://github.com/life1347))
|
||||||
|
- Fission metrics integration [\#677](https://github.com/fission/fission/pull/677) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Replace Werkzeug with Bjoern as underlying WSGI server [\#672](https://github.com/fission/fission/pull/672) ([life1347](https://github.com/life1347))
|
||||||
|
- Enabling multi-tenancy for fission objects. [\#655](https://github.com/fission/fission/pull/655) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
|
||||||
|
## [0.7.2](https://github.com/fission/fission/tree/0.7.2) (2018-05-05)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/0.7.1...0.7.2)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Add benchmark script [\#666](https://github.com/fission/fission/pull/666) ([life1347](https://github.com/life1347))
|
||||||
|
- Fixed the issue with update wiping values [\#663](https://github.com/fission/fission/pull/663) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Fix newdeploy backend failed to delete deployment due to incorrect resource version [\#657](https://github.com/fission/fission/pull/657) ([life1347](https://github.com/life1347))
|
||||||
|
- Function update should be possible without change to code [\#652](https://github.com/fission/fission/pull/652) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Fixes the issue with fn test and adds relevant test cases, fixes \#650 [\#651](https://github.com/fission/fission/pull/651) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Fix test cases occasional failure [\#647](https://github.com/fission/fission/pull/647) ([life1347](https://github.com/life1347))
|
||||||
|
- Change time precision for fluentd influxdb plugin to nano second [\#646](https://github.com/fission/fission/pull/646) ([life1347](https://github.com/life1347))
|
||||||
|
- Setting buildStatus to pending when function's source archive is updated. [\#637](https://github.com/fission/fission/pull/637) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Fix SEGFAULT issue when buildmgr failed to update package [\#635](https://github.com/fission/fission/pull/635) ([life1347](https://github.com/life1347))
|
||||||
|
- Fix executor does not reap specialized function pod when env no longer exists [\#633](https://github.com/fission/fission/pull/633) ([life1347](https://github.com/life1347))
|
||||||
|
- Update readme to point to the proper link [\#628](https://github.com/fission/fission/pull/628) ([jgall](https://github.com/jgall))
|
||||||
|
- Changes needed for release 0.7.1 [\#622](https://github.com/fission/fission/pull/622) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Add default value to cli flag [\#619](https://github.com/fission/fission/pull/619) ([life1347](https://github.com/life1347))
|
||||||
|
- Remove port forward in tests for router, controller and nats pods [\#611](https://github.com/fission/fission/pull/611) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- meaningful error message when fetch request is received for a package when build is not successful. [\#661](https://github.com/fission/fission/pull/661) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Delete deployment with proper delete propagation policy [\#630](https://github.com/fission/fission/pull/630) ([life1347](https://github.com/life1347))
|
||||||
|
- Fix buildmgr SEGFAULT when it failed to update package [\#626](https://github.com/fission/fission/pull/626) ([life1347](https://github.com/life1347))
|
||||||
|
- Fission upgrade tests [\#605](https://github.com/fission/fission/pull/605) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Removed the fn pods functionality [\#594](https://github.com/fission/fission/pull/594) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Testing proposal: Requirements and frameworks exploration [\#581](https://github.com/fission/fission/pull/581) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
|
||||||
## [0.7.1](https://github.com/fission/fission/tree/0.7.1) (2018-04-10)
|
## [0.7.1](https://github.com/fission/fission/tree/0.7.1) (2018-04-10)
|
||||||
[Full Changelog](https://github.com/fission/fission/compare/0.7.0...0.7.1)
|
[Full Changelog](https://github.com/fission/fission/compare/0.7.0...0.7.1)
|
||||||
|
|
||||||
@@ -54,7 +156,7 @@ filename | sha256 hash
|
|||||||
|
|
||||||
**Merged pull requests:**
|
**Merged pull requests:**
|
||||||
|
|
||||||
- This change fixes an error in a yaml file in the fission-core chart. [\#563](https://github.com/fission/fission/pull/563) ([yiqunding](https://github.com/yiqunding))
|
- This change fixes an error in a yaml file in the fission-core chart. [\#563](https://github.com/fission/fission/pull/563) ([smartding](https://github.com/smartding))
|
||||||
- \[ci skip\] update release number [\#561](https://github.com/fission/fission/pull/561) ([appleboy](https://github.com/appleboy))
|
- \[ci skip\] update release number [\#561](https://github.com/fission/fission/pull/561) ([appleboy](https://github.com/appleboy))
|
||||||
- Fixes \#537 - warning should not be given when updating to newdeploy [\#545](https://github.com/fission/fission/pull/545) ([vishal-biyani](https://github.com/vishal-biyani))
|
- Fixes \#537 - warning should not be given when updating to newdeploy [\#545](https://github.com/fission/fission/pull/545) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
- Docs update [\#542](https://github.com/fission/fission/pull/542) ([soamvasani](https://github.com/soamvasani))
|
- Docs update [\#542](https://github.com/fission/fission/pull/542) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Multi-tenancy in Fission
|
||||||
|
|
||||||
|
Multi-tenancy in Fission allows users to create Fission objects, i.e functions, packages, environments and triggers in different namespaces.
|
||||||
|
It mandates that a function reference secrets, configmaps and its package (if explicity referenced during function creation/updation) to be present in the same namespace as the function.
|
||||||
|
This allows user separation and prevents in-advertent access to sensitive data of other users sharing the same cluster.
|
||||||
|
However, users are allowed and encouraged to share environments to ensure optimal utilization of cluster resources. To achieve this, users can create all the necessary environments in a ns, say ns1 and then go on to create functions in different namespaces and refer to env in ns1.
|
||||||
|
Users that prefer complete isolation can create their env, functions in the same ns.
|
||||||
|
|
||||||
|
## Roles and privileges
|
||||||
|
|
||||||
|
1. Cluster-Admin Role : Fission's services need cluster-admin privileges to monitor, create, update and delete resources across namespaces.
|
||||||
|
2. Package-getter Role : This role has privileges to do a get, watch and list on fission package objects.
|
||||||
|
3. Secret-Configmap-getter Role : This role has privileges to do a get, watch and list on secrets and configmaps.
|
||||||
|
|
||||||
|
## Service Accounts
|
||||||
|
|
||||||
|
1. fission-fetcher
|
||||||
|
|
||||||
|
This SA is created in every namespace that a user creates runtime environments in.
|
||||||
|
Also created in function namespaces where a user creates functions that use NewDeploy executor backend.
|
||||||
|
|
||||||
|
2. fission-builder
|
||||||
|
|
||||||
|
This SA is created in every namespace that a user creates builder environments in.
|
||||||
|
|
||||||
|
## Role-bindings
|
||||||
|
|
||||||
|
1. Package-getter-binding
|
||||||
|
|
||||||
|
Every time a user creates a package explicity in a namespace, this rolebinding is created in package's namespace (which is also function's namespace). This grants package-getter role to fission-fetcher SA present in the referenced environment's namespace.
|
||||||
|
If the package is a source package, then, fission-builder SA present in the environment namespace is also added to this rolebinding.
|
||||||
|
|
||||||
|
Next when the user creates a function in the same namespace, if the function's executor type is newdeploy, then, the fission-fetcher SA present in function namespace is also added to the same rolebinding.
|
||||||
|
|
||||||
|
Note : For functions that have executor type poolmgr, the env pods are created in the namespace that env object is created. Whereas, for those functions that have executor type New deploy mgr, the function pods are created in the namespace that function object is created in.
|
||||||
|
|
||||||
|
This is because, poolmgr allows env sharing and optimal resource utilization. so generic env pools are created in a different namespace and all functions that prefer sharing this env pool can reference these pools.
|
||||||
|
If users require strict isolation, they can either create functions with new deploy backend, or, create env's in different namespaces and not share them across functions.
|
||||||
|
|
||||||
|
2. Secret-Configmap-getter-binding
|
||||||
|
|
||||||
|
Every time a user creates a function in a namespace, Secret-Configmap-getter-binding is created in the same namespace, granting secret-configmap-getter role to fission-fetcher SA present in the referenced environment's namespace in case the executor type is poolmgr.
|
||||||
|
If the executor type is newdeploymgr, then the same rolebinding is created in the same namespace as the function, granting the same secret-configmap-getter role to fission-fetcher SA present in the function namespace.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
1. create a generic python runtime env in ns 1 and function with poolmgr executor type in ns 2 that references it.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ fission env create --name python --image fission/python-env --envns ns1
|
||||||
|
$ fission function create --name func1 --env python --code hello.py --fns ns2
|
||||||
|
```
|
||||||
|
|
||||||
|
2. create a builder and runtime environment in ns3, a source pkg in ns3 and a function referring to this src pkg also in ns3. (for complete isolation, all objs are in ns3)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ fission env create --name python-builder-env --builder fission/python-builder --image fission/python-env --ns3
|
||||||
|
$ fission package create --src src-pkg.zip --env python-builder-env --buildcmd "./build.sh" --pkgns ns3
|
||||||
|
$ fission fn create --name func3 --fns ns3 --pkg $pkg --entrypoint "user.main"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Note
|
||||||
|
|
||||||
|
1. To maintain backward compatibility, fission objects that are created without the ns flags are created in default namespace. Also, the run time env pods in such a case will continue to live in fission-function ns and builder env pods in fission-builder ns
|
||||||
|
2. Since all envs in a namespace have the same fission-fetcher SA mounted in them, even though multiple envs are created in a namespace and referenced by functions in different namespaces, the SA will have previleges to view those function's secrets if any.
|
||||||
|
3. Similarly, if there are multiple functions in different namespaces but all sharing an env in one namespace, the fission-fetcher SA in that namespace will have previleges to see all of their secrets.
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
# Fission CLI Extensibility
|
||||||
|
|
||||||
|
### Approach
|
||||||
|
To sum up the approach: git-style plugins.
|
||||||
|
|
||||||
|
Plugins are named with the fission prefix `fission-*`. When fission is invoked with an undefined/non-core subcommand
|
||||||
|
is called (like `fission foo`). Fission will look in the PATH
|
||||||
|
|
||||||
|
**Installing Fission**
|
||||||
|
```bash
|
||||||
|
# The same as before
|
||||||
|
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.7.2/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Installing Fission Workflows**
|
||||||
|
```bash
|
||||||
|
# The same process as Fission cli itself
|
||||||
|
$ curl -Lo fission https://github.com/fission/fission-workflows/releases/download/0.4.0/fission-workflows-osx && chmod +x fission-workflows && sudo mv fission-workflows /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Invoking Fission Workflows**
|
||||||
|
```bash
|
||||||
|
$ fission workflows invocation get b1278e802a
|
||||||
|
# Which is equivalent to:
|
||||||
|
$ fission-workflows invocation get b1278e802a
|
||||||
|
```
|
||||||
|
General flow:
|
||||||
|
1. fission does not recognize `workflows` subcommand
|
||||||
|
2. fission checks the path for a binary called `fission-workflows`
|
||||||
|
3. fission finds the binary.
|
||||||
|
4. fission invokes the `fission-workflows`, passing the remainder of the arguments.
|
||||||
|
|
||||||
|
**Discoverability: fission --help**
|
||||||
|
```bash
|
||||||
|
$ fission --help
|
||||||
|
USAGE:
|
||||||
|
fission [global options] command [command options] [arguments...]
|
||||||
|
|
||||||
|
VERSION:
|
||||||
|
0.6.0
|
||||||
|
|
||||||
|
COMMANDS:
|
||||||
|
function, fn Create, update and manage functions
|
||||||
|
httptrigger, ht, route Manage HTTP triggers (routes) for functions
|
||||||
|
timetrigger, tt, timer Manage Time triggers (timers) for functions
|
||||||
|
mqtrigger, mqt, messagequeue Manage message queue triggers for functions
|
||||||
|
environment, env Manage environments
|
||||||
|
watch, w Manage watches
|
||||||
|
package, pkg Manage packages
|
||||||
|
spec, specs Manage a declarative app specification
|
||||||
|
upgrade Upgrade tool from fission v0.1
|
||||||
|
tpr2crd Migrate tool for TPR to CRD
|
||||||
|
help, h Shows a list of commands or help for one command
|
||||||
|
|
||||||
|
PLUGINS:
|
||||||
|
workflows, wf Inspect and manage workflow executions
|
||||||
|
ui Start the user interface
|
||||||
|
|
||||||
|
GLOBAL OPTIONS:
|
||||||
|
--server value Fission server URL (default: "http://127.0.0.1:65356")
|
||||||
|
--help, -h show help
|
||||||
|
--version, -v print the version
|
||||||
|
```
|
||||||
|
Of course Fission needs to be able to find all plugins for this. There are several ways in which we can provide discoverability. The simplest one is for Fission to look in the path for all binaries starting with the `fission-*` prefix. Optionally, fission could invoke a specific command on the subcommand to get info about the plugin (such as version, help text, aliases...)
|
||||||
|
|
||||||
|
With Fission Workflows this info would look something like this:
|
||||||
|
```bash
|
||||||
|
$ fission-workflows --plugin
|
||||||
|
name: workflows
|
||||||
|
version: 0.4.0
|
||||||
|
help: Inspect and manage workflow executions
|
||||||
|
```
|
||||||
|
The idea is that this plugin info is all completely optional.
|
||||||
|
If it is not available, we simply degrade the results to user.
|
||||||
|
This way users/we can easily prototype or add plugins without having to worry about adhering to some interface.
|
||||||
|
|
||||||
|
**List version**
|
||||||
|
```bash
|
||||||
|
$ fission --version
|
||||||
|
client:
|
||||||
|
fission: 0.8.0
|
||||||
|
fission-workflows: 0.4.0
|
||||||
|
server:
|
||||||
|
fission: 0.8.1
|
||||||
|
fission-workflows: 0.3.0
|
||||||
|
```
|
||||||
|
Again, versioning info for fission-workflows is taken from the plugin info of the commands.
|
||||||
|
Note: a related issue is to have some more formalized plugin support/discoverability on the server-side,
|
||||||
|
but that is out of the scope of this issue.
|
||||||
|
|
||||||
|
### Other (optional) extensions and notes
|
||||||
|
- Like git we could setup a preferred binary path, where fission looks first when searching for the subcommand.
|
||||||
|
This could optionally be defined with a `FISSION_EXEC_PATH`.
|
||||||
|
- With the current approach we cannot have aliases for commands---fission will not be able to find fission-workflows
|
||||||
|
when the user calls `fission wf`. This might be UX issue, with these long path names. One option is let the user fix
|
||||||
|
it themselves by symlinking `fission-wf` to `fission-workflows`; using the plugin info Fission can recognize and
|
||||||
|
merge aliases together.
|
||||||
|
- To help detect versioning conflicts (old version of fission, too new version of fission workflows). We could add
|
||||||
|
a `requires` field to the fission-workflows plugin info. Then we could throw a warning or error, when two out of sync
|
||||||
|
versions are being used.
|
||||||
|
- To avoid unhelpful errors to the user when they have not installed a plugin, we could add a heuristic to check
|
||||||
|
`https://github.com/fission/SUBCOMMAND` to see if the subcommand might be an uninstalled plugin.
|
||||||
|
OR, we could lookup a simple text file that contains common plugins `https://github.com/fission/fission/plugins.txt`
|
||||||
|
and list them as suggestions to the user. OR we could of course just default to a bit help text that says something
|
||||||
|
like `unknown subcommand 'foo'. If this is a plugin, ensure that it is present on your PATH`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Motivation
|
||||||
|
|
||||||
|
The proposed approach is to use the git-based plugin system for now. Reasons for this approach over a sophisticated,
|
||||||
|
integrated plugin-based approach:
|
||||||
|
- It is low effort to implement.
|
||||||
|
- It is easy to extend with minimal to no required interface.
|
||||||
|
- The binaries remain standalone, allowing users to separate them if needed and make independent development on the
|
||||||
|
binaries easy.
|
||||||
|
|
||||||
|
Limitations of the proposed approach:
|
||||||
|
- The user still has to do some work, adding binaries to the PATH; ensuring that permissions are correct; ensuring
|
||||||
|
that the binary is executable; how to deal with duplicate binaries on the PATH. All this makes this approach assume
|
||||||
|
basic/intermediate knowledge of the OS from the user.
|
||||||
|
- I have to admit: I am not entirely sure if this approach requires any changes for Windows. Probably not.
|
||||||
|
- Upgrading fission with many plugins could be cumbersome, as you would need to upgrade each binary one by one.
|
||||||
|
Improving this is probably best left to future work.
|
||||||
|
|
||||||
|
|
||||||
|
The more heavyweight solution solves some of these limitations to an extent, but these do not way up to the increased
|
||||||
|
development and maintainance cost IMO. If needed we could explore this option (or some hybrid option) in the future.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# Java Environment : Design & considerations
|
||||||
|
|
||||||
|
This document documents the design and thoughts that lead to design of Java environment. Before we dive deeper, some important points:
|
||||||
|
|
||||||
|
- When we say Java, we really mean JVM. That does not mean that all languages will work seamlessly, so support will be added gradually based on validation. Some of popular languages as of today are:
|
||||||
|
- Scala
|
||||||
|
- Groovy
|
||||||
|
- Kotlin (Server side with Spring)
|
||||||
|
|
||||||
|
- In Java there are a few prominent frameworks which have a ecosystem of their own (See list below). How these framework fit in the environment will be detailed later, but it is important to understand their place in ecosystem and design for it.
|
||||||
|
- A large percentage of entertprise developers use [Spring framework](https://spring.io/) as has been shown by multiple surveys
|
||||||
|
- Reactive has taken up recently with data intensive operations [Reactive extensions for JVM](https://github.com/ReactiveX/RxJava)
|
||||||
|
- [Spark](http://sparkjava.com/) is a micro web framework
|
||||||
|
|
||||||
|
A draft implementation of the Java environment design is in the branch [java_env_alpha](https://github.com/fission/fission/tree/java_env_alpha). Also a earlier implementation based on Vort.x framework [can be found here](https://github.com/tobias/fission-java-env/)
|
||||||
|
|
||||||
|
## Function interface
|
||||||
|
|
||||||
|
The goal is here is to minimize the lock in for the user into any framework as much as possible. Java 8 introduced an interface called ```Function``` which could be a great fit here. The user has to implement the ```Function<T, R>``` class and to meet the contract implement the apply method:
|
||||||
|
|
||||||
|
```
|
||||||
|
public class HelloWorld implements Function<T, R> {
|
||||||
|
|
||||||
|
public R apply(T str) {
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Now - the T & R could be different things and we discuss some options below:
|
||||||
|
|
||||||
|
### Body in request and response
|
||||||
|
|
||||||
|
From the early implementation in the branch mentioned above, the environment extracts the body and send it as a JSON string. The JSON then can be transformed into the appropriate object by the function.
|
||||||
|
|
||||||
|
This works well, but has one major limitation: the function does not get access to other things like headers etc. The same thing applies to the reponse: function can send the body but looses control over status code etc.
|
||||||
|
|
||||||
|
### HttpServletRequest and HttpServletResponse
|
||||||
|
|
||||||
|
It is possible to send the [HttpServeletRequest](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html) request object as it is to the function class but then the interface becomes a bit too low level. For example the function user has to retrieve the body of request using ```getInputStream``` which gives raw input stream and needs additional work.
|
||||||
|
|
||||||
|
Also most enterprise applications today use a framework of some sort for web applications instead of dealing with the raw HttpServlet
|
||||||
|
|
||||||
|
### Custom/Context Object
|
||||||
|
|
||||||
|
A custom object which encapsulates all needed fields etc. can be used to pass the data from environment to the function. But this means the user has to import a Fission object/library for this object in the application code.
|
||||||
|
|
||||||
|
This approach has been taken in the implementation done earlier for a Java environment in Fission and [object interface can be found here](https://github.com/tobias/fission-java-env/blob/master/src/main/java/io/fission/api/Context.java). Related discussion is in the [issue](https://github.com/fission/fission/issues/91)
|
||||||
|
|
||||||
|
AWS Lambda also uses a context object, but the purpose is very different, [details of context object here](https://docs.aws.amazon.com/lambda/latest/dg/java-context-object.html).
|
||||||
|
|
||||||
|
### Spring's HttpEntity
|
||||||
|
|
||||||
|
If we have to depend on a class/library, it is probably better to depend on a class which is part of ecosystem. So instead of using the low level interface of Servlet, we can use [HttpEntity's subclasses RequestEntity and ResponseEntity](https://docs.spring.io/spring/docs/5.0.5.RELEASE/javadoc-api/org/springframework/http/HttpEntity.html). This ensures that the function user is not locked in the Fission object contract, but also gets the full access to request/response object.
|
||||||
|
|
||||||
|
The Spring cloud function project also discusses the issue of not having access to other things in request and [related issues are here](https://github.com/spring-cloud/spring-cloud-function/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+header)
|
||||||
|
|
||||||
|
### Thoughts
|
||||||
|
|
||||||
|
- If we only intend to pass request/response object to function - then using HttpEntity might be a good choice
|
||||||
|
|
||||||
|
- If there is a need for additional exchange of information between the environment and function execution in future, then a custom/context object is a better option. We can wrap the HttpEntity's fields and additional fields in the custom context object
|
||||||
|
|
||||||
|
## Environment Design
|
||||||
|
|
||||||
|
JVM environment design is based on Spring boot and Spring MVC frameworks. The details can be found in branch, but here are some key points:
|
||||||
|
|
||||||
|
- All classes in the function and dependent classes are loaded into JVM. Which means the user should supply the uber/fat jar for execution.
|
||||||
|
- The entrypoint class is specified by the user as ```entrypoint``` flag on the class. The method is by convention (```apply``` as per the Function interface contract)
|
||||||
+1
-1
@@ -72,7 +72,7 @@ func MakeBuilder(sharedVolumePath string) *Builder {
|
|||||||
|
|
||||||
func (builder *Builder) VersionHandler(w http.ResponseWriter, r *http.Request) {
|
func (builder *Builder) VersionHandler(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, fission.VersionInfo().String())
|
fmt.Fprintf(w, fission.BuildInfo().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) {
|
func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ func Start(storageSvcUrl string, envBuilderNamespace string) error {
|
|||||||
go envWatcher.watchEnvironments()
|
go envWatcher.watchEnvironments()
|
||||||
|
|
||||||
pkgWatcher := makePackageWatcher(fissionClient,
|
pkgWatcher := makePackageWatcher(fissionClient,
|
||||||
kubernetesClient.CoreV1().RESTClient(), envBuilderNamespace, storageSvcUrl)
|
kubernetesClient, envBuilderNamespace, storageSvcUrl)
|
||||||
go pkgWatcher.watchPackages()
|
go pkgWatcher.watchPackages(fissionClient, kubernetesClient, envBuilderNamespace)
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-11
@@ -22,8 +22,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
|
|
||||||
"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"
|
||||||
@@ -40,25 +38,26 @@ import (
|
|||||||
// 3. Send upload request to fetcher to upload deployment package.
|
// 3. Send upload request to fetcher to upload deployment package.
|
||||||
// 4. Return upload response and build logs.
|
// 4. Return upload response and build logs.
|
||||||
// *. Return build logs and error if any one of steps above failed.
|
// *. Return build logs and error if any one of steps above failed.
|
||||||
func buildPackage(fissionClient *crd.FissionClient, builderNamespace string,
|
func buildPackage(fissionClient *crd.FissionClient, envBuilderNamespace string,
|
||||||
storageSvcUrl string, pkg *crd.Package) (uploadResp *fetcher.UploadResponse, buildLogs string, err error) {
|
storageSvcUrl string, pkg *crd.Package) (uploadResp *fetcher.UploadResponse, buildLogs string, err error) {
|
||||||
|
|
||||||
env, err := fissionClient.Environments(metav1.NamespaceDefault).Get(pkg.Spec.Environment.Name)
|
env, err := fissionClient.Environments(pkg.Spec.Environment.Namespace).Get(pkg.Spec.Environment.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := fmt.Sprintf("Error getting environment CRD info: %v", err)
|
e := fmt.Sprintf("Error getting environment CRD info: %v", err)
|
||||||
log.Println(e)
|
log.Println(e)
|
||||||
return nil, e, fission.MakeError(http.StatusInternalServerError, e)
|
return nil, 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, envBuilderNamespace)
|
||||||
srcPkgFilename := fmt.Sprintf("%v-%v", pkg.Metadata.Name, strings.ToLower(uniuri.NewLen(6)))
|
srcPkgFilename := fmt.Sprintf("%v-%v", pkg.Metadata.Name, strings.ToLower(uniuri.NewLen(6)))
|
||||||
fetcherC := fetcherClient.MakeClient(fmt.Sprintf("http://%v:8000", svcName))
|
fetcherC := fetcherClient.MakeClient(fmt.Sprintf("http://%v:8000", svcName))
|
||||||
builderC := builderClient.MakeClient(fmt.Sprintf("http://%v:8001", svcName))
|
builderC := builderClient.MakeClient(fmt.Sprintf("http://%v:8001", svcName))
|
||||||
|
|
||||||
fetchReq := &fetcher.FetchRequest{
|
fetchReq := &fetcher.FetchRequest{
|
||||||
FetchType: fetcher.FETCH_SOURCE,
|
FetchType: fetcher.FETCH_SOURCE,
|
||||||
Package: pkg.Metadata,
|
Package: pkg.Metadata,
|
||||||
Filename: srcPkgFilename,
|
Filename: srcPkgFilename,
|
||||||
|
KeepArchive: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// send fetch request to fetcher
|
// send fetch request to fetcher
|
||||||
@@ -95,9 +94,12 @@ func buildPackage(fissionClient *crd.FissionClient, builderNamespace string,
|
|||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
archivePackage := !env.Spec.KeepArchive
|
||||||
|
|
||||||
uploadReq := &fetcher.UploadRequest{
|
uploadReq := &fetcher.UploadRequest{
|
||||||
Filename: buildResp.ArtifactFilename,
|
Filename: buildResp.ArtifactFilename,
|
||||||
StorageSvcUrl: storageSvcUrl,
|
StorageSvcUrl: storageSvcUrl,
|
||||||
|
ArchivePackage: archivePackage,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Start uploading deployment package: %v", buildResp.ArtifactFilename)
|
log.Printf("Start uploading deployment package: %v", buildResp.ArtifactFilename)
|
||||||
@@ -131,7 +133,7 @@ func updatePackage(fissionClient *crd.FissionClient,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update package spec
|
// update package spec
|
||||||
pkg, err := fissionClient.Packages(metav1.NamespaceDefault).Update(pkg)
|
pkg, err := fissionClient.Packages(pkg.Metadata.Namespace).Update(pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error updating package: %v", err)
|
log.Printf("Error updating package: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+106
-49
@@ -23,13 +23,13 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/api/extensions/v1beta1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
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/crd"
|
||||||
@@ -42,7 +42,10 @@ const (
|
|||||||
CLEANUP_BUILDERS
|
CLEANUP_BUILDERS
|
||||||
|
|
||||||
LABEL_ENV_NAME = "envName"
|
LABEL_ENV_NAME = "envName"
|
||||||
|
LABEL_ENV_NAMESPACE = "envNamespace"
|
||||||
LABEL_ENV_RESOURCEVERSION = "envResourceVersion"
|
LABEL_ENV_RESOURCEVERSION = "envResourceVersion"
|
||||||
|
LABEL_DEPLOYMENT_OWNER = "owner"
|
||||||
|
BUILDER_MGR = "buildermgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -130,14 +133,22 @@ func makeEnvironmentWatcher(fissionClient *crd.FissionClient,
|
|||||||
return envWatcher
|
return envWatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) getCacheKey(envName string, envResourceVersion string) string {
|
func (envw *environmentWatcher) getCacheKey(envName string, envNamespace string, envResourceVersion string) string {
|
||||||
return fmt.Sprintf("%v-%v", envName, envResourceVersion)
|
return fmt.Sprintf("%v-%v-%v", envName, envNamespace, envResourceVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) getLabels(envName string, envResourceVersion string) map[string]string {
|
func (env *environmentWatcher) getLabelForDeploymentOwner() map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
LABEL_DEPLOYMENT_OWNER: BUILDER_MGR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (envw *environmentWatcher) getLabels(envName string, envNamespace string, envResourceVersion string) map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
LABEL_ENV_NAME: envName,
|
LABEL_ENV_NAME: envName,
|
||||||
|
LABEL_ENV_NAMESPACE: envNamespace,
|
||||||
LABEL_ENV_RESOURCEVERSION: envResourceVersion,
|
LABEL_ENV_RESOURCEVERSION: envResourceVersion,
|
||||||
|
LABEL_DEPLOYMENT_OWNER: BUILDER_MGR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,10 +224,17 @@ func (envw *environmentWatcher) service() {
|
|||||||
req := <-envw.requestChan
|
req := <-envw.requestChan
|
||||||
switch req.requestType {
|
switch req.requestType {
|
||||||
case GET_BUILDER:
|
case GET_BUILDER:
|
||||||
key := envw.getCacheKey(req.env.Metadata.Name, req.env.Metadata.ResourceVersion)
|
// In order to support backward compatibility, for all environments with builder image created in default env,
|
||||||
|
// the pods will be created in fission-builder namespace
|
||||||
|
ns := envw.builderNamespace
|
||||||
|
if req.env.Metadata.Namespace != metav1.NamespaceDefault {
|
||||||
|
ns = req.env.Metadata.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
key := envw.getCacheKey(req.env.Metadata.Name, ns, req.env.Metadata.ResourceVersion)
|
||||||
builderInfo, ok := envw.cache[key]
|
builderInfo, ok := envw.cache[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
builderInfo, err := envw.createBuilder(req.env)
|
builderInfo, err := envw.createBuilder(req.env, ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.respChan <- envwResponse{err: err}
|
req.respChan <- envwResponse{err: err}
|
||||||
continue
|
continue
|
||||||
@@ -229,7 +247,13 @@ func (envw *environmentWatcher) service() {
|
|||||||
latestEnvList := make(map[string]*crd.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)
|
// In order to support backward compatibility, for all builder images created in default
|
||||||
|
// env, the pods are created in fission-builder namespace
|
||||||
|
ns := envw.builderNamespace
|
||||||
|
if env.Metadata.Namespace != metav1.NamespaceDefault {
|
||||||
|
ns = env.Metadata.Namespace
|
||||||
|
}
|
||||||
|
key := envw.getCacheKey(env.Metadata.Name, ns, env.Metadata.ResourceVersion)
|
||||||
latestEnvList[key] = &env
|
latestEnvList[key] = &env
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,38 +263,40 @@ func (envw *environmentWatcher) service() {
|
|||||||
// cache and CRD. 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(envw.getLabelForDeploymentOwner(), metav1.NamespaceAll)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
log.Println(err.Error())
|
||||||
}
|
}
|
||||||
for _, svc := range svcList {
|
for _, svc := range svcList {
|
||||||
envName := svc.ObjectMeta.Labels[LABEL_ENV_NAME]
|
envName := svc.ObjectMeta.Labels[LABEL_ENV_NAME]
|
||||||
|
envNamespace := svc.ObjectMeta.Labels[LABEL_ENV_NAMESPACE]
|
||||||
envResourceVersion := svc.ObjectMeta.Labels[LABEL_ENV_RESOURCEVERSION]
|
envResourceVersion := svc.ObjectMeta.Labels[LABEL_ENV_RESOURCEVERSION]
|
||||||
key := envw.getCacheKey(envName, envResourceVersion)
|
key := envw.getCacheKey(envName, envNamespace, envResourceVersion)
|
||||||
if _, ok := latestEnvList[key]; !ok {
|
if _, ok := latestEnvList[key]; !ok {
|
||||||
err := envw.deleteBuilderService(svc.ObjectMeta.Labels)
|
err := envw.deleteBuilderServiceByName(svc.ObjectMeta.Name, svc.ObjectMeta.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error removing builder service: %v", err)
|
log.Printf("Error removing builder service: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(envw.cache, svc.ObjectMeta.Name)
|
delete(envw.cache, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
deployList, err := envw.getBuilderDeploymentList(nil)
|
deployList, err := envw.getBuilderDeploymentList(envw.getLabelForDeploymentOwner(), metav1.NamespaceAll)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(err.Error())
|
log.Printf(err.Error())
|
||||||
}
|
}
|
||||||
for _, deploy := range deployList {
|
for _, deploy := range deployList {
|
||||||
envName := deploy.ObjectMeta.Labels[LABEL_ENV_NAME]
|
envName := deploy.ObjectMeta.Labels[LABEL_ENV_NAME]
|
||||||
|
envNamespace := deploy.ObjectMeta.Labels[LABEL_ENV_NAMESPACE]
|
||||||
envResourceVersion := deploy.ObjectMeta.Labels[LABEL_ENV_RESOURCEVERSION]
|
envResourceVersion := deploy.ObjectMeta.Labels[LABEL_ENV_RESOURCEVERSION]
|
||||||
key := envw.getCacheKey(envName, envResourceVersion)
|
key := envw.getCacheKey(envName, envNamespace, envResourceVersion)
|
||||||
if _, ok := latestEnvList[key]; !ok {
|
if _, ok := latestEnvList[key]; !ok {
|
||||||
err := envw.deleteBuilderDeployment(deploy.ObjectMeta.Labels)
|
err := envw.deleteBuilderDeploymentByName(deploy.ObjectMeta.Name, deploy.ObjectMeta.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error removing builder deployment: %v", err)
|
log.Printf("Error removing builder deployment: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(envw.cache, deploy.ObjectMeta.Name)
|
delete(envw.cache, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,19 +320,19 @@ func (envw *environmentWatcher) cleanupEnvBuilders(envs []crd.Environment) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) createBuilder(env *crd.Environment) (*builderInfo, error) {
|
func (envw *environmentWatcher) createBuilder(env *crd.Environment, ns string) (*builderInfo, error) {
|
||||||
var svc *apiv1.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, ns, env.Metadata.ResourceVersion)
|
||||||
|
|
||||||
svcList, err := envw.getBuilderServiceList(sel)
|
svcList, err := envw.getBuilderServiceList(sel, ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// there should be only one service in svcList
|
// 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, ns)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -316,13 +342,20 @@ func (envw *environmentWatcher) createBuilder(env *crd.Environment) (*builderInf
|
|||||||
return nil, fmt.Errorf("Found more than one builder service for environment %v", env.Metadata.Name)
|
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, ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// there should be only one deploy in deployList
|
// there should be only one deploy in deployList
|
||||||
if len(deployList) == 0 {
|
if len(deployList) == 0 {
|
||||||
deploy, err = envw.createBuilderDeployment(env)
|
// create builder SA in this ns, if not already created
|
||||||
|
_, err := fission.SetupSA(envw.kubernetesClient, fission.FissionBuilderSA, ns)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error : %v creating %s in ns : %s", err, fission.FissionBuilderSA, ns)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
deploy, err = envw.createBuilderDeployment(env, ns)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -339,16 +372,36 @@ func (envw *environmentWatcher) createBuilder(env *crd.Environment) (*builderInf
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) deleteBuilderService(sel map[string]string) error {
|
func (envw *environmentWatcher) deleteBuilderServiceByName(name, namespace string) error {
|
||||||
svcList, err := envw.getBuilderServiceList(sel)
|
err := envw.kubernetesClient.CoreV1().
|
||||||
|
Services(namespace).
|
||||||
|
Delete(name, &delOpt)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting builder service %s.%s: %v", name, namespace, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (envw *environmentWatcher) deleteBuilderDeploymentByName(name, namespace string) error {
|
||||||
|
err := envw.kubernetesClient.ExtensionsV1beta1().
|
||||||
|
Deployments(namespace).
|
||||||
|
Delete(name, &delOpt)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting builder deployment %s.%s: %v", name, namespace, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (envw *environmentWatcher) deleteBuilderService(sel map[string]string, ns string) error {
|
||||||
|
svcList, err := envw.getBuilderServiceList(sel, ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, svc := range svcList {
|
for _, svc := range svcList {
|
||||||
log.Printf("Removing builder service: %v", svc.ObjectMeta.Name)
|
log.Printf("Removing builder service: %v", svc.ObjectMeta.Name)
|
||||||
err = envw.kubernetesClient.
|
err = envw.kubernetesClient.CoreV1().
|
||||||
Services(envw.builderNamespace).
|
Services(ns).
|
||||||
Delete(svc.ObjectMeta.Name, nil)
|
Delete(svc.ObjectMeta.Name, &delOpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error deleting builder service: %v", err)
|
return fmt.Errorf("Error deleting builder service: %v", err)
|
||||||
}
|
}
|
||||||
@@ -356,15 +409,15 @@ func (envw *environmentWatcher) deleteBuilderService(sel map[string]string) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) deleteBuilderDeployment(sel map[string]string) error {
|
func (envw *environmentWatcher) deleteBuilderDeployment(sel map[string]string, ns string) error {
|
||||||
deployList, err := envw.getBuilderDeploymentList(sel)
|
deployList, err := envw.getBuilderDeploymentList(sel, ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, deploy := range deployList {
|
for _, deploy := range deployList {
|
||||||
log.Printf("Removing builder deployment: %v", deploy.ObjectMeta.Name)
|
log.Printf("Removing builder deployment: %v", deploy.ObjectMeta.Name)
|
||||||
err = envw.kubernetesClient.ExtensionsV1beta1().
|
err = envw.kubernetesClient.ExtensionsV1beta1().
|
||||||
Deployments(envw.builderNamespace).
|
Deployments(ns).
|
||||||
Delete(deploy.ObjectMeta.Name, &delOpt)
|
Delete(deploy.ObjectMeta.Name, &delOpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error deleteing builder deployment: %v", err)
|
return fmt.Errorf("Error deleteing builder deployment: %v", err)
|
||||||
@@ -373,8 +426,8 @@ func (envw *environmentWatcher) deleteBuilderDeployment(sel map[string]string) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) getBuilderServiceList(sel map[string]string) ([]apiv1.Service, error) {
|
func (envw *environmentWatcher) getBuilderServiceList(sel map[string]string, ns string) ([]apiv1.Service, error) {
|
||||||
svcList, err := envw.kubernetesClient.Services(envw.builderNamespace).List(
|
svcList, err := envw.kubernetesClient.CoreV1().Services(ns).List(
|
||||||
metav1.ListOptions{
|
metav1.ListOptions{
|
||||||
LabelSelector: labels.Set(sel).AsSelector().String(),
|
LabelSelector: labels.Set(sel).AsSelector().String(),
|
||||||
})
|
})
|
||||||
@@ -384,12 +437,12 @@ func (envw *environmentWatcher) getBuilderServiceList(sel map[string]string) ([]
|
|||||||
return svcList.Items, nil
|
return svcList.Items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) createBuilderService(env *crd.Environment) (*apiv1.Service, error) {
|
func (envw *environmentWatcher) createBuilderService(env *crd.Environment, ns string) (*apiv1.Service, error) {
|
||||||
name := envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion)
|
name := fmt.Sprintf("%v-%v", env.Metadata.Name, env.Metadata.ResourceVersion)
|
||||||
sel := envw.getLabels(env.Metadata.Name, env.Metadata.ResourceVersion)
|
sel := envw.getLabels(env.Metadata.Name, ns, env.Metadata.ResourceVersion)
|
||||||
service := apiv1.Service{
|
service := apiv1.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: envw.builderNamespace,
|
Namespace: ns,
|
||||||
Name: name,
|
Name: name,
|
||||||
Labels: sel,
|
Labels: sel,
|
||||||
},
|
},
|
||||||
@@ -419,15 +472,15 @@ func (envw *environmentWatcher) createBuilderService(env *crd.Environment) (*api
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
log.Printf("Creating builder service: %v", name)
|
log.Printf("Creating builder service: %v", name)
|
||||||
_, err := envw.kubernetesClient.Services(envw.builderNamespace).Create(&service)
|
_, err := envw.kubernetesClient.CoreV1().Services(ns).Create(&service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &service, nil
|
return &service, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) getBuilderDeploymentList(sel map[string]string) ([]v1beta1.Deployment, error) {
|
func (envw *environmentWatcher) getBuilderDeploymentList(sel map[string]string, ns string) ([]v1beta1.Deployment, error) {
|
||||||
deployList, err := envw.kubernetesClient.ExtensionsV1beta1().Deployments(envw.builderNamespace).List(
|
deployList, err := envw.kubernetesClient.ExtensionsV1beta1().Deployments(ns).List(
|
||||||
metav1.ListOptions{
|
metav1.ListOptions{
|
||||||
LabelSelector: labels.Set(sel).AsSelector().String(),
|
LabelSelector: labels.Set(sel).AsSelector().String(),
|
||||||
})
|
})
|
||||||
@@ -437,22 +490,25 @@ func (envw *environmentWatcher) getBuilderDeploymentList(sel map[string]string)
|
|||||||
return deployList.Items, nil
|
return deployList.Items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (envw *environmentWatcher) createBuilderDeployment(env *crd.Environment) (*v1beta1.Deployment, error) {
|
func (envw *environmentWatcher) createBuilderDeployment(env *crd.Environment, ns string) (*v1beta1.Deployment, error) {
|
||||||
sharedMountPath := "/packages"
|
sharedMountPath := "/packages"
|
||||||
sharedCfgMapPath := "/configs"
|
sharedCfgMapPath := "/configs"
|
||||||
sharedSecretPath := "/secrets"
|
sharedSecretPath := "/secrets"
|
||||||
name := envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion)
|
name := fmt.Sprintf("%v-%v", env.Metadata.Name, env.Metadata.ResourceVersion)
|
||||||
sel := envw.getLabels(env.Metadata.Name, env.Metadata.ResourceVersion)
|
sel := envw.getLabels(env.Metadata.Name, ns, env.Metadata.ResourceVersion)
|
||||||
var replicas int32 = 1
|
var replicas int32 = 1
|
||||||
|
|
||||||
podAnnotation := make(map[string]string)
|
podAnnotations := env.Metadata.Annotations
|
||||||
|
if podAnnotations == nil {
|
||||||
|
podAnnotations = make(map[string]string)
|
||||||
|
}
|
||||||
if envw.useIstio && env.Spec.AllowAccessToExternalNetwork {
|
if envw.useIstio && env.Spec.AllowAccessToExternalNetwork {
|
||||||
podAnnotation["sidecar.istio.io/inject"] = "false"
|
podAnnotations["sidecar.istio.io/inject"] = "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
deployment := &v1beta1.Deployment{
|
deployment := &v1beta1.Deployment{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: envw.builderNamespace,
|
Namespace: ns,
|
||||||
Name: name,
|
Name: name,
|
||||||
Labels: sel,
|
Labels: sel,
|
||||||
},
|
},
|
||||||
@@ -464,7 +520,7 @@ func (envw *environmentWatcher) createBuilderDeployment(env *crd.Environment) (*
|
|||||||
Template: apiv1.PodTemplateSpec{
|
Template: apiv1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: sel,
|
Labels: sel,
|
||||||
Annotations: podAnnotation,
|
Annotations: podAnnotations,
|
||||||
},
|
},
|
||||||
Spec: apiv1.PodSpec{
|
Spec: apiv1.PodSpec{
|
||||||
Volumes: []apiv1.Volume{
|
Volumes: []apiv1.Volume{
|
||||||
@@ -491,7 +547,7 @@ func (envw *environmentWatcher) createBuilderDeployment(env *crd.Environment) (*
|
|||||||
fission.MergeContainerSpecs(&apiv1.Container{
|
fission.MergeContainerSpecs(&apiv1.Container{
|
||||||
Name: "builder",
|
Name: "builder",
|
||||||
Image: env.Spec.Builder.Image,
|
Image: env.Spec.Builder.Image,
|
||||||
ImagePullPolicy: apiv1.PullAlways,
|
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
VolumeMounts: []apiv1.VolumeMount{
|
VolumeMounts: []apiv1.VolumeMount{
|
||||||
{
|
{
|
||||||
@@ -565,10 +621,11 @@ func (envw *environmentWatcher) createBuilderDeployment(env *crd.Environment) (*
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
log.Printf("Creating builder deployment: %v", envw.getCacheKey(env.Metadata.Name, env.Metadata.ResourceVersion))
|
log.Printf("Creating builder deployment: %v", fmt.Sprintf("%v-%v", env.Metadata.Name, env.Metadata.ResourceVersion))
|
||||||
_, err := envw.kubernetesClient.ExtensionsV1beta1().Deployments(envw.builderNamespace).Create(deployment)
|
_, err := envw.kubernetesClient.ExtensionsV1beta1().Deployments(ns).Create(deployment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return deployment, nil
|
return deployment, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-10
@@ -21,35 +21,38 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
apiv1 "k8s.io/client-go/pkg/api/v1"
|
|
||||||
k8sCache "k8s.io/client-go/tools/cache"
|
k8sCache "k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/cache"
|
"github.com/fission/fission/cache"
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
packageWatcher struct {
|
packageWatcher struct {
|
||||||
fissionClient *crd.FissionClient
|
fissionClient *crd.FissionClient
|
||||||
|
k8sClient *kubernetes.Clientset
|
||||||
podStore k8sCache.Store
|
podStore k8sCache.Store
|
||||||
|
pkgStore k8sCache.Store
|
||||||
builderNamespace string
|
builderNamespace string
|
||||||
storageSvcUrl string
|
storageSvcUrl string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func makePackageWatcher(fissionClient *crd.FissionClient, getter k8sCache.Getter,
|
func makePackageWatcher(fissionClient *crd.FissionClient, k8sClientSet *kubernetes.Clientset,
|
||||||
builderNamespace string, storageSvcUrl string) *packageWatcher {
|
builderNamespace string, storageSvcUrl string) *packageWatcher {
|
||||||
|
lw := k8sCache.NewListWatchFromClient(k8sClientSet.CoreV1().RESTClient(), "pods", metav1.NamespaceAll, fields.Everything())
|
||||||
lw := k8sCache.NewListWatchFromClient(getter, "pods", builderNamespace, fields.Everything())
|
|
||||||
store, controller := k8sCache.NewInformer(lw, &apiv1.Pod{}, 30*time.Second, k8sCache.ResourceEventHandlerFuncs{})
|
store, controller := k8sCache.NewInformer(lw, &apiv1.Pod{}, 30*time.Second, k8sCache.ResourceEventHandlerFuncs{})
|
||||||
go controller.Run(make(chan struct{}))
|
go controller.Run(make(chan struct{}))
|
||||||
|
|
||||||
pkgw := &packageWatcher{
|
pkgw := &packageWatcher{
|
||||||
fissionClient: fissionClient,
|
fissionClient: fissionClient,
|
||||||
|
k8sClient: k8sClientSet,
|
||||||
podStore: store,
|
podStore: store,
|
||||||
builderNamespace: builderNamespace,
|
builderNamespace: builderNamespace,
|
||||||
storageSvcUrl: storageSvcUrl,
|
storageSvcUrl: storageSvcUrl,
|
||||||
@@ -118,8 +121,16 @@ func (pkgw *packageWatcher) build(buildCache *cache.Cache, srcpkg *crd.Package)
|
|||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
pod := item.(*apiv1.Pod)
|
pod := item.(*apiv1.Pod)
|
||||||
|
|
||||||
|
// In order to support backward compatibility, for all builder images created in default env,
|
||||||
|
// the pods will be created in fission-builder namespace
|
||||||
|
builderNs := pkgw.builderNamespace
|
||||||
|
if env.Metadata.Namespace != metav1.NamespaceDefault {
|
||||||
|
builderNs = env.Metadata.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
// Filter non-matching pods
|
// Filter non-matching pods
|
||||||
if pod.ObjectMeta.Labels[LABEL_ENV_NAME] != env.Metadata.Name ||
|
if pod.ObjectMeta.Labels[LABEL_ENV_NAME] != env.Metadata.Name ||
|
||||||
|
pod.ObjectMeta.Labels[LABEL_ENV_NAMESPACE] != builderNs ||
|
||||||
pod.ObjectMeta.Labels[LABEL_ENV_RESOURCEVERSION] != env.Metadata.ResourceVersion {
|
pod.ObjectMeta.Labels[LABEL_ENV_RESOURCEVERSION] != env.Metadata.ResourceVersion {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -138,8 +149,18 @@ func (pkgw *packageWatcher) build(buildCache *cache.Cache, srcpkg *crd.Package)
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadResp, buildLogs, err := buildPackage(pkgw.fissionClient,
|
// Add the package getter rolebinding to builder sa
|
||||||
pkgw.builderNamespace, pkgw.storageSvcUrl, pkg)
|
// we continue here if role binding was not setup succeesffully. this is because without this, the fetcher wont be able to fetch the source pkg into the container and
|
||||||
|
// the build will fail eventually
|
||||||
|
err := fission.SetupRoleBinding(pkgw.k8sClient, fission.PackageGetterRB, pkg.Metadata.Namespace, fission.PackageGetterCR, fission.ClusterRole, fission.FissionBuilderSA, builderNs)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error : %v in setting up the role binding %s for pkg : %s.%s", err, fission.PackageGetterRB, pkg.Metadata.Name, pkg.Metadata.Namespace)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
log.Printf("Setup rolebinding for sa : %s.%s for pkg : %s.%s", fission.FissionBuilderSA, builderNs, pkg.Metadata.Name, pkg.Metadata.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadResp, buildLogs, err := buildPackage(pkgw.fissionClient, builderNs, pkgw.storageSvcUrl, pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error building package %v: %v", pkg.Metadata.Name, err)
|
log.Printf("Error building package %v: %v", pkg.Metadata.Name, err)
|
||||||
updatePackage(pkgw.fissionClient, pkg, fission.BuildStatusFailed, buildLogs, nil)
|
updatePackage(pkgw.fissionClient, pkg, fission.BuildStatusFailed, buildLogs, nil)
|
||||||
@@ -149,7 +170,7 @@ func (pkgw *packageWatcher) build(buildCache *cache.Cache, srcpkg *crd.Package)
|
|||||||
log.Printf("Start updating info of package: %v", pkg.Metadata.Name)
|
log.Printf("Start updating info of package: %v", pkg.Metadata.Name)
|
||||||
|
|
||||||
fnList, err := pkgw.fissionClient.
|
fnList, err := pkgw.fissionClient.
|
||||||
Functions(metav1.NamespaceDefault).List(metav1.ListOptions{})
|
Functions(metav1.NamespaceAll).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)
|
||||||
@@ -191,13 +212,18 @@ func (pkgw *packageWatcher) build(buildCache *cache.Cache, srcpkg *crd.Package)
|
|||||||
// build timeout
|
// build timeout
|
||||||
updatePackage(pkgw.fissionClient, pkg,
|
updatePackage(pkgw.fissionClient, pkg,
|
||||||
fission.BuildStatusFailed, "Build timeout due to environment builder not ready", nil)
|
fission.BuildStatusFailed, "Build timeout due to environment builder not ready", nil)
|
||||||
|
|
||||||
|
log.Printf("Max retries exceeded in building the source pkg : %s.%s, timeout due to environment builder not ready",
|
||||||
|
pkg.Metadata.Name, pkg.Metadata.Namespace)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pkgw *packageWatcher) watchPackages() {
|
func (pkgw *packageWatcher) watchPackages(fissionClient *crd.FissionClient,
|
||||||
|
kubernetesClient *kubernetes.Clientset, builderNamespace string) {
|
||||||
buildCache := cache.MakeCache(0, 0)
|
buildCache := cache.MakeCache(0, 0)
|
||||||
lw := k8sCache.NewListWatchFromClient(pkgw.fissionClient.GetCrdClient(), "packages", apiv1.NamespaceDefault, fields.Everything())
|
lw := k8sCache.NewListWatchFromClient(pkgw.fissionClient.GetCrdClient(), "packages", apiv1.NamespaceAll, fields.Everything())
|
||||||
_, controller := k8sCache.NewInformer(lw, &crd.Package{}, 60*time.Second, k8sCache.ResourceEventHandlerFuncs{
|
pkgStore, controller := k8sCache.NewInformer(lw, &crd.Package{}, 60*time.Second, k8sCache.ResourceEventHandlerFuncs{
|
||||||
AddFunc: func(obj interface{}) {
|
AddFunc: func(obj interface{}) {
|
||||||
pkg := obj.(*crd.Package)
|
pkg := obj.(*crd.Package)
|
||||||
go pkgw.build(buildCache, pkg)
|
go pkgw.build(buildCache, pkg)
|
||||||
@@ -207,5 +233,6 @@ func (pkgw *packageWatcher) watchPackages() {
|
|||||||
go pkgw.build(buildCache, pkg)
|
go pkgw.build(buildCache, pkg)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
pkgw.pkgStore = pkgStore
|
||||||
controller.Run(make(chan struct{}))
|
controller.Run(make(chan struct{}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
name: fission-all
|
name: fission-all
|
||||||
version: 0.7.1
|
version: 0.9.2
|
||||||
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.7.1
|
appVersion: 0.9.2
|
||||||
|
|||||||
@@ -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.7.1/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
|
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.9.2/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.7.1/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
|
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.9.2/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.7.1/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.9.2/fission-cli-windows.exe
|
||||||
|
|
||||||
2. You're ready to use Fission!
|
2. You're ready to use Fission!
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,37 @@ metadata:
|
|||||||
istio-injection: enabled
|
istio-injection: enabled
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: secret-configmap-getter
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- '*'
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
- configmaps
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- watch
|
||||||
|
- list
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: package-getter
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- '*'
|
||||||
|
resources:
|
||||||
|
- packages
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- watch
|
||||||
|
- list
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
@@ -44,21 +75,6 @@ roleRef:
|
|||||||
name: admin
|
name: admin
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
|
||||||
---
|
|
||||||
kind: RoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
|
||||||
metadata:
|
|
||||||
name: fission-function-admin
|
|
||||||
namespace: {{ .Values.functionNamespace }}
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: fission-svc
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: admin
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
@@ -79,21 +95,6 @@ kind: ServiceAccount
|
|||||||
metadata:
|
metadata:
|
||||||
name: fission-fetcher
|
name: fission-fetcher
|
||||||
namespace: {{ .Values.functionNamespace }}
|
namespace: {{ .Values.functionNamespace }}
|
||||||
|
|
||||||
---
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
|
||||||
metadata:
|
|
||||||
name: fission-fetcher-crd
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: fission-fetcher
|
|
||||||
namespace: {{ .Values.functionNamespace }}
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: cluster-admin
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
@@ -101,20 +102,6 @@ metadata:
|
|||||||
name: fission-builder
|
name: fission-builder
|
||||||
namespace: {{ .Values.builderNamespace }}
|
namespace: {{ .Values.builderNamespace }}
|
||||||
|
|
||||||
---
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
|
||||||
metadata:
|
|
||||||
name: fission-builder-crd
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: fission-builder
|
|
||||||
namespace: {{ .Values.builderNamespace }}
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: cluster-admin
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -139,6 +126,10 @@ spec:
|
|||||||
env:
|
env:
|
||||||
- name: FISSION_FUNCTION_NAMESPACE
|
- name: FISSION_FUNCTION_NAMESPACE
|
||||||
value: "{{ .Values.functionNamespace }}"
|
value: "{{ .Values.functionNamespace }}"
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/healthz"
|
path: "/healthz"
|
||||||
@@ -168,6 +159,10 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
application: fission-router
|
application: fission-router
|
||||||
svc: router
|
svc: router
|
||||||
|
annotations:
|
||||||
|
prometheus.io/scrape: "true"
|
||||||
|
prometheus.io/path: "/metrics"
|
||||||
|
prometheus.io/port: "8080"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: router
|
- name: router
|
||||||
@@ -175,6 +170,19 @@ spec:
|
|||||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--routerPort", "8888", "--executorUrl", "http://executor.{{ .Release.Namespace }}"]
|
args: ["--routerPort", "8888", "--executorUrl", "http://executor.{{ .Release.Namespace }}"]
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: ROUTER_ROUND_TRIP_TIMEOUT
|
||||||
|
value: {{ .Values.routerRoundTripTimeout | default "50ms" | quote }}
|
||||||
|
- name: ROUTER_ROUNDTRIP_TIMEOUT_EXPONENT
|
||||||
|
value: {{ .Values.routerRoundTripTimeoutExponent | default 2 | quote }}
|
||||||
|
- name: ROUTER_ROUND_TRIP_KEEP_ALIVE_TIME
|
||||||
|
value: {{ .Values.routerRoundTripKeepAliveTime | default "30s" | quote }}
|
||||||
|
- name: ROUTER_ROUND_TRIP_MAX_RETRIES
|
||||||
|
value: {{ .Values.routerRoundTripMaxRetries | default 10 | quote }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/router-healthz"
|
path: "/router-healthz"
|
||||||
@@ -188,6 +196,11 @@ spec:
|
|||||||
port: 8888
|
port: 8888
|
||||||
initialDelaySeconds: 35
|
initialDelaySeconds: 35
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: metrics
|
||||||
|
- containerPort: 8888
|
||||||
|
name: http
|
||||||
serviceAccount: fission-svc
|
serviceAccount: fission-svc
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -219,6 +232,10 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
svc: executor
|
svc: executor
|
||||||
|
annotations:
|
||||||
|
prometheus.io/scrape: "true"
|
||||||
|
prometheus.io/path: "/metrics"
|
||||||
|
prometheus.io/port: "8080"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: executor
|
- name: executor
|
||||||
@@ -235,6 +252,14 @@ spec:
|
|||||||
value: "{{ .Values.pullPolicy }}"
|
value: "{{ .Values.pullPolicy }}"
|
||||||
- name: ENABLE_ISTIO
|
- name: ENABLE_ISTIO
|
||||||
value: "{{ .Values.enableIstio }}"
|
value: "{{ .Values.enableIstio }}"
|
||||||
|
- name: FETCHER_MINCPU
|
||||||
|
value: {{ .Values.fetcherMinCpu | default "10m" | quote }}
|
||||||
|
- name: FETCHER_MINMEM
|
||||||
|
value: {{ .Values.fetcherMinMem | default "16Mi" | quote }}
|
||||||
|
- name: FETCHER_MAXCPU
|
||||||
|
value: {{ .Values.fetcherMaxCpu | default "40m" | quote }}
|
||||||
|
- name: FETCHER_MAXMEM
|
||||||
|
value: {{ .Values.fetcherMaxMem | default "128Mi" | quote }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/healthz"
|
path: "/healthz"
|
||||||
@@ -248,6 +273,11 @@ spec:
|
|||||||
port: 8888
|
port: 8888
|
||||||
initialDelaySeconds: 35
|
initialDelaySeconds: 35
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: metrics
|
||||||
|
- containerPort: 8888
|
||||||
|
name: http
|
||||||
serviceAccount: fission-svc
|
serviceAccount: fission-svc
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -447,7 +477,8 @@ spec:
|
|||||||
- name: fission-log
|
- name: fission-log
|
||||||
hostPath:
|
hostPath:
|
||||||
path: /var/log/fission
|
path: /var/log/fission
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
---
|
---
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -576,6 +607,7 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
svc: storagesvc
|
svc: storagesvc
|
||||||
|
application: fission-storage
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: storagesvc
|
- name: storagesvc
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: {{ template "fullname" . }}-{{ .Chart.Version }}-{{ randNumeric 3 }}
|
||||||
|
labels:
|
||||||
|
# The "release" convention makes it easy to tie a release to all of the
|
||||||
|
# Kubernetes resources that were created as part of that release.
|
||||||
|
release: "{{ .Release.Name }}"
|
||||||
|
# This makes it easy to audit chart usage.
|
||||||
|
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||||
|
app: {{ template "name" . }}
|
||||||
|
annotations:
|
||||||
|
# This is what defines this resource as a hook. Without this line, the
|
||||||
|
# job is considered part of the release.
|
||||||
|
"helm.sh/hook": pre-upgrade
|
||||||
|
"helm.sh/hook-delete-policy": hook-succeeded
|
||||||
|
spec:
|
||||||
|
backoffLimit: 0
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: {{ template "fullname" . }}
|
||||||
|
labels:
|
||||||
|
release: "{{ .Release.Name }}"
|
||||||
|
app: {{ template "name" . }}
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: pre-upgrade-job
|
||||||
|
image: {{ .Values.preUpgradeChecksImage }}:{{ .Values.imageTag }}
|
||||||
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
|
command: [ "/pre-upgrade-checks" ]
|
||||||
|
args: ["--fn-pod-namespace", "{{ .Values.functionNamespace }}", "--envbuilder-namespace", "{{ .Values.builderNamespace }}"]
|
||||||
|
serviceAccount: fission-svc
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: redis
|
||||||
|
labels:
|
||||||
|
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 6379
|
||||||
|
targetPort: 6379
|
||||||
|
protocol: TCP
|
||||||
|
name: redis
|
||||||
|
selector:
|
||||||
|
app: redis
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: redis
|
||||||
|
labels:
|
||||||
|
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: redis
|
||||||
|
serviceName: redis
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: redis
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: redis
|
||||||
|
image: redis:3.2-alpine
|
||||||
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- containerPort: 6379
|
||||||
|
name: redis
|
||||||
@@ -65,6 +65,7 @@ metadata:
|
|||||||
name: storagesvc
|
name: storagesvc
|
||||||
labels:
|
labels:
|
||||||
svc: storagesvc
|
svc: storagesvc
|
||||||
|
application: fission-storage
|
||||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||||
spec:
|
spec:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ image: fission/fission-bundle
|
|||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
## Fission image version
|
## Fission image version
|
||||||
imageTag: 0.7.1
|
imageTag: 0.9.2
|
||||||
|
|
||||||
## Fission fetcher repository
|
## Fission fetcher repository
|
||||||
fetcherImage: fission/fetcher
|
fetcherImage: fission/fetcher
|
||||||
|
|
||||||
## Fission fetcher image version
|
## Fission fetcher image version
|
||||||
fetcherImageTag: 0.7.1
|
fetcherImageTag: 0.9.2
|
||||||
|
|
||||||
## Port at which Fission controller service should be exposed
|
## Port at which Fission controller service should be exposed
|
||||||
controllerPort: 31313
|
controllerPort: 31313
|
||||||
@@ -48,7 +48,7 @@ enableIstio: false
|
|||||||
logger:
|
logger:
|
||||||
influxdbAdmin: "admin"
|
influxdbAdmin: "admin"
|
||||||
fluentdImage: fission/fluentd
|
fluentdImage: fission/fluentd
|
||||||
fluentdImageTag: 0.7.1
|
fluentdImageTag: 0.9.2
|
||||||
|
|
||||||
## Type of Queue you would like to use
|
## Type of Queue you would like to use
|
||||||
## currently supports nats-streaming, azure-storage-queue
|
## currently supports nats-streaming, azure-storage-queue
|
||||||
@@ -91,3 +91,6 @@ heapster: false
|
|||||||
## This interval configures the frequency at which it runs inside the storagesvc pod.
|
## This interval configures the frequency at which it runs inside the storagesvc pod.
|
||||||
## The value is in minutes.
|
## The value is in minutes.
|
||||||
pruneInterval: 60
|
pruneInterval: 60
|
||||||
|
|
||||||
|
## Fission pre-install/pre-upgrade checks live in this image
|
||||||
|
preUpgradeChecksImage: fission/pre-upgrade-checks
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
name: fission-core
|
name: fission-core
|
||||||
version: 0.7.1
|
version: 0.9.2
|
||||||
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.7.1
|
appVersion: 0.9.2
|
||||||
|
|||||||
@@ -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.7.1/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
|
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.9.2/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.7.1/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
|
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.9.2/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.7.1/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.9.2/fission-cli-windows.exe
|
||||||
|
|
||||||
2. You're ready to use Fission!
|
2. You're ready to use Fission!
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,37 @@ metadata:
|
|||||||
istio-injection: enabled
|
istio-injection: enabled
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: secret-configmap-getter
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- '*'
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
- configmaps
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- watch
|
||||||
|
- list
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: package-getter
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- '*'
|
||||||
|
resources:
|
||||||
|
- packages
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- watch
|
||||||
|
- list
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
@@ -44,21 +75,6 @@ roleRef:
|
|||||||
name: admin
|
name: admin
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
|
||||||
---
|
|
||||||
kind: RoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
|
||||||
metadata:
|
|
||||||
name: fission-function-admin
|
|
||||||
namespace: {{ .Values.functionNamespace }}
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: fission-svc
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: admin
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
@@ -80,20 +96,6 @@ metadata:
|
|||||||
name: fission-fetcher
|
name: fission-fetcher
|
||||||
namespace: {{ .Values.functionNamespace }}
|
namespace: {{ .Values.functionNamespace }}
|
||||||
|
|
||||||
---
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
|
||||||
metadata:
|
|
||||||
name: fission-fetcher-crd
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: fission-fetcher
|
|
||||||
namespace: {{ .Values.functionNamespace }}
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: cluster-admin
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
@@ -101,20 +103,6 @@ metadata:
|
|||||||
name: fission-builder
|
name: fission-builder
|
||||||
namespace: {{ .Values.builderNamespace }}
|
namespace: {{ .Values.builderNamespace }}
|
||||||
|
|
||||||
---
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
|
||||||
metadata:
|
|
||||||
name: fission-builder-crd
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: fission-builder
|
|
||||||
namespace: {{ .Values.builderNamespace }}
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: cluster-admin
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -139,6 +127,10 @@ spec:
|
|||||||
env:
|
env:
|
||||||
- name: FISSION_FUNCTION_NAMESPACE
|
- name: FISSION_FUNCTION_NAMESPACE
|
||||||
value: "{{ .Values.functionNamespace }}"
|
value: "{{ .Values.functionNamespace }}"
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/healthz"
|
path: "/healthz"
|
||||||
@@ -175,6 +167,19 @@ spec:
|
|||||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--routerPort", "8888", "--executorUrl", "http://executor.{{ .Release.Namespace }}"]
|
args: ["--routerPort", "8888", "--executorUrl", "http://executor.{{ .Release.Namespace }}"]
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: ROUTER_ROUND_TRIP_TIMEOUT
|
||||||
|
value: {{ .Values.routerRoundTripTimeout | default "50ms" | quote }}
|
||||||
|
- name: ROUTER_ROUNDTRIP_TIMEOUT_EXPONENT
|
||||||
|
value: {{ .Values.routerRoundTripTimeoutExponent | default 2 | quote }}
|
||||||
|
- name: ROUTER_ROUND_TRIP_KEEP_ALIVE_TIME
|
||||||
|
value: {{ .Values.routerRoundTripKeepAliveTime | default "30s" | quote }}
|
||||||
|
- name: ROUTER_ROUND_TRIP_MAX_RETRIES
|
||||||
|
value: {{ .Values.routerRoundTripMaxRetries | default 10 | quote }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/router-healthz"
|
path: "/router-healthz"
|
||||||
@@ -233,6 +238,14 @@ spec:
|
|||||||
value: "{{ .Values.pullPolicy }}"
|
value: "{{ .Values.pullPolicy }}"
|
||||||
- name: ENABLE_ISTIO
|
- name: ENABLE_ISTIO
|
||||||
value: "{{ .Values.enableIstio }}"
|
value: "{{ .Values.enableIstio }}"
|
||||||
|
- name: FETCHER_MINCPU
|
||||||
|
value: {{ .Values.fetcherMinCpu | default "10m" | quote }}
|
||||||
|
- name: FETCHER_MINMEM
|
||||||
|
value: {{ .Values.fetcherMinMem | default "16Mi" | quote }}
|
||||||
|
- name: FETCHER_MAXCPU
|
||||||
|
value: {{ .Values.fetcherMaxCpu | default "40m" | quote }}
|
||||||
|
- name: FETCHER_MAXMEM
|
||||||
|
value: {{ .Values.fetcherMaxMem | default "128Mi" | quote }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/healthz"
|
path: "/healthz"
|
||||||
@@ -334,6 +347,7 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
svc: storagesvc
|
svc: storagesvc
|
||||||
|
application: fission-storage
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: storagesvc
|
- name: storagesvc
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: {{ template "fullname" . }}-{{ .Chart.Version }}-{{ randNumeric 3 }}
|
||||||
|
labels:
|
||||||
|
# The "release" convention makes it easy to tie a release to all of the
|
||||||
|
# Kubernetes resources that were created as part of that release.
|
||||||
|
release: "{{ .Release.Name }}"
|
||||||
|
# This makes it easy to audit chart usage.
|
||||||
|
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||||
|
app: {{ template "name" . }}
|
||||||
|
annotations:
|
||||||
|
# This is what defines this resource as a hook. Without this line, the
|
||||||
|
# job is considered part of the release.
|
||||||
|
"helm.sh/hook": pre-upgrade
|
||||||
|
"helm.sh/hook-delete-policy": hook-succeeded
|
||||||
|
spec:
|
||||||
|
backoffLimit: 0
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: {{ template "fullname" . }}
|
||||||
|
labels:
|
||||||
|
release: "{{ .Release.Name }}"
|
||||||
|
app: {{ template "name" . }}
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: pre-upgrade-job
|
||||||
|
image: {{ .Values.preUpgradeChecksImage }}:{{ .Values.imageTag }}
|
||||||
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
|
command: [ "/pre-upgrade-checks" ]
|
||||||
|
args: ["--fn-pod-namespace", "{{ .Values.functionNamespace }}", "--envbuilder-namespace", "{{ .Values.builderNamespace }}"]
|
||||||
|
serviceAccount: fission-svc
|
||||||
@@ -44,6 +44,7 @@ metadata:
|
|||||||
name: storagesvc
|
name: storagesvc
|
||||||
labels:
|
labels:
|
||||||
svc: storagesvc
|
svc: storagesvc
|
||||||
|
application: fission-storage
|
||||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||||
spec:
|
spec:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ routerServiceType: LoadBalancer
|
|||||||
image: fission/fission-bundle
|
image: fission/fission-bundle
|
||||||
|
|
||||||
## Fission image version
|
## Fission image version
|
||||||
imageTag: 0.7.1
|
imageTag: 0.9.2
|
||||||
|
|
||||||
## Image pull policy
|
## Image pull policy
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
@@ -22,7 +22,7 @@ pullPolicy: IfNotPresent
|
|||||||
fetcherImage: fission/fetcher
|
fetcherImage: fission/fetcher
|
||||||
|
|
||||||
## Fission fetcher image version
|
## Fission fetcher image version
|
||||||
fetcherImageTag: 0.7.1
|
fetcherImageTag: 0.9.2
|
||||||
|
|
||||||
## Port at which Fission controller service should be exposed
|
## Port at which Fission controller service should be exposed
|
||||||
controllerPort: 31313
|
controllerPort: 31313
|
||||||
@@ -62,3 +62,6 @@ analytics: true
|
|||||||
## This interval configures the frequency at which it runs inside the storagesvc pod.
|
## This interval configures the frequency at which it runs inside the storagesvc pod.
|
||||||
## The value is in minutes.
|
## The value is in minutes.
|
||||||
pruneInterval: 60
|
pruneInterval: 60
|
||||||
|
|
||||||
|
## Fission pre-install/pre-upgrade checks live in this image
|
||||||
|
preUpgradeChecksImage: fission/pre-upgrade-checks
|
||||||
@@ -28,11 +28,15 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
apiv1 "k8s.io/client-go/pkg/api/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UrlForFunction(name string) string {
|
func UrlForFunction(name, namespace string) string {
|
||||||
prefix := "/fission-function"
|
prefix := "/fission-function"
|
||||||
|
if namespace != metav1.NamespaceDefault {
|
||||||
|
prefix = fmt.Sprintf("/fission-function/%s", namespace)
|
||||||
|
}
|
||||||
return fmt.Sprintf("%v/%v", prefix, name)
|
return fmt.Sprintf("%v/%v", prefix, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
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 fission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
rbac "k8s.io/api/rbac/v1beta1"
|
||||||
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/json"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file has util functions needed for setting up and cleaning up RBAC objects.
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxRetries = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// MakeSAObj returns a ServiceAccount object with the given SA name and namespace
|
||||||
|
func MakeSAObj(sa, ns string) *apiv1.ServiceAccount {
|
||||||
|
return &apiv1.ServiceAccount{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: ns,
|
||||||
|
Name: sa,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupSA checks if a service account is present in the namespace, if not creates it.
|
||||||
|
func SetupSA(k8sClient *kubernetes.Clientset, sa, ns string) (*apiv1.ServiceAccount, error) {
|
||||||
|
saObj, err := k8sClient.CoreV1().ServiceAccounts(ns).Get(sa, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
return saObj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if k8serrors.IsNotFound(err) {
|
||||||
|
saObj = MakeSAObj(sa, ns)
|
||||||
|
saObj, err = k8sClient.CoreV1().ServiceAccounts(ns).Create(saObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return saObj, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeRoleBindingObj is a helper function called from other functions in this file only.
|
||||||
|
// given a rolebinging name and namespace, it makes a rolebinding object mapping the role to the SA of the namespace.
|
||||||
|
func makeRoleBindingObj(roleBinding, roleBindingNs, role, roleKind, sa, saNamespace string) *rbac.RoleBinding {
|
||||||
|
return &rbac.RoleBinding{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: roleBinding,
|
||||||
|
Namespace: roleBindingNs,
|
||||||
|
},
|
||||||
|
Subjects: []rbac.Subject{
|
||||||
|
{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
Name: sa,
|
||||||
|
Namespace: saNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RoleRef: rbac.RoleRef{
|
||||||
|
Kind: roleKind,
|
||||||
|
Name: role,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isSAInRoleBinding checkis if a service account is present in the rolebinding object
|
||||||
|
func isSAInRoleBinding(rbObj *rbac.RoleBinding, sa, ns string) bool {
|
||||||
|
for _, subject := range rbObj.Subjects {
|
||||||
|
if subject.Name == sa && subject.Namespace == ns {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PatchSpec is a standard struct needed for JSON merge.
|
||||||
|
type PatchSpec struct {
|
||||||
|
Op string `json:"op"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Value rbac.Subject `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSaToRoleBindingWithRetries adds a service account to a rolebinding object. IT retries on already exists and conflict errors.
|
||||||
|
func AddSaToRoleBindingWithRetries(k8sClient *kubernetes.Clientset, roleBinding, roleBindingNs, sa, saNamespace, role, roleKind string) (err error) {
|
||||||
|
patch := PatchSpec{}
|
||||||
|
patch.Op = "add"
|
||||||
|
patch.Path = "/subjects/-"
|
||||||
|
patch.Value = rbac.Subject{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
Name: sa,
|
||||||
|
Namespace: saNamespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
patchJson, err := json.Marshal([]PatchSpec{patch})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error marshalling patch into json")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < MaxRetries; i++ {
|
||||||
|
_, err = k8sClient.RbacV1beta1().RoleBindings(roleBindingNs).Patch(roleBinding, types.JSONPatchType, patchJson)
|
||||||
|
if err == nil {
|
||||||
|
log.Printf("Patched rolebinding : %s.%s", roleBinding, roleBindingNs)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if k8serrors.IsNotFound(err) {
|
||||||
|
// someone may have deleted the object between us checking if the object is present and deciding to patch
|
||||||
|
// so just create the object again
|
||||||
|
rbObj := makeRoleBindingObj(roleBinding, roleBindingNs, role, roleKind, sa, saNamespace)
|
||||||
|
rbObj, err = k8sClient.RbacV1beta1().RoleBindings(roleBindingNs).Create(rbObj)
|
||||||
|
if err == nil {
|
||||||
|
log.Printf("Created rolebinding : %s.%s", roleBinding, roleBindingNs)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if k8serrors.IsAlreadyExists(err) {
|
||||||
|
log.Printf("rolebinding object : %s.%s already exists, retrying patch, err: %v", roleBinding, roleBindingNs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Error returned by rolebinding create, %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if k8serrors.IsConflict(err) {
|
||||||
|
// TODO : Need to test this out, not able to simulate conflicts yet.
|
||||||
|
// Initially, my understanding was that patch can never error on conflict because Api server will handle the conflicts for us.
|
||||||
|
// but one CI run did show patch errored out on conflict : https://api.travis-ci.org/v3/job/373161490/log.txt, look for :
|
||||||
|
// Error returned by rolebinding patch : <some more text> there is a meaningful conflict (firstResourceVersion: "35482724", currentResourceVersion: "35482849")
|
||||||
|
// so, m guessing retrying patch should help. will watch out for any such conflicts and fix the issue if any
|
||||||
|
log.Printf("Conflict reported on patch of rolebinding : %s.%s. Retrying patch operation, err : %v", roleBinding, roleBindingNs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Error returned by rolebinding patch : %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Exceeded maxretries : %d adding SA: %s.%s to rolebinding : %s.%s, giving up, err: %v", MaxRetries, sa, saNamespace, roleBinding, roleBindingNs, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveSAFromRoleBindingWithRetries removes an SA from the rolebinding passed as parameter. If this is the only SA in
|
||||||
|
// the rolebinding, then it deletes the rolebinding object.
|
||||||
|
func RemoveSAFromRoleBindingWithRetries(k8sClient *kubernetes.Clientset, roleBinding, roleBindingNs string, saToRemove map[string]bool) (err error) {
|
||||||
|
for i := 0; i < MaxRetries; i++ {
|
||||||
|
rbObj, err := k8sClient.RbacV1beta1().RoleBindings(roleBindingNs).Get(
|
||||||
|
roleBinding, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
// silently ignoring the error. there's no need for us to remove sa anymore.
|
||||||
|
log.Printf("rolebinding %s.%s not found, but ignoring the error since we're cleaning up", roleBinding, roleBindingNs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
subjects := rbObj.Subjects
|
||||||
|
newSubjects := make([]rbac.Subject, 0)
|
||||||
|
|
||||||
|
// TODO : optimize it.
|
||||||
|
for _, item := range subjects {
|
||||||
|
if _, ok := saToRemove[MakeSAMapKey(item.Name, item.Namespace)]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newSubjects = append(newSubjects, rbac.Subject{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
Name: item.Name,
|
||||||
|
Namespace: item.Namespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(newSubjects) == 0 {
|
||||||
|
return DeleteRoleBinding(k8sClient, roleBinding, roleBindingNs)
|
||||||
|
}
|
||||||
|
|
||||||
|
rbObj.Subjects = newSubjects
|
||||||
|
|
||||||
|
// cant use patch for deletes, the results become in-deterministic, so using update.
|
||||||
|
_, err = k8sClient.RbacV1beta1().RoleBindings(rbObj.Namespace).Update(rbObj)
|
||||||
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
log.Printf("Removed sa's : %v from rolebinding : %s.%s", saToRemove, roleBinding, roleBindingNs)
|
||||||
|
return nil
|
||||||
|
case k8serrors.IsConflict(err):
|
||||||
|
log.Printf("Conflict in update of rolebinding: %s.%s, retrying", roleBinding, roleBindingNs)
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
log.Printf("Rolebinding Update Errored out : %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Maxretries : %d exceeded for removing SA's : %v from rolebinding %s.%s, giving up, err: %v", MaxRetries, saToRemove, roleBinding, roleBindingNs, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupRoleBinding adds a role to a service account if the rolebinding object is already present in the namespace.
|
||||||
|
// if not, it creates a rolebinding object granting the role to the SA in the namespace.
|
||||||
|
func SetupRoleBinding(k8sClient *kubernetes.Clientset, roleBinding, roleBindingNs, role, roleKind, sa, saNamespace string) error {
|
||||||
|
// get the role binding object
|
||||||
|
rbObj, err := k8sClient.RbacV1beta1().RoleBindings(roleBindingNs).Get(
|
||||||
|
roleBinding, metav1.GetOptions{})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if !isSAInRoleBinding(rbObj, sa, saNamespace) {
|
||||||
|
return AddSaToRoleBindingWithRetries(k8sClient, roleBinding, roleBindingNs, sa, saNamespace, role, roleKind)
|
||||||
|
}
|
||||||
|
log.Printf("SA : %s.%s already present in rolebinding : %s.%s, so nothing to add", sa, saNamespace, roleBinding, roleBindingNs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if role binding is missing, create it. also add this sa to the binding.
|
||||||
|
if k8serrors.IsNotFound(err) {
|
||||||
|
log.Printf("Rolebinding %s does NOT exist in ns %s. Creating it", roleBinding, roleBindingNs)
|
||||||
|
rbObj = makeRoleBindingObj(roleBinding, roleBindingNs, role, roleKind, sa, saNamespace)
|
||||||
|
rbObj, err = k8sClient.RbacV1beta1().RoleBindings(roleBindingNs).Create(rbObj)
|
||||||
|
if k8serrors.IsAlreadyExists(err) {
|
||||||
|
err = AddSaToRoleBindingWithRetries(k8sClient, roleBinding, roleBindingNs, sa, saNamespace, role, roleKind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRoleBinding deletes a rolebinding object. if k8s throws an error that the rolebinding is not there, it just
|
||||||
|
// returns silently.
|
||||||
|
func DeleteRoleBinding(k8sClient *kubernetes.Clientset, roleBinding, roleBindingNs string) error {
|
||||||
|
// if deleteRoleBinding is invoked by 2 fission services at the same time for the same rolebinding,
|
||||||
|
// the first call will succeed while the 2nd will fail with isNotFound. but we dont want to error out then.
|
||||||
|
err := k8sClient.RbacV1beta1().RoleBindings(roleBindingNs).Delete(roleBinding, &metav1.DeleteOptions{})
|
||||||
|
if err == nil || k8serrors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeSAMapKey(saName, saNamespace string) string {
|
||||||
|
return fmt.Sprintf("%s-%s", saName, saNamespace)
|
||||||
|
}
|
||||||
+65
-2
@@ -25,7 +25,9 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
@@ -33,6 +35,15 @@ import (
|
|||||||
"github.com/fission/fission/fission/logdb"
|
"github.com/fission/fission/fission/logdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var podNamespace string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
podNamespace = os.Getenv("POD_NAMESPACE")
|
||||||
|
if podNamespace == "" {
|
||||||
|
podNamespace = "fission"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
API struct {
|
API struct {
|
||||||
fissionClient *crd.FissionClient
|
fissionClient *crd.FissionClient
|
||||||
@@ -109,6 +120,31 @@ func (api *API) respondWithError(w http.ResponseWriter, err error) {
|
|||||||
http.Error(w, msg, code)
|
http.Error(w, msg, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) extractQueryParamFromRequest(r *http.Request, queryParam string) string {
|
||||||
|
values := r.URL.Query()
|
||||||
|
return values.Get(queryParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if namespace exists, if not create it.
|
||||||
|
func (api *API) createNsIfNotExists(ns string) error {
|
||||||
|
if ns == metav1.NamespaceDefault {
|
||||||
|
// we dont have to create default ns
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := api.kubernetesClient.CoreV1().Namespaces().Get(ns, metav1.GetOptions{})
|
||||||
|
if err != nil && kerrors.IsNotFound(err) {
|
||||||
|
ns := &apiv1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: ns,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err = api.kubernetesClient.CoreV1().Namespaces().Create(ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (api *API) getLogDBConfig(dbType string) logDBConfig {
|
func (api *API) getLogDBConfig(dbType string) logDBConfig {
|
||||||
dbType = strings.ToUpper(dbType)
|
dbType = strings.ToUpper(dbType)
|
||||||
// retrieve db auth config from the env
|
// retrieve db auth config from the env
|
||||||
@@ -128,7 +164,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, fission.VersionInfo().String())
|
fmt.Fprintf(w, fission.ApiInfo().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) ApiVersionMismatchHandler(w http.ResponseWriter, r *http.Request) {
|
func (api *API) ApiVersionMismatchHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -140,6 +176,18 @@ func (api *API) HealthHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) GetSvcName(w http.ResponseWriter, r *http.Request) {
|
||||||
|
appLabelSelector := "application=" + r.URL.Query().Get("application")
|
||||||
|
services, err := api.kubernetesClient.CoreV1().Services(podNamespace).List(metav1.ListOptions{
|
||||||
|
LabelSelector: appLabelSelector,
|
||||||
|
})
|
||||||
|
if err != nil || len(services.Items) > 1 || len(services.Items) == 0 {
|
||||||
|
api.respondWithError(w, err)
|
||||||
|
}
|
||||||
|
service := services.Items[0]
|
||||||
|
fmt.Fprintf(w, service.Name+"."+podNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
func (api *API) Serve(port int) {
|
func (api *API) Serve(port int) {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.HandleFunc("/healthz", api.HealthHandler).Methods("GET")
|
r.HandleFunc("/healthz", api.HealthHandler).Methods("GET")
|
||||||
@@ -189,12 +237,27 @@ 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("/v2/recorders", api.RecorderApiList).Methods("GET")
|
||||||
|
r.HandleFunc("/v2/recorders", api.RecorderApiCreate).Methods("POST")
|
||||||
|
r.HandleFunc("/v2/recorders/{recorder}", api.RecorderApiGet).Methods("GET")
|
||||||
|
r.HandleFunc("/v2/recorders/{recorder}", api.RecorderApiUpdate).Methods("PUT")
|
||||||
|
r.HandleFunc("/v2/recorders/{recorder}", api.RecorderApiDelete).Methods("DELETE")
|
||||||
|
|
||||||
|
r.HandleFunc("/v2/records", api.RecordsApiListAll).Methods("GET")
|
||||||
|
r.HandleFunc("/v2/records/function/{function}", api.RecordsApiFilterByFunction).Methods("GET")
|
||||||
|
r.HandleFunc("/v2/records/trigger/{trigger}", api.RecordsApiFilterByTrigger).Methods("GET")
|
||||||
|
r.HandleFunc("/v2/records/time", api.RecordsApiFilterByTime).Methods("GET")
|
||||||
|
|
||||||
|
r.HandleFunc("/v2/replay/{reqUID}", api.ReplayByReqUID).Methods("GET")
|
||||||
|
|
||||||
|
r.HandleFunc("/v2/secrets/{secret}", api.SecretGet).Methods("GET")
|
||||||
|
r.HandleFunc("/v2/configmaps/{configmap}", api.ConfigMapGet).Methods("GET")
|
||||||
|
|
||||||
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/logs/{function}", api.FunctionPodLogs).Methods("POST")
|
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)
|
||||||
|
r.HandleFunc("/proxy/svcname", api.GetSvcName).Queries("application", "").Methods("GET")
|
||||||
|
|
||||||
address := fmt.Sprintf(":%v", port)
|
address := fmt.Sprintf(":%v", port)
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/pkg/api/v1"
|
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/controller/client"
|
"github.com/fission/fission/controller/client"
|
||||||
@@ -122,7 +122,7 @@ func TestFunctionApi(t *testing.T) {
|
|||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.FunctionDelete(m2)
|
defer g.client.FunctionDelete(m2)
|
||||||
|
|
||||||
funcs, err := g.client.FunctionList()
|
funcs, err := g.client.FunctionList(metav1.NamespaceDefault)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
assert(len(funcs) == 2, fmt.Sprintf("created two functions, but found %v", len(funcs)))
|
assert(len(funcs) == 2, fmt.Sprintf("created two functions, but found %v", len(funcs)))
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ func TestHTTPTriggerApi(t *testing.T) {
|
|||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.HTTPTriggerDelete(m2)
|
defer g.client.HTTPTriggerDelete(m2)
|
||||||
|
|
||||||
ts, err := g.client.HTTPTriggerList()
|
ts, err := g.client.HTTPTriggerList(metav1.NamespaceDefault)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
assert(len(ts) == 2, fmt.Sprintf("created two triggers, but found %v", len(ts)))
|
assert(len(ts) == 2, fmt.Sprintf("created two triggers, but found %v", len(ts)))
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ func TestEnvironmentApi(t *testing.T) {
|
|||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.EnvironmentDelete(m2)
|
defer g.client.EnvironmentDelete(m2)
|
||||||
|
|
||||||
ts, err := g.client.EnvironmentList()
|
ts, err := g.client.EnvironmentList(metav1.NamespaceDefault)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
assert(len(ts) == 2, fmt.Sprintf("created two envs, but found %v", len(ts)))
|
assert(len(ts) == 2, fmt.Sprintf("created two envs, but found %v", len(ts)))
|
||||||
}
|
}
|
||||||
@@ -279,7 +279,7 @@ func TestWatchApi(t *testing.T) {
|
|||||||
panicIf(err)
|
panicIf(err)
|
||||||
defer g.client.WatchDelete(m2)
|
defer g.client.WatchDelete(m2)
|
||||||
|
|
||||||
ws, err := g.client.WatchList()
|
ws, err := g.client.WatchList(metav1.NamespaceDefault)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
assert(len(ws) == 2, fmt.Sprintf("created two watches, but found %v", len(ws)))
|
assert(len(ws) == 2, fmt.Sprintf("created two watches, but found %v", len(ws)))
|
||||||
}
|
}
|
||||||
@@ -323,7 +323,7 @@ func TestTimeTriggerApi(t *testing.T) {
|
|||||||
_, err = g.client.TimeTriggerCreate(testTrigger)
|
_, err = g.client.TimeTriggerCreate(testTrigger)
|
||||||
assertCronSpecFails(err)
|
assertCronSpecFails(err)
|
||||||
|
|
||||||
ts, err := g.client.TimeTriggerList()
|
ts, err := g.client.TimeTriggerList(metav1.NamespaceDefault)
|
||||||
panicIf(err)
|
panicIf(err)
|
||||||
assert(len(ts) == 1, fmt.Sprintf("created two time triggers, but found %v", len(ts)))
|
assert(len(ts) == 1, fmt.Sprintf("created two time triggers, but found %v", len(ts)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -88,3 +90,25 @@ func (c *Client) handleCreateResponse(resp *http.Response) ([]byte, error) {
|
|||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
return body, err
|
return body, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) ServerInfo() (*fission.ServerInfo, error) {
|
||||||
|
url := fmt.Sprintf(c.Url)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := &fission.ServerInfo{}
|
||||||
|
err = json.Unmarshal(body, info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Fission Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) SecretGet(m *metav1.ObjectMeta) (*apiv1.Secret, error) {
|
||||||
|
relativeUrl := fmt.Sprintf("secrets/%v", m.Name)
|
||||||
|
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var secret apiv1.Secret
|
||||||
|
err = json.Unmarshal(body, &secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &secret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ConfigMapGet(m *metav1.ObjectMeta) (*apiv1.ConfigMap, error) {
|
||||||
|
relativeUrl := fmt.Sprintf("configmaps/%v", m.Name)
|
||||||
|
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var configMap apiv1.ConfigMap
|
||||||
|
err = json.Unmarshal(body, &configMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &configMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetSvcURL(label string) (string, error) {
|
||||||
|
url := fmt.Sprintf("%s/proxy/svcname?"+label, c.Url)
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp == nil {
|
||||||
|
return "", fmt.Errorf("Failed to find service for given label: %v", label)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
storageSvc := string(body)
|
||||||
|
|
||||||
|
return storageSvc, err
|
||||||
|
}
|
||||||
@@ -24,14 +24,14 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) EnvironmentCreate(env *crd.Environment) (*metav1.ObjectMeta, error) {
|
func (c *Client) EnvironmentCreate(env *crd.Environment) (*metav1.ObjectMeta, error) {
|
||||||
err := env.Validate()
|
err := env.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("Environment", err)
|
return nil, fv1.AggregateValidationErrors("Environment", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(env)
|
reqbody, err := json.Marshal(env)
|
||||||
@@ -86,7 +86,7 @@ func (c *Client) EnvironmentGet(m *metav1.ObjectMeta) (*crd.Environment, error)
|
|||||||
func (c *Client) EnvironmentUpdate(env *crd.Environment) (*metav1.ObjectMeta, error) {
|
func (c *Client) EnvironmentUpdate(env *crd.Environment) (*metav1.ObjectMeta, error) {
|
||||||
err := env.Validate()
|
err := env.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("Environment", err)
|
return nil, fv1.AggregateValidationErrors("Environment", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(env)
|
reqbody, err := json.Marshal(env)
|
||||||
@@ -121,8 +121,9 @@ func (c *Client) EnvironmentDelete(m *metav1.ObjectMeta) error {
|
|||||||
return c.delete(relativeUrl)
|
return c.delete(relativeUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) EnvironmentList() ([]crd.Environment, error) {
|
func (c *Client) EnvironmentList(ns string) ([]crd.Environment, error) {
|
||||||
resp, err := http.Get(c.url("environments"))
|
relativeUrl := fmt.Sprintf("environments?namespace=%v", ns)
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) FunctionCreate(f *crd.Function) (*metav1.ObjectMeta, error) {
|
func (c *Client) FunctionCreate(f *crd.Function) (*metav1.ObjectMeta, error) {
|
||||||
err := f.Validate()
|
err := f.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("Function", err)
|
return nil, fv1.AggregateValidationErrors("Function", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(f)
|
reqbody, err := json.Marshal(f)
|
||||||
@@ -100,7 +100,7 @@ func (c *Client) FunctionGetRawDeployment(m *metav1.ObjectMeta) ([]byte, error)
|
|||||||
func (c *Client) FunctionUpdate(f *crd.Function) (*metav1.ObjectMeta, error) {
|
func (c *Client) FunctionUpdate(f *crd.Function) (*metav1.ObjectMeta, error) {
|
||||||
err := f.Validate()
|
err := f.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("Function", err)
|
return nil, fv1.AggregateValidationErrors("Function", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(f)
|
reqbody, err := json.Marshal(f)
|
||||||
@@ -134,8 +134,9 @@ func (c *Client) FunctionDelete(m *metav1.ObjectMeta) error {
|
|||||||
return c.delete(relativeUrl)
|
return c.delete(relativeUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) FunctionList() ([]crd.Function, error) {
|
func (c *Client) FunctionList(functionNamespace string) ([]crd.Function, error) {
|
||||||
resp, err := http.Get(c.url("functions"))
|
relativeUrl := fmt.Sprintf("functions?namespace=%v", functionNamespace)
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) HTTPTriggerCreate(t *crd.HTTPTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) HTTPTriggerCreate(t *crd.HTTPTrigger) (*metav1.ObjectMeta, error) {
|
||||||
err := t.Validate()
|
err := t.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("HTTPTrigger", err)
|
return nil, fv1.AggregateValidationErrors("HTTPTrigger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
@@ -86,7 +86,7 @@ func (c *Client) HTTPTriggerGet(m *metav1.ObjectMeta) (*crd.HTTPTrigger, error)
|
|||||||
func (c *Client) HTTPTriggerUpdate(t *crd.HTTPTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) HTTPTriggerUpdate(t *crd.HTTPTrigger) (*metav1.ObjectMeta, error) {
|
||||||
err := t.Validate()
|
err := t.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("HTTPTrigger", err)
|
return nil, fv1.AggregateValidationErrors("HTTPTrigger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
@@ -120,8 +120,9 @@ func (c *Client) HTTPTriggerDelete(m *metav1.ObjectMeta) error {
|
|||||||
return c.delete(relativeUrl)
|
return c.delete(relativeUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) HTTPTriggerList() ([]crd.HTTPTrigger, error) {
|
func (c *Client) HTTPTriggerList(triggerNamespace string) ([]crd.HTTPTrigger, error) {
|
||||||
resp, err := http.Get(c.url("triggers/http"))
|
relativeUrl := fmt.Sprintf("triggers/http?namespace=%v", triggerNamespace)
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,13 @@ import (
|
|||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) WatchCreate(w *crd.KubernetesWatchTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) WatchCreate(w *crd.KubernetesWatchTrigger) (*metav1.ObjectMeta, error) {
|
||||||
err := w.Validate()
|
err := w.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("KubernetesWatchTrigger", err)
|
return nil, fv1.AggregateValidationErrors("KubernetesWatchTrigger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(w)
|
reqbody, err := json.Marshal(w)
|
||||||
@@ -94,8 +95,9 @@ func (c *Client) WatchDelete(m *metav1.ObjectMeta) error {
|
|||||||
return c.delete(relativeUrl)
|
return c.delete(relativeUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) WatchList() ([]crd.KubernetesWatchTrigger, error) {
|
func (c *Client) WatchList(ns string) ([]crd.KubernetesWatchTrigger, error) {
|
||||||
resp, err := http.Get(c.url("watches"))
|
relativeUrl := fmt.Sprintf("watches?namespace=%v", ns)
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) MessageQueueTriggerCreate(t *crd.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) MessageQueueTriggerCreate(t *crd.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
|
||||||
err := t.Validate()
|
err := t.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("MessageQueueTrigger", err)
|
return nil, fv1.AggregateValidationErrors("MessageQueueTrigger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
@@ -86,7 +86,7 @@ func (c *Client) MessageQueueTriggerGet(m *metav1.ObjectMeta) (*crd.MessageQueue
|
|||||||
func (c *Client) MessageQueueTriggerUpdate(mqTrigger *crd.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) MessageQueueTriggerUpdate(mqTrigger *crd.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
|
||||||
err := mqTrigger.Validate()
|
err := mqTrigger.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("MessageQueueTrigger", err)
|
return nil, fv1.AggregateValidationErrors("MessageQueueTrigger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(mqTrigger)
|
reqbody, err := json.Marshal(mqTrigger)
|
||||||
@@ -120,11 +120,11 @@ func (c *Client) MessageQueueTriggerDelete(m *metav1.ObjectMeta) error {
|
|||||||
return c.delete(relativeUrl)
|
return c.delete(relativeUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) MessageQueueTriggerList(mqType string) ([]crd.MessageQueueTrigger, error) {
|
func (c *Client) MessageQueueTriggerList(mqType string, ns 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
|
||||||
relativeUrl += fmt.Sprintf("?mqtype=%v", mqType)
|
relativeUrl += fmt.Sprintf("?mqtype=%v&namespace=%v", mqType, ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.Get(c.url(relativeUrl))
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) PackageCreate(f *crd.Package) (*metav1.ObjectMeta, error) {
|
func (c *Client) PackageCreate(f *crd.Package) (*metav1.ObjectMeta, error) {
|
||||||
err := f.Validate()
|
err := f.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("Package", err)
|
return nil, fv1.AggregateValidationErrors("Package", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(f)
|
reqbody, err := json.Marshal(f)
|
||||||
@@ -86,7 +86,7 @@ func (c *Client) PackageGet(m *metav1.ObjectMeta) (*crd.Package, error) {
|
|||||||
func (c *Client) PackageUpdate(f *crd.Package) (*metav1.ObjectMeta, error) {
|
func (c *Client) PackageUpdate(f *crd.Package) (*metav1.ObjectMeta, error) {
|
||||||
err := f.Validate()
|
err := f.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("Package", err)
|
return nil, fv1.AggregateValidationErrors("Package", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(f)
|
reqbody, err := json.Marshal(f)
|
||||||
@@ -120,8 +120,9 @@ func (c *Client) PackageDelete(m *metav1.ObjectMeta) error {
|
|||||||
return c.delete(relativeUrl)
|
return c.delete(relativeUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PackageList() ([]crd.Package, error) {
|
func (c *Client) PackageList(pkgNamespace string) ([]crd.Package, error) {
|
||||||
resp, err := http.Get(c.url("packages"))
|
relativeUrl := fmt.Sprintf("packages?namespace=%v", pkgNamespace)
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Fission Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/fission/fission/crd"
|
||||||
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
|
"github.com/fission/fission/redis/build/gen"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) RecorderCreate(r *crd.Recorder) (*metav1.ObjectMeta, error) {
|
||||||
|
err := r.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fv1.AggregateValidationErrors("Recorder", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reqbody, err := json.Marshal(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Post(c.url("recorders"), "application/json", bytes.NewReader(reqbody))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleCreateResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var m metav1.ObjectMeta
|
||||||
|
err = json.Unmarshal(body, &m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RecorderGet(m *metav1.ObjectMeta) (*crd.Recorder, error) {
|
||||||
|
relativeUrl := fmt.Sprintf("recorders/%v", m.Name)
|
||||||
|
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var r crd.Recorder
|
||||||
|
err = json.Unmarshal(body, &r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RecorderUpdate(recorder *crd.Recorder) (*metav1.ObjectMeta, error) {
|
||||||
|
err := recorder.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fv1.AggregateValidationErrors("Recorder", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reqbody, err := json.Marshal(recorder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
relativeUrl := fmt.Sprintf("recorders/%v", recorder.Metadata.Name)
|
||||||
|
|
||||||
|
resp, err := c.put(relativeUrl, "application/json", reqbody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var m metav1.ObjectMeta
|
||||||
|
err = json.Unmarshal(body, &m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RecorderDelete(m *metav1.ObjectMeta) error {
|
||||||
|
relativeUrl := fmt.Sprintf("recorders/%v", m.Name)
|
||||||
|
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||||
|
return c.delete(relativeUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RecorderList(ns string) ([]crd.Recorder, error) {
|
||||||
|
relativeUrl := "recorders"
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
recorders := make([]crd.Recorder, 0)
|
||||||
|
err = json.Unmarshal(body, &recorders)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return recorders, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move to different file?
|
||||||
|
func (c *Client) RecordsByFunction(function string) ([]*redisCache.RecordedEntry, error) {
|
||||||
|
relativeUrl := fmt.Sprintf("records/function/%v", function)
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
records := make([]*redisCache.RecordedEntry, 0)
|
||||||
|
err = json.Unmarshal(body, &records)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RecordsAll() ([]*redisCache.RecordedEntry, error) {
|
||||||
|
relativeUrl := "records"
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
records := make([]*redisCache.RecordedEntry, 0)
|
||||||
|
err = json.Unmarshal(body, &records)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RecordsByTrigger(trigger string) ([]*redisCache.RecordedEntry, error) {
|
||||||
|
relativeUrl := fmt.Sprintf("records/trigger/%v", trigger)
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
records := make([]*redisCache.RecordedEntry, 0)
|
||||||
|
err = json.Unmarshal(body, &records)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RecordsByTime(from string, to string) ([]*redisCache.RecordedEntry, error) {
|
||||||
|
relativeUrl := "records/time"
|
||||||
|
relativeUrl += fmt.Sprintf("?from=%v&to=%v", from, to)
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
records := make([]*redisCache.RecordedEntry, 0)
|
||||||
|
err = json.Unmarshal(body, &records)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Fission Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) ReplayByReqUID(reqUID string) ([]string, error) {
|
||||||
|
relativeUrl := fmt.Sprintf("replay/%v", reqUID)
|
||||||
|
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := c.handleResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
replayed := make([]string, 0)
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &replayed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return replayed, nil
|
||||||
|
}
|
||||||
@@ -24,14 +24,14 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) TimeTriggerCreate(t *crd.TimeTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) TimeTriggerCreate(t *crd.TimeTrigger) (*metav1.ObjectMeta, error) {
|
||||||
err := t.Validate()
|
err := t.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("TimeTrigger", err)
|
return nil, fv1.AggregateValidationErrors("TimeTrigger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
@@ -86,7 +86,7 @@ func (c *Client) TimeTriggerGet(m *metav1.ObjectMeta) (*crd.TimeTrigger, error)
|
|||||||
func (c *Client) TimeTriggerUpdate(t *crd.TimeTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) TimeTriggerUpdate(t *crd.TimeTrigger) (*metav1.ObjectMeta, error) {
|
||||||
err := t.Validate()
|
err := t.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fission.AggregateValidationErrors("TimeTrigger", err)
|
return nil, fv1.AggregateValidationErrors("TimeTrigger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
@@ -120,8 +120,9 @@ func (c *Client) TimeTriggerDelete(m *metav1.ObjectMeta) error {
|
|||||||
return c.delete(relativeUrl)
|
return c.delete(relativeUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) TimeTriggerList() ([]crd.TimeTrigger, error) {
|
func (c *Client) TimeTriggerList(ns string) ([]crd.TimeTrigger, error) {
|
||||||
resp, err := http.Get(c.url("triggers/time"))
|
relativeUrl := fmt.Sprintf("triggers/time?namespace=%v", ns)
|
||||||
|
resp, err := http.Get(c.url(relativeUrl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Fission Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *API) ConfigMapGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
name := vars["configmap"]
|
||||||
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
configMap, err := a.kubernetesClient.CoreV1().ConfigMaps(ns).Get(name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting config map: %s from ns: %s", name, ns)
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := json.Marshal(configMap)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
@@ -30,7 +30,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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(metav1.NamespaceAll).List(metav1.ListOptions{})
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceAll
|
||||||
|
}
|
||||||
|
|
||||||
|
envs, err := a.fissionClient.Environments(ns).List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
@@ -60,6 +65,13 @@ func (a *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if namespace exists, if not create it.
|
||||||
|
err = a.createNsIfNotExists(env.Metadata.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Create(&env)
|
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Create(&env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
@@ -79,7 +91,8 @@ func (a *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["environment"]
|
name := vars["environment"]
|
||||||
ns := vars["namespace"]
|
|
||||||
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
@@ -140,7 +153,8 @@ func (a *API) EnvironmentApiUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) EnvironmentApiDelete(w http.ResponseWriter, r *http.Request) {
|
func (a *API) EnvironmentApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["environment"]
|
name := vars["environment"]
|
||||||
ns := vars["namespace"]
|
|
||||||
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/pkg/api"
|
|
||||||
apiv1 "k8s.io/client-go/pkg/api/v1"
|
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
@@ -44,7 +43,12 @@ func (a *API) getIstioServiceLabels(fnName string) map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(metav1.NamespaceAll).List(metav1.ListOptions{})
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceAll
|
||||||
|
}
|
||||||
|
|
||||||
|
funcs, err := a.fissionClient.Functions(ns).List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
@@ -73,6 +77,13 @@ func (a *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if namespace exists, if not create it.
|
||||||
|
err = a.createNsIfNotExists(f.Metadata.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Create(&f)
|
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Create(&f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
@@ -92,7 +103,7 @@ func (a *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["function"]
|
name := vars["function"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
@@ -151,7 +162,7 @@ func (a *API) FunctionApiUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
|
func (a *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["function"]
|
name := vars["function"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
@@ -205,7 +216,7 @@ func (a *API) FunctionPodLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
ns = "fission-function"
|
ns = "fission-function"
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := a.fissionClient.Functions(api.NamespaceDefault).Get(fnName)
|
f, err := a.fissionClient.Functions(ns).Get(fnName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -30,7 +30,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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(metav1.NamespaceAll).List(metav1.ListOptions{})
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceAll
|
||||||
|
}
|
||||||
|
|
||||||
|
triggers, err := a.fissionClient.HTTPTriggers(ns).List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
@@ -81,6 +86,13 @@ func (a *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if namespace exists, if not create it.
|
||||||
|
err = a.createNsIfNotExists(t.Metadata.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
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)
|
||||||
@@ -100,7 +112,7 @@ func (a *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["httpTrigger"]
|
name := vars["httpTrigger"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
@@ -166,7 +178,7 @@ func (a *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) HTTPTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
func (a *API) HTTPTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["httpTrigger"]
|
name := vars["httpTrigger"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,12 @@ import (
|
|||||||
|
|
||||||
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(metav1.NamespaceAll).List(metav1.ListOptions{})
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceAll
|
||||||
|
}
|
||||||
|
|
||||||
|
triggers, err := a.fissionClient.MessageQueueTriggers(ns).List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
@@ -57,6 +62,13 @@ func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if namespace exists, if not create it.
|
||||||
|
err = a.createNsIfNotExists(mqTrigger.Metadata.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
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)
|
||||||
@@ -75,7 +87,7 @@ func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Reques
|
|||||||
func (a *API) MessageQueueTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) MessageQueueTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["mqTrigger"]
|
name := vars["mqTrigger"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
@@ -133,7 +145,7 @@ func (a *API) MessageQueueTriggerApiUpdate(w http.ResponseWriter, r *http.Reques
|
|||||||
func (a *API) MessageQueueTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
func (a *API) MessageQueueTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["mqTrigger"]
|
name := vars["mqTrigger"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
@@ -29,7 +31,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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(metav1.NamespaceAll).List(metav1.ListOptions{})
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceAll
|
||||||
|
}
|
||||||
|
funcs, err := a.fissionClient.Packages(ns).List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
@@ -59,13 +65,22 @@ func (a *API) PackageApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure size limits
|
// Ensure size limits
|
||||||
if len(f.Spec.Source.Literal) > 256*1024 {
|
if len(f.Spec.Source.Literal) > int(fission.ArchiveLiteralSizeLimit) {
|
||||||
err := fission.MakeError(fission.ErrorInvalidArgument, "Package literal larger than 256K")
|
err := fission.MakeError(fission.ErrorInvalidArgument,
|
||||||
|
fmt.Sprintf("Package literal larger than %s", humanize.Bytes(uint64(fission.ArchiveLiteralSizeLimit))))
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(f.Spec.Deployment.Literal) > 256*1024 {
|
if len(f.Spec.Deployment.Literal) > int(fission.ArchiveLiteralSizeLimit) {
|
||||||
err := fission.MakeError(fission.ErrorInvalidArgument, "Package literal larger than 256K")
|
err := fission.MakeError(fission.ErrorInvalidArgument,
|
||||||
|
fmt.Sprintf("Package literal larger than %s", humanize.Bytes(uint64(fission.ArchiveLiteralSizeLimit))))
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if namespace exists, if not create it.
|
||||||
|
err = a.createNsIfNotExists(f.Metadata.Namespace)
|
||||||
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -89,7 +104,7 @@ func (a *API) PackageApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) PackageApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) PackageApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["package"]
|
name := vars["package"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
@@ -154,7 +169,7 @@ func (a *API) PackageApiUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) PackageApiDelete(w http.ResponseWriter, r *http.Request) {
|
func (a *API) PackageApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["package"]
|
name := vars["package"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Fission Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/fission/fission"
|
||||||
|
"github.com/fission/fission/crd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *API) RecorderApiList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
recorders, err := a.fissionClient.Recorders(metav1.NamespaceAll).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := json.Marshal(recorders.Items)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) RecorderApiCreate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var recorder crd.Recorder
|
||||||
|
err = json.Unmarshal(body, &recorder)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tnew, err := a.fissionClient.Recorders(recorder.Metadata.Namespace).Create(&recorder)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := json.Marshal(tnew.Metadata)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) RecorderApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
name := vars["recorder"]
|
||||||
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
recorder, err := a.fissionClient.Recorders(ns).Get(name)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := json.Marshal(recorder)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) RecorderApiUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
name := vars["recorder"]
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var recorder crd.Recorder
|
||||||
|
err = json.Unmarshal(body, &recorder)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != recorder.Metadata.Name {
|
||||||
|
err = fission.MakeError(fission.ErrorInvalidArgument, "Recorder name doesn't match URL")
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rnew, err := a.fissionClient.Recorders(recorder.Metadata.Namespace).Update(&recorder)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := json.Marshal(rnew.Metadata)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) RecorderApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
name := vars["recorder"]
|
||||||
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
err := a.fissionClient.Recorders(ns).Delete(name, &metav1.DeleteOptions{})
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, []byte(""))
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/fission/fission/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *API) RecordsApiListAll(w http.ResponseWriter, r *http.Request) {
|
||||||
|
resp, err := redis.RecordsListAll()
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) RecordsApiFilterByFunction(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
query := vars["function"]
|
||||||
|
|
||||||
|
recorders, err := a.fissionClient.Recorders(metav1.NamespaceAll).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
triggers, err := a.fissionClient.HTTPTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := redis.RecordsFilterByFunction(query, recorders, triggers)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) RecordsApiFilterByTrigger(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
query := vars["trigger"]
|
||||||
|
|
||||||
|
recorders, err := a.fissionClient.Recorders(metav1.NamespaceAll).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
triggers, err := a.fissionClient.HTTPTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := redis.RecordsFilterByTrigger(query, recorders, triggers)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) RecordsApiFilterByTime(w http.ResponseWriter, r *http.Request) {
|
||||||
|
from := r.FormValue("from")
|
||||||
|
to := r.FormValue("to")
|
||||||
|
|
||||||
|
resp, err := redis.RecordsFilterByTime(from, to)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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 (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"github.com/fission/fission/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *API) ReplayByReqUID(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
queriedID := vars["reqUID"]
|
||||||
|
|
||||||
|
routerUrl := fmt.Sprintf("http://router.%v", podNamespace)
|
||||||
|
|
||||||
|
resp, err := redis.ReplayByReqUID(routerUrl, queriedID)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Fission Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *API) SecretGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
name := vars["secret"]
|
||||||
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := a.kubernetesClient.CoreV1().Secrets(ns).Get(name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting secret: %s from ns: %s", name, ns)
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := json.Marshal(secret)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.respondWithSuccess(w, resp)
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ func (api *API) StorageServiceProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("Error parsing url %v: %v", u, err)
|
msg := fmt.Sprintf("Error parsing url %v: %v", u, err)
|
||||||
log.Println(msg)
|
log.Println(msg)
|
||||||
http.Error(w, msg, 500)
|
http.Error(w, msg, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
director := func(req *http.Request) {
|
director := func(req *http.Request) {
|
||||||
|
|||||||
@@ -30,7 +30,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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(metav1.NamespaceAll).List(metav1.ListOptions{})
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceAll
|
||||||
|
}
|
||||||
|
|
||||||
|
triggers, err := a.fissionClient.TimeTriggers(ns).List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
@@ -67,6 +72,13 @@ func (a *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if namespace exists, if not create it.
|
||||||
|
err = a.createNsIfNotExists(t.Metadata.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
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)
|
||||||
@@ -86,7 +98,7 @@ func (a *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["timeTrigger"]
|
name := vars["timeTrigger"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
@@ -153,7 +165,7 @@ func (a *API) TimeTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) TimeTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
func (a *API) TimeTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["timeTrigger"]
|
name := vars["timeTrigger"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
+15
-3
@@ -29,7 +29,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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(metav1.NamespaceAll).List(metav1.ListOptions{})
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = metav1.NamespaceAll
|
||||||
|
}
|
||||||
|
|
||||||
|
watches, err := a.fissionClient.KubernetesWatchTriggers(ns).List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.respondWithError(w, err)
|
a.respondWithError(w, err)
|
||||||
return
|
return
|
||||||
@@ -59,6 +64,13 @@ func (a *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO check for duplicate watches
|
// TODO check for duplicate watches
|
||||||
|
// TODO check for duplicate watches -> we probably wont need it?
|
||||||
|
// check if namespace exists, if not create it.
|
||||||
|
err = a.createNsIfNotExists(watch.Metadata.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
a.respondWithError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@@ -79,7 +91,7 @@ func (a *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
|
func (a *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["watch"]
|
name := vars["watch"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
@@ -107,7 +119,7 @@ func (a *API) WatchApiUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *API) WatchApiDelete(w http.ResponseWriter, r *http.Request) {
|
func (a *API) WatchApiDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["watch"]
|
name := vars["watch"]
|
||||||
ns := vars["namespace"]
|
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
ns = metav1.NamespaceDefault
|
ns = metav1.NamespaceDefault
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func (api *API) WorkflowApiserverProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
ssUrl, err := url.Parse(u)
|
ssUrl, err := url.Parse(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("Error parsing url %v: %v", u, err)
|
msg := fmt.Sprintf("Error parsing url %v: %v", u, err)
|
||||||
http.Error(w, msg, 500)
|
http.Error(w, msg, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -153,6 +153,13 @@ func configureClient(config *rest.Config) {
|
|||||||
&metav1.ListOptions{},
|
&metav1.ListOptions{},
|
||||||
&metav1.DeleteOptions{},
|
&metav1.DeleteOptions{},
|
||||||
)
|
)
|
||||||
|
scheme.AddKnownTypes(
|
||||||
|
groupversion,
|
||||||
|
&Recorder{},
|
||||||
|
&RecorderList{},
|
||||||
|
&metav1.ListOptions{},
|
||||||
|
&metav1.DeleteOptions{},
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
schemeBuilder.AddToScheme(scheme.Scheme)
|
schemeBuilder.AddToScheme(scheme.Scheme)
|
||||||
@@ -208,10 +215,12 @@ func (fc *FissionClient) TimeTriggers(ns string) TimeTriggerInterface {
|
|||||||
func (fc *FissionClient) MessageQueueTriggers(ns string) MessageQueueTriggerInterface {
|
func (fc *FissionClient) MessageQueueTriggers(ns string) MessageQueueTriggerInterface {
|
||||||
return MakeMessageQueueTriggerInterface(fc.crdClient, ns)
|
return MakeMessageQueueTriggerInterface(fc.crdClient, ns)
|
||||||
}
|
}
|
||||||
|
func (fc *FissionClient) Recorders(ns string) RecorderInterface {
|
||||||
|
return MakeRecorderInterface(fc.crdClient, ns)
|
||||||
|
}
|
||||||
func (fc *FissionClient) Packages(ns string) PackageInterface {
|
func (fc *FissionClient) Packages(ns string) PackageInterface {
|
||||||
return MakePackageInterface(fc.crdClient, ns)
|
return MakePackageInterface(fc.crdClient, ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fc *FissionClient) WaitForCRDs() error {
|
func (fc *FissionClient) WaitForCRDs() error {
|
||||||
return waitForCRDs(fc.crdClient)
|
return waitForCRDs(fc.crdClient)
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -160,6 +160,22 @@ func EnsureFissionCRDs(clientset *apiextensionsclient.Clientset) error {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Recorders
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "recorders.fission.io",
|
||||||
|
},
|
||||||
|
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||||
|
Group: crdGroupName,
|
||||||
|
Version: crdVersion,
|
||||||
|
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||||
|
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||||
|
Kind: "Recorder",
|
||||||
|
Plural: "recorders",
|
||||||
|
Singular: "recorder",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
// Packages: archives containing source or binaries for one or more functions
|
// Packages: archives containing source or binaries for one or more functions
|
||||||
{
|
{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
|||||||
+121
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
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/watch"
|
||||||
|
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
RecorderInterface interface {
|
||||||
|
Create(*Recorder) (*Recorder, error)
|
||||||
|
Get(name string) (*Recorder, error)
|
||||||
|
Update(*Recorder) (*Recorder, error)
|
||||||
|
Delete(name string, opts *metav1.DeleteOptions) error
|
||||||
|
List(opts metav1.ListOptions) (*RecorderList, error)
|
||||||
|
Watch(opts metav1.ListOptions) (watch.Interface, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
recorderClient struct {
|
||||||
|
client *rest.RESTClient
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeRecorderInterface(crdClient *rest.RESTClient, namespace string) RecorderInterface {
|
||||||
|
return &recorderClient{
|
||||||
|
client: crdClient,
|
||||||
|
namespace: namespace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *recorderClient) Create(r *Recorder) (*Recorder, error) {
|
||||||
|
var result Recorder
|
||||||
|
err := rc.client.Post().
|
||||||
|
Resource("recorders").
|
||||||
|
Namespace("default").
|
||||||
|
Body(r).
|
||||||
|
Do().Into(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *recorderClient) Get(name string) (*Recorder, error) {
|
||||||
|
var result Recorder
|
||||||
|
err := rc.client.Get().
|
||||||
|
Resource("recorders").
|
||||||
|
Namespace(rc.namespace).
|
||||||
|
Name(name).
|
||||||
|
Do().Into(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *recorderClient) Update(r *Recorder) (*Recorder, error) {
|
||||||
|
var result Recorder
|
||||||
|
err := rc.client.Put().
|
||||||
|
Resource("recorders").
|
||||||
|
Namespace(rc.namespace).
|
||||||
|
Name(r.Metadata.Name).
|
||||||
|
Body(r).
|
||||||
|
Do().Into(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *recorderClient) Delete(name string, opts *metav1.DeleteOptions) error {
|
||||||
|
return rc.client.Delete().
|
||||||
|
Namespace(rc.namespace).
|
||||||
|
Resource("recorders").
|
||||||
|
Name(name).
|
||||||
|
Body(opts).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *recorderClient) List(opts metav1.ListOptions) (*RecorderList, error) {
|
||||||
|
var result RecorderList
|
||||||
|
err := rc.client.Get().
|
||||||
|
Namespace(rc.namespace).
|
||||||
|
Resource("recorders").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Do().
|
||||||
|
Into(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *recorderClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
|
||||||
|
return rc.client.Get().
|
||||||
|
Prefix("watch").
|
||||||
|
Namespace(rc.namespace).
|
||||||
|
Resource("recorders").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Watch()
|
||||||
|
}
|
||||||
+17
-338
@@ -17,345 +17,24 @@ limitations under the License.
|
|||||||
package crd
|
package crd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hashicorp/go-multierror"
|
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||||
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 (
|
type (
|
||||||
// Packages. Think of these as function-level images.
|
Package = fv1.Package
|
||||||
Package struct {
|
PackageList = fv1.PackageList
|
||||||
metav1.TypeMeta `json:",inline"`
|
Function = fv1.Function
|
||||||
Metadata metav1.ObjectMeta `json:"metadata"`
|
FunctionList = fv1.FunctionList
|
||||||
Spec fission.PackageSpec `json:"spec"`
|
Environment = fv1.Environment
|
||||||
|
EnvironmentList = fv1.EnvironmentList
|
||||||
Status fission.PackageStatus `json:"status"`
|
HTTPTrigger = fv1.HTTPTrigger
|
||||||
}
|
HTTPTriggerList = fv1.HTTPTriggerList
|
||||||
PackageList struct {
|
KubernetesWatchTrigger = fv1.KubernetesWatchTrigger
|
||||||
metav1.TypeMeta `json:",inline"`
|
KubernetesWatchTriggerList = fv1.KubernetesWatchTriggerList
|
||||||
Metadata metav1.ListMeta `json:"metadata"`
|
TimeTrigger = fv1.TimeTrigger
|
||||||
|
TimeTriggerList = fv1.TimeTriggerList
|
||||||
Items []Package `json:"items"`
|
MessageQueueTrigger = fv1.MessageQueueTrigger
|
||||||
}
|
MessageQueueTriggerList = fv1.MessageQueueTriggerList
|
||||||
|
Recorder = fv1.Recorder
|
||||||
// Functions.
|
RecorderList = fv1.RecorderList
|
||||||
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 (t *TimeTrigger) GetObjectKind() schema.ObjectKind {
|
|
||||||
return &t.TypeMeta
|
|
||||||
}
|
|
||||||
func (m *MessageQueueTrigger) GetObjectKind() schema.ObjectKind {
|
|
||||||
return &m.TypeMeta
|
|
||||||
}
|
|
||||||
func (p *Package) GetObjectKind() schema.ObjectKind {
|
|
||||||
return &p.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 (t *TimeTrigger) GetObjectMeta() metav1.Object {
|
|
||||||
return &t.Metadata
|
|
||||||
}
|
|
||||||
func (m *MessageQueueTrigger) GetObjectMeta() metav1.Object {
|
|
||||||
return &m.Metadata
|
|
||||||
}
|
|
||||||
func (p *Package) GetObjectMeta() metav1.Object {
|
|
||||||
return &p.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 (ml *MessageQueueTriggerList) GetObjectKind() schema.ObjectKind {
|
|
||||||
return &ml.TypeMeta
|
|
||||||
}
|
|
||||||
func (pl *PackageList) GetObjectKind() schema.ObjectKind {
|
|
||||||
return &pl.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 (ml *MessageQueueTriggerList) GetListMeta() metav1.List {
|
|
||||||
return &ml.Metadata
|
|
||||||
}
|
|
||||||
func (pl *PackageList) GetListMeta() metav1.List {
|
|
||||||
return &pl.Metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateMetadata(field string, m metav1.ObjectMeta) error {
|
|
||||||
return fission.ValidateKubeReference(field, m.Name, m.Namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Package) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
|
|
||||||
result = multierror.Append(result,
|
|
||||||
validateMetadata("Package", p.Metadata),
|
|
||||||
p.Spec.Validate(),
|
|
||||||
p.Status.Validate())
|
|
||||||
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pl *PackageList) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
// not validate ListMeta
|
|
||||||
for _, p := range pl.Items {
|
|
||||||
result = multierror.Append(result, p.Validate())
|
|
||||||
}
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
|
|
||||||
result = multierror.Append(result,
|
|
||||||
validateMetadata("Function", f.Metadata),
|
|
||||||
f.Spec.Validate())
|
|
||||||
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fl *FunctionList) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
for _, f := range fl.Items {
|
|
||||||
result = multierror.Append(result, f.Validate())
|
|
||||||
}
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Environment) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
|
|
||||||
result = multierror.Append(result,
|
|
||||||
validateMetadata("Environment", e.Metadata),
|
|
||||||
e.Spec.Validate())
|
|
||||||
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (el *EnvironmentList) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
for _, e := range el.Items {
|
|
||||||
result = multierror.Append(result, e.Validate())
|
|
||||||
}
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HTTPTrigger) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
|
|
||||||
result = multierror.Append(result,
|
|
||||||
validateMetadata("HTTPTrigger", h.Metadata),
|
|
||||||
h.Spec.Validate())
|
|
||||||
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hl *HTTPTriggerList) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
for _, h := range hl.Items {
|
|
||||||
result = multierror.Append(result, h.Validate())
|
|
||||||
}
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KubernetesWatchTrigger) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
|
|
||||||
result = multierror.Append(result,
|
|
||||||
validateMetadata("KubernetesWatchTrigger", k.Metadata),
|
|
||||||
k.Spec.Validate())
|
|
||||||
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kl *KubernetesWatchTriggerList) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
for _, k := range kl.Items {
|
|
||||||
result = multierror.Append(result, k.Validate())
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TimeTrigger) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
|
|
||||||
result = multierror.Append(result,
|
|
||||||
validateMetadata("TimeTrigger", t.Metadata),
|
|
||||||
t.Spec.Validate())
|
|
||||||
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tl *TimeTriggerList) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
for _, t := range tl.Items {
|
|
||||||
result = multierror.Append(result, t.Validate())
|
|
||||||
}
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MessageQueueTrigger) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
|
|
||||||
result = multierror.Append(result,
|
|
||||||
validateMetadata("MessageQueueTrigger", m.Metadata),
|
|
||||||
m.Spec.Validate())
|
|
||||||
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ml *MessageQueueTriggerList) Validate() error {
|
|
||||||
var result *multierror.Error
|
|
||||||
for _, m := range ml.Items {
|
|
||||||
result = multierror.Append(result, m.Validate())
|
|
||||||
}
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,22 +4,30 @@ The `binary` runtime is a go server that uses a subprocess to invoke executables
|
|||||||
|
|
||||||
Use Cases
|
Use Cases
|
||||||
- Execute bash scripts
|
- Execute bash scripts
|
||||||
- Run executables of languages that have no dedicated environment yet.
|
- Execute arbitrary binaries (such as common sysadmin tools)
|
||||||
|
- Get support in _any_ programming language by executing the generated executable.
|
||||||
|
|
||||||
## Words of Caution
|
⚠️ **Words of Caution** ⚠️
|
||||||
The environment runs on an alpine image with some additional utilty commandline tools installed, such as 'grep'.
|
|
||||||
However, in case you want to make use of more ecsoteric commandline tools, you should add the relevant apk to the
|
The environment runs on an alpine image with some additional utility commandline tools installed, such as 'grep'.
|
||||||
|
However, in case you want to make use of more esoteric commandline tools, you should add the relevant apk to the
|
||||||
Dockerfile and build a new binary environment. See 'Compiling' for instructions.
|
Dockerfile and build a new binary environment. See 'Compiling' for instructions.
|
||||||
|
|
||||||
When executing functions using binaries, **ensure that the executable is built for the right architecture**.
|
When executing functions using binaries, **ensure that the executable is built for the right architecture**.
|
||||||
Using the default binary environment this means that the binary should be build for Linux.
|
Using the default binary environment this means that the binary should be build for Linux.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
To get started with the latest binary environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fission env create --name binary --image fission/binary-env --builder fission/binary-builder
|
||||||
|
```
|
||||||
|
|
||||||
The interface to the executable used by this environment is somewhat similar to a [CGI interface](https://en.wikipedia.org/wiki/Common_Gateway_Interface).
|
The interface to the executable used by this environment is somewhat similar to a [CGI interface](https://en.wikipedia.org/wiki/Common_Gateway_Interface).
|
||||||
This means that any HTTP headers are converted to environment variables of the form "HTTP_<header-name>". For example these
|
This means that any HTTP headers are converted to environment variables of the form "HTTP_<header-name>". For example these
|
||||||
are some of frequently occurring headers:
|
are some of frequently occurring headers:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
# Request Metadata
|
# Request Metadata
|
||||||
CONTENT_LENGTH
|
CONTENT_LENGTH
|
||||||
REQUEST_URI
|
REQUEST_URI
|
||||||
@@ -28,18 +36,23 @@ REQUEST_METHOD
|
|||||||
# HTTP Headers
|
# HTTP Headers
|
||||||
HTTP_ACCEPT
|
HTTP_ACCEPT
|
||||||
HTTP_USER-AGENT
|
HTTP_USER-AGENT
|
||||||
...
|
HTTP_CONTENT-TYPE
|
||||||
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
The body of HTTP piped over the STDIN to the executable.
|
The body of HTTP piped over the STDIN to the executable.
|
||||||
All output that is provided to the server over the STDOUT will be transformed into the HTTP response.
|
All output that is provided to the server over the STDOUT will be transformed into the HTTP response.
|
||||||
|
|
||||||
|
See the [README](../../examples/binary/README.md) in the binary examples directory for additional usage instructions.
|
||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|
||||||
Build the Dockerfile:
|
To build the runtime environment:
|
||||||
```bash
|
```bash
|
||||||
docker build --tag=${USER}/binary-env .
|
docker build --tag=${USER}/binary-env .
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [README](../../examples/binary/README.md) in the binary examples directory for usage instructions.
|
To build the builder environment:
|
||||||
|
```bash
|
||||||
|
(cd builder/ && docker build --tag=${USER}/binary-builder .)
|
||||||
|
```
|
||||||
@@ -46,7 +46,7 @@ type (
|
|||||||
|
|
||||||
func (bs *BinaryServer) SpecializeHandler(w http.ResponseWriter, r *http.Request) {
|
func (bs *BinaryServer) SpecializeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if specialized {
|
if specialized {
|
||||||
w.WriteHeader(400)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte("Not a generic container"))
|
w.Write([]byte("Not a generic container"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
|
FROM microsoft/dotnet:1.1-sdk AS builder
|
||||||
|
|
||||||
|
COPY * /proj/
|
||||||
|
RUN cd /proj && ./project-build.sh
|
||||||
|
|
||||||
|
# Build env image
|
||||||
FROM microsoft/dotnet:1.1.0-runtime
|
FROM microsoft/dotnet:1.1.0-runtime
|
||||||
|
|
||||||
WORKDIR /fission-workdir
|
WORKDIR /fission-workdir
|
||||||
COPY out .
|
COPY --from=builder /proj/out .
|
||||||
EXPOSE 8888
|
EXPOSE 8888
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet"]
|
ENTRYPOINT ["dotnet"]
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
THIS_DIR=$(realpath $(dirname $0))
|
# clean up
|
||||||
|
rm -rf bin obj out
|
||||||
docker run -v $THIS_DIR:/proj microsoft/dotnet:1.1-sdk sh -c "cd /proj && ./project-build.sh"
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
|
FROM microsoft/dotnet:2.0.0-sdk AS builder
|
||||||
|
|
||||||
|
COPY * /proj/
|
||||||
|
RUN cd /proj && ./project-build.sh
|
||||||
|
|
||||||
|
# Build env image
|
||||||
FROM microsoft/dotnet:2.0-runtime
|
FROM microsoft/dotnet:2.0-runtime
|
||||||
|
|
||||||
WORKDIR /fission-workdir
|
WORKDIR /fission-workdir
|
||||||
COPY out .
|
COPY --from=builder /proj/out .
|
||||||
EXPOSE 8888
|
EXPOSE 8888
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet"]
|
ENTRYPOINT ["dotnet"]
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
THIS_DIR=$(realpath $(dirname $0))
|
# clean up
|
||||||
|
rm -rf bin obj out
|
||||||
docker run -v $THIS_DIR:/proj microsoft/dotnet:2.0.0-sdk sh -c "cd /proj && ./project-build.sh"
|
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func specializePod(f *fetcher.Fetcher, fetchPayload *string, loadPayload *string
|
|||||||
|
|
||||||
_, err = f.FetchSecretsAndCfgMaps(fetchReq.Secrets, fetchReq.ConfigMaps)
|
_, err = f.FetchSecretsAndCfgMaps(fetchReq.Secrets, fetchReq.ConfigMaps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error fetching secerts/configmaps: %v", err)
|
log.Fatalf("Error fetching secrets/configmaps: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,15 @@ type (
|
|||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Secrets []fission.SecretReference `json:"secretList"`
|
Secrets []fission.SecretReference `json:"secretList"`
|
||||||
ConfigMaps []fission.ConfigMapReference `json:"configMapList"`
|
ConfigMaps []fission.ConfigMapReference `json:"configMapList"`
|
||||||
|
KeepArchive bool `json:"keeparchive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadRequest send from builder manager describes which
|
// UploadRequest send from builder manager describes which
|
||||||
// deployment package should be upload to storage service.
|
// deployment package should be upload to storage service.
|
||||||
UploadRequest struct {
|
UploadRequest struct {
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
StorageSvcUrl string `json:"storagesvcurl"`
|
StorageSvcUrl string `json:"storagesvcurl"`
|
||||||
|
ArchivePackage bool `json:"archivepackage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadResponse defines the download url of an archive and
|
// UploadResponse defines the download url of an archive and
|
||||||
@@ -165,7 +167,7 @@ func writeSecretOrConfigMap(dataMap map[string][]byte, dirPath string) error {
|
|||||||
|
|
||||||
func (fetcher *Fetcher) VersionHandler(w http.ResponseWriter, r *http.Request) {
|
func (fetcher *Fetcher) VersionHandler(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, fission.VersionInfo().String())
|
fmt.Fprintf(w, fission.BuildInfo().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fetcher *Fetcher) FetchHandler(w http.ResponseWriter, r *http.Request) {
|
func (fetcher *Fetcher) FetchHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -221,13 +223,13 @@ func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
|||||||
if len(req.Filename) == 0 {
|
if len(req.Filename) == 0 {
|
||||||
e := fmt.Sprintf("Fetch request received for an empty file name, request: %v", req)
|
e := fmt.Sprintf("Fetch request received for an empty file name, request: %v", req)
|
||||||
log.Printf(e)
|
log.Printf(e)
|
||||||
return 400, errors.New(e)
|
return http.StatusBadRequest, errors.New(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify first if the file already exists.
|
// verify first if the file already exists.
|
||||||
if _, err := os.Stat(filepath.Join(fetcher.sharedVolumePath, req.Filename)); err == nil {
|
if _, err := os.Stat(filepath.Join(fetcher.sharedVolumePath, req.Filename)); err == nil {
|
||||||
log.Printf("Requested file: %s already exists at %s. Skipping fetch", req.Filename, fetcher.sharedVolumePath)
|
log.Printf("Requested file: %s already exists at %s. Skipping fetch", req.Filename, fetcher.sharedVolumePath)
|
||||||
return 200, nil
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpFile := req.Filename + ".tmp"
|
tmpFile := req.Filename + ".tmp"
|
||||||
@@ -239,7 +241,7 @@ func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
e := fmt.Sprintf("Failed to download url %v: %v", req.Url, err)
|
e := fmt.Sprintf("Failed to download url %v: %v", req.Url, err)
|
||||||
log.Printf(e)
|
log.Printf(e)
|
||||||
return 400, errors.New(e)
|
return http.StatusBadRequest, errors.New(e)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// get pkg
|
// get pkg
|
||||||
@@ -247,7 +249,7 @@ func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
e := fmt.Sprintf("Failed to get package: %v", err)
|
e := fmt.Sprintf("Failed to get package: %v", err)
|
||||||
log.Printf(e)
|
log.Printf(e)
|
||||||
return 500, errors.New(e)
|
return http.StatusInternalServerError, errors.New(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
var archive *fission.Archive
|
var archive *fission.Archive
|
||||||
@@ -261,7 +263,7 @@ func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
|||||||
if pkg.Status.BuildStatus != fission.BuildStatusSucceeded && pkg.Status.BuildStatus != fission.BuildStatusNone {
|
if pkg.Status.BuildStatus != fission.BuildStatusSucceeded && pkg.Status.BuildStatus != fission.BuildStatusNone {
|
||||||
e := fmt.Sprintf("Build status for the function's pkg : %s.%s is : %s, can't fetch deployment", pkg.Metadata.Name, pkg.Metadata.Namespace, pkg.Status.BuildStatus)
|
e := fmt.Sprintf("Build status for the function's pkg : %s.%s is : %s, can't fetch deployment", pkg.Metadata.Name, pkg.Metadata.Namespace, pkg.Status.BuildStatus)
|
||||||
log.Printf(e)
|
log.Printf(e)
|
||||||
return 500, errors.New(e)
|
return http.StatusInternalServerError, errors.New(e)
|
||||||
}
|
}
|
||||||
archive = &pkg.Spec.Deployment
|
archive = &pkg.Spec.Deployment
|
||||||
}
|
}
|
||||||
@@ -272,7 +274,7 @@ func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
e := fmt.Sprintf("Failed to write file %v: %v", tmpPath, err)
|
e := fmt.Sprintf("Failed to write file %v: %v", tmpPath, err)
|
||||||
log.Printf(e)
|
log.Printf(e)
|
||||||
return 500, errors.New(e)
|
return http.StatusInternalServerError, errors.New(e)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// download and verify
|
// download and verify
|
||||||
@@ -280,27 +282,27 @@ func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
e := fmt.Sprintf("Failed to download url %v: %v", req.Url, err)
|
e := fmt.Sprintf("Failed to download url %v: %v", req.Url, err)
|
||||||
log.Printf(e)
|
log.Printf(e)
|
||||||
return 400, errors.New(e)
|
return http.StatusBadRequest, errors.New(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = verifyChecksum(tmpPath, &archive.Checksum)
|
err = verifyChecksum(tmpPath, &archive.Checksum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := fmt.Sprintf("Failed to verify checksum: %v", err)
|
e := fmt.Sprintf("Failed to verify checksum: %v", err)
|
||||||
log.Printf(e)
|
log.Printf(e)
|
||||||
return 400, errors.New(e)
|
return http.StatusBadRequest, errors.New(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check file type here, if the file is a zip file unarchive it.
|
if archiver.Zip.Match(tmpPath) && !req.KeepArchive {
|
||||||
if archiver.Zip.Match(tmpPath) {
|
|
||||||
// unarchive tmp file to a tmp unarchive path
|
// unarchive tmp file to a tmp unarchive path
|
||||||
tmpUnarchivePath := filepath.Join(fetcher.sharedVolumePath, uuid.NewV4().String())
|
tmpUnarchivePath := filepath.Join(fetcher.sharedVolumePath, uuid.NewV4().String())
|
||||||
err := fetcher.unarchive(tmpPath, tmpUnarchivePath)
|
err := fetcher.unarchive(tmpPath, tmpUnarchivePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
log.Println(err.Error())
|
||||||
return 500, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpPath = tmpUnarchivePath
|
tmpPath = tmpUnarchivePath
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,11 +310,11 @@ func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
|||||||
err := fetcher.rename(tmpPath, filepath.Join(fetcher.sharedVolumePath, req.Filename))
|
err := fetcher.rename(tmpPath, filepath.Join(fetcher.sharedVolumePath, req.Filename))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
log.Println(err.Error())
|
||||||
return 500, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Successfully placed at %v", filepath.Join(fetcher.sharedVolumePath, req.Filename))
|
log.Printf("Successfully placed at %v", filepath.Join(fetcher.sharedVolumePath, req.Filename))
|
||||||
return 200, nil
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchSecretsAndCfgMaps fetches secrets and configmaps specified by user
|
// FetchSecretsAndCfgMaps fetches secrets and configmaps specified by user
|
||||||
@@ -420,12 +422,22 @@ func (fetcher *Fetcher) UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
srcFilepath := filepath.Join(fetcher.sharedVolumePath, req.Filename)
|
srcFilepath := filepath.Join(fetcher.sharedVolumePath, req.Filename)
|
||||||
dstFilepath := filepath.Join(fetcher.sharedVolumePath, zipFilename)
|
dstFilepath := filepath.Join(fetcher.sharedVolumePath, zipFilename)
|
||||||
|
|
||||||
err = fetcher.archive(srcFilepath, dstFilepath)
|
if req.ArchivePackage {
|
||||||
if err != nil {
|
err = fetcher.archive(srcFilepath, dstFilepath)
|
||||||
e := fmt.Sprintf("Error archiving zip file: %v", err)
|
if err != nil {
|
||||||
log.Println(e)
|
e := fmt.Sprintf("Error archiving zip file: %v", err)
|
||||||
http.Error(w, e, http.StatusInternalServerError)
|
log.Println(e)
|
||||||
return
|
http.Error(w, e, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = os.Rename(srcFilepath, dstFilepath)
|
||||||
|
if err != nil {
|
||||||
|
e := fmt.Sprintf("Error renaming the archive: %v", err)
|
||||||
|
log.Println(e)
|
||||||
|
http.Error(w, e, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Starting upload...")
|
log.Println("Starting upload...")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
ARG GO_VERSION=1.9.2
|
ARG GO_VERSION=1.9.2
|
||||||
|
|
||||||
FROM golang:${GO_VERSION}
|
FROM golang:${GO_VERSION} AS builder
|
||||||
|
|
||||||
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,7 +10,12 @@ ADD server.go ${APP}
|
|||||||
|
|
||||||
WORKDIR ${APP}
|
WORKDIR ${APP}
|
||||||
RUN go get
|
RUN go get
|
||||||
RUN go build -o /server server.go
|
RUN go build -a -o /server server.go
|
||||||
|
|
||||||
|
FROM ubuntu:18.04
|
||||||
|
WORKDIR /
|
||||||
|
COPY --from=builder /server /
|
||||||
|
RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENTRYPOINT ["/server"]
|
ENTRYPOINT ["/server"]
|
||||||
EXPOSE 8888
|
EXPOSE 8888
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ FROM ${BUILDER_IMAGE}
|
|||||||
|
|
||||||
FROM golang:${GO_VERSION}
|
FROM golang:${GO_VERSION}
|
||||||
|
|
||||||
COPY --from=0 /builder /builder
|
|
||||||
|
|
||||||
ENV GOPATH /usr
|
ENV GOPATH /usr
|
||||||
|
WORKDIR ${GOPATH}
|
||||||
|
|
||||||
|
COPY --from=0 /builder /builder
|
||||||
ADD build.sh /usr/local/bin/build
|
ADD build.sh /usr/local/bin/build
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
srcDir=${GOPATH}/src/$(basename ${SRC_PKG})
|
||||||
|
|
||||||
|
trap "rm -rf ${srcDir}" EXIT
|
||||||
|
|
||||||
if [ -d ${SRC_PKG} ]
|
if [ -d ${SRC_PKG} ]
|
||||||
then
|
then
|
||||||
echo "Building in directory ${SRC_PKG}"
|
echo "Building in directory ${srcDir}"
|
||||||
cd ${SRC_PKG}
|
ln -sf ${SRC_PKG} ${srcDir}
|
||||||
go build -buildmode=plugin -i -o ${DEPLOY_PKG} .
|
|
||||||
elif [ -f ${SRC_PKG} ]
|
elif [ -f ${SRC_PKG} ]
|
||||||
then
|
then
|
||||||
fn=$(basename ${SRC_PKG})
|
echo "Building file ${SRC_PKG} in ${srcDir}"
|
||||||
d=/tmp/$fn
|
mkdir -p ${srcDir}
|
||||||
mkdir $d
|
cp ${SRC_PKG} ${srcDir}/function.go
|
||||||
trap "rm -rf /tmp/$fn" EXIT
|
|
||||||
echo "Building file ${SRC_PKG} in $d"
|
|
||||||
|
|
||||||
cd $d
|
|
||||||
cp ${SRC_PKG} ./function.go
|
|
||||||
go build -buildmode=plugin -i -o ${DEPLOY_PKG} .
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
cd ${srcDir}
|
||||||
|
go build -buildmode=plugin -i -o ${DEPLOY_PKG} .
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
.springBeans
|
||||||
|
.project
|
||||||
|
.mvn
|
||||||
|
.settings
|
||||||
|
.classpath
|
||||||
|
target/
|
||||||
|
bin/
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
FROM maven:3.5-jdk-8 as BUILD
|
||||||
|
COPY src /usr/src/myapp/src
|
||||||
|
COPY pom.xml /usr/src/myapp
|
||||||
|
RUN mvn -f /usr/src/myapp/pom.xml clean package
|
||||||
|
|
||||||
|
FROM openjdk:8-jdk-alpine
|
||||||
|
VOLUME /tmp
|
||||||
|
COPY --from=BUILD /usr/src/myapp/target/env-java-0.0.1-SNAPSHOT.jar /app.jar
|
||||||
|
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar","--server.port=8888"]
|
||||||
|
EXPOSE 8888
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Fission: Java and JVM Environment
|
||||||
|
|
||||||
|
This is the JVM environment for Fission.
|
||||||
|
|
||||||
|
It's a Docker image containing a OpenJDK8 runtime, along with a
|
||||||
|
dynamic loader. A few dependencies are included in the
|
||||||
|
pom.xml file.
|
||||||
|
|
||||||
|
## Customizing this image
|
||||||
|
|
||||||
|
To add package dependencies, edit pom.xml to add what you
|
||||||
|
need, and rebuild this image (instructions below).
|
||||||
|
|
||||||
|
## Rebuilding and pushing the image
|
||||||
|
|
||||||
|
You'll need access to a Docker registry to push the image: you can
|
||||||
|
sign up for Docker hub at hub.docker.com, or use registries from
|
||||||
|
gcr.io, quay.io, etc. Let's assume you're using a docker hub account
|
||||||
|
called USER. Build and push the image to the the registry:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker build -t USER/jvm-env . && docker push USER/jvm-env
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using the image in fission
|
||||||
|
|
||||||
|
You can add this customized image to fission with "fission env
|
||||||
|
create":
|
||||||
|
|
||||||
|
```
|
||||||
|
fission env create --name jvm --image USER/jvm-env
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, if you already have an environment, you can update its image:
|
||||||
|
|
||||||
|
```
|
||||||
|
fission env update --name jvm --image USER/jvm-env
|
||||||
|
```
|
||||||
|
|
||||||
|
After this, fission functions that have the env parameter set to the
|
||||||
|
same environment name as this command will use this environment.
|
||||||
|
|
||||||
|
## Web Server Framework
|
||||||
|
|
||||||
|
JVM environment uses Tomcat HTTP server by default as it is included in spring web. You can choose to use jetty or undertow by changing the dependency in pom.xml file as shown below.
|
||||||
|
|
||||||
|
```
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<!-- Exclude the Tomcat dependency -->
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<!-- Use Jetty instead -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jetty</artifactId>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Java and JVM builder
|
||||||
|
|
||||||
|
JVM environment builder is based on OpenJDK8 and Maven 3.5.4 version. The default build command runs `mvn clean package` and uses the target/*with-dependencies.jar file for function. The default build command can be overridden as long as the uber jar file is copied to ${DEPLOY_PKG}.
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
## Fission builder base image
|
||||||
|
ARG BUILDER_IMAGE=fission/builder:latest
|
||||||
|
FROM ${BUILDER_IMAGE}
|
||||||
|
|
||||||
|
## Section copied from the OpenJDK 8 Dockerfile
|
||||||
|
|
||||||
|
ENV LANG C.UTF-8
|
||||||
|
RUN { \
|
||||||
|
echo '#!/bin/sh'; \
|
||||||
|
echo 'set -e'; \
|
||||||
|
echo; \
|
||||||
|
echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \
|
||||||
|
} > /usr/local/bin/docker-java-home \
|
||||||
|
&& chmod +x /usr/local/bin/docker-java-home
|
||||||
|
ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk
|
||||||
|
ENV PATH $PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin
|
||||||
|
|
||||||
|
ENV JAVA_VERSION 8u171
|
||||||
|
ENV JAVA_ALPINE_VERSION 8.171.11-r0
|
||||||
|
|
||||||
|
RUN set -x \
|
||||||
|
&& apk add --no-cache \
|
||||||
|
openjdk8="$JAVA_ALPINE_VERSION" \
|
||||||
|
&& [ "$JAVA_HOME" = "$(docker-java-home)" ]
|
||||||
|
|
||||||
|
## Section copied from the Maven Dockerfile
|
||||||
|
|
||||||
|
RUN apk add --no-cache curl tar bash procps
|
||||||
|
|
||||||
|
ARG MAVEN_VERSION=3.5.4
|
||||||
|
ARG USER_HOME_DIR="/root"
|
||||||
|
ARG SHA=ce50b1c91364cb77efe3776f756a6d92b76d9038b0a0782f7d53acf1e997a14d
|
||||||
|
ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries
|
||||||
|
|
||||||
|
RUN mkdir -p /usr/share/maven /usr/share/maven/ref \
|
||||||
|
&& curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
|
||||||
|
&& echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \
|
||||||
|
&& tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \
|
||||||
|
&& rm -f /tmp/apache-maven.tar.gz \
|
||||||
|
&& ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
|
||||||
|
|
||||||
|
ENV MAVEN_HOME /usr/share/maven
|
||||||
|
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
|
||||||
|
|
||||||
|
## Fission builder specific section
|
||||||
|
ADD build.sh /usr/local/bin/build
|
||||||
|
EXPOSE 8001
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eou pipefail
|
||||||
|
mvn clean package
|
||||||
|
cp ${SRC_PKG}/target/*with-dependencies.jar ${DEPLOY_PKG}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>io.fission</groupId>
|
||||||
|
<artifactId>env-java</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.0.1.RELEASE</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.fission</groupId>
|
||||||
|
<artifactId>fission-java-core</artifactId>
|
||||||
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<!-- Adding Sonatype repository to pull snapshots -->
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>fission-java-core</id>
|
||||||
|
<name>fission-java-core-snapshot</name>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package io.fission;
|
||||||
|
|
||||||
|
public class FunctionLoadRequest {
|
||||||
|
|
||||||
|
private String filepath;
|
||||||
|
private String functionName;
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
String getFilepath() {
|
||||||
|
return filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFilepath(String filepath) {
|
||||||
|
this.filepath = filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFunctionName() {
|
||||||
|
return functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFunctionName(String functionName) {
|
||||||
|
this.functionName = functionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package io.fission;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.springframework.boot.*;
|
||||||
|
import org.springframework.boot.autoconfigure.*;
|
||||||
|
import org.springframework.http.RequestEntity;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import io.fission.Function;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
public class Server {
|
||||||
|
|
||||||
|
private Function fn;
|
||||||
|
|
||||||
|
private static final int CLASS_LENGTH = 6;
|
||||||
|
|
||||||
|
private static Logger logger = Logger.getGlobal();
|
||||||
|
|
||||||
|
@RequestMapping(value = "/", method = { RequestMethod.GET, RequestMethod.POST, RequestMethod.DELETE,
|
||||||
|
RequestMethod.PUT })
|
||||||
|
ResponseEntity<Object> home(RequestEntity<?> req) {
|
||||||
|
if (fn == null) {
|
||||||
|
return ResponseEntity.badRequest().body("Container not specialized");
|
||||||
|
} else {
|
||||||
|
return ((ResponseEntity<Object>) ((Function) fn).call(req, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(path = "/v2/specialize", consumes = "application/json")
|
||||||
|
ResponseEntity<String> specialize(@RequestBody FunctionLoadRequest req) {
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
File file = new File(req.getFilepath());
|
||||||
|
if (!file.exists()) {
|
||||||
|
return ResponseEntity.badRequest().body("/userfunc/user not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
String entryPoint = req.getFunctionName();
|
||||||
|
logger.log(Level.INFO, "Entrypoint class:" + entryPoint);
|
||||||
|
if (entryPoint == null) {
|
||||||
|
return ResponseEntity.badRequest().body("Entrypoint class is missing in the function");
|
||||||
|
}
|
||||||
|
|
||||||
|
JarFile jarFile = null;
|
||||||
|
ClassLoader cl = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
jarFile = new JarFile(file);
|
||||||
|
Enumeration<JarEntry> e = jarFile.entries();
|
||||||
|
URL[] urls = { new URL("jar:file:" + file + "!/") };
|
||||||
|
|
||||||
|
// TODO Check if the classloading can be improved for ex. use something like:
|
||||||
|
// Thread.currentThread().setContextClassLoader(cl);
|
||||||
|
if (this.getClass().getClassLoader() == null) {
|
||||||
|
cl = URLClassLoader.newInstance(urls);
|
||||||
|
} else {
|
||||||
|
cl = URLClassLoader.newInstance(urls, this.getClass().getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cl == null) {
|
||||||
|
return ResponseEntity.status(500).body("Failed to initialize the classloader");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load all dependent classes from libraries etc.
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
JarEntry je = e.nextElement();
|
||||||
|
if (je.isDirectory() || !je.getName().endsWith(".class")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String className = je.getName().substring(0, je.getName().length() - CLASS_LENGTH);
|
||||||
|
className = className.replace('/', '.');
|
||||||
|
cl.loadClass(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate the function class
|
||||||
|
fn = (Function) cl.loadClass(entryPoint).newInstance();
|
||||||
|
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return ResponseEntity.badRequest().body("Error loading the Function class file");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return ResponseEntity.badRequest().body("Error loading Function or dependent class");
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return ResponseEntity.badRequest().body("Error creating a new instance of function class");
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return ResponseEntity.badRequest().body("Error creating a new instance of function class");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return ResponseEntity.badRequest().body("Error reading the JAR file");
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
// cl.close();
|
||||||
|
jarFile.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return ResponseEntity.badRequest().body("Error closing the file while loading the class");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long elapsedTime = System.nanoTime() - startTime;
|
||||||
|
logger.log(Level.INFO, "Specialize call done in: " + elapsedTime / 1000000 + " ms");
|
||||||
|
return ResponseEntity.ok("Done");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SpringApplication.run(Server.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
FROM alpine:3.5
|
FROM alpine:3.5
|
||||||
|
|
||||||
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 libev-dev
|
||||||
RUN pip3 install --upgrade pip
|
RUN pip3 install --upgrade pip
|
||||||
RUN rm -r /root/.cache
|
RUN rm -r /root/.cache
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
FROM alpine:3.5
|
FROM alpine:3.5
|
||||||
|
|
||||||
RUN apk update
|
RUN apk update
|
||||||
RUN apk add --no-cache python python-dev build-base py-pip
|
RUN apk add --no-cache python python-dev build-base py-pip libev-dev
|
||||||
RUN pip install --upgrade pip
|
RUN pip install --upgrade pip
|
||||||
RUN rm -r /root/.cache
|
RUN rm -r /root/.cache
|
||||||
|
|
||||||
|
|||||||
@@ -43,3 +43,23 @@ Or, if you already have an environment, you can update its image:
|
|||||||
|
|
||||||
After this, fission functions that have the env parameter set to the
|
After this, fission functions that have the env parameter set to the
|
||||||
same environment name as this command will use this environment.
|
same environment name as this command will use this environment.
|
||||||
|
|
||||||
|
## Web Server Framework
|
||||||
|
|
||||||
|
Python environment build and start a WSGI server, to support high HTTP
|
||||||
|
traffic. As it is applied in different use cases, this provides two server
|
||||||
|
frameworks: `bjoern` and `gevent`. They all support high concurrency request.
|
||||||
|
|
||||||
|
`bjoern` has good performance on RPS, to be ideal for most light resource
|
||||||
|
utilization cases.
|
||||||
|
|
||||||
|
`gevent` is a good supplement because of its internal multi-threads. It
|
||||||
|
supports heavy resource load functions, with well distribution of response
|
||||||
|
time.
|
||||||
|
|
||||||
|
Python environment pod remains `bjoern` framework by default. And it runs `gevent`
|
||||||
|
framework by setting the container environment `WSGI_FRAMEWORK` value to `GEVENT`.
|
||||||
|
|
||||||
|
The environment value is configured normally in two ways. One way is to set in Dockerfile
|
||||||
|
and build it into image. The other way is to set in Kubernetes deployment spec during
|
||||||
|
pod running and restart it.
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
Flask===0.11.1
|
Flask===0.11.1
|
||||||
|
bjoern
|
||||||
httplib2
|
httplib2
|
||||||
python-dateutil
|
python-dateutil
|
||||||
requests==2.7.0
|
requests==2.7.0
|
||||||
redis
|
redis
|
||||||
hiredis
|
hiredis
|
||||||
|
gevent
|
||||||
|
|||||||
@@ -4,99 +4,108 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
import imp
|
import imp
|
||||||
import os
|
import os
|
||||||
|
import bjoern
|
||||||
|
from gevent.pywsgi import WSGIServer
|
||||||
from flask import Flask, request, abort, g
|
from flask import Flask, request, abort, g
|
||||||
|
|
||||||
app = Flask(__name__)
|
class FuncApp(Flask):
|
||||||
|
def __init__(self, name, loglevel = logging.DEBUG):
|
||||||
|
super(FuncApp, self).__init__(name)
|
||||||
|
|
||||||
userfunc = None
|
# init the class members
|
||||||
|
self.userfunc = None
|
||||||
|
self.root = logging.getLogger()
|
||||||
|
self.ch = logging.StreamHandler(sys.stdout)
|
||||||
|
|
||||||
@app.route('/specialize', methods=['POST'])
|
#
|
||||||
def load():
|
# Logging setup. TODO: Loglevel hard-coded for now. We could allow
|
||||||
global userfunc
|
# functions/routes to override this somehow; or we could create
|
||||||
# load user function from codepath
|
# separate dev vs. prod environments.
|
||||||
codepath = '/userfunc/user'
|
#
|
||||||
userfunc = (imp.load_source('user', codepath)).main
|
self.root.setLevel(loglevel)
|
||||||
return ""
|
self.ch.setLevel(loglevel)
|
||||||
|
self.ch.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
||||||
|
self.logger.addHandler(self.ch)
|
||||||
|
|
||||||
@app.route('/v2/specialize', methods=['POST'])
|
#
|
||||||
def loadv2():
|
# Register the routers
|
||||||
global userfunc
|
#
|
||||||
body = request.get_json()
|
@self.route('/specialize', methods=['POST'])
|
||||||
filepath = body['filepath']
|
def load():
|
||||||
handler = body['functionName']
|
# load user function from codepath
|
||||||
|
codepath = '/userfunc/user'
|
||||||
|
self.userfunc = (imp.load_source('user', codepath)).main
|
||||||
|
return ""
|
||||||
|
|
||||||
# The value of "functionName" is consist of `<module-name>.<function-name>`.
|
@self.route('/v2/specialize', methods=['POST'])
|
||||||
moduleName, funcName = handler.split(".")
|
def loadv2():
|
||||||
|
body = request.get_json()
|
||||||
|
filepath = body['filepath']
|
||||||
|
handler = body['functionName']
|
||||||
|
|
||||||
# check whether the destination is a directory or a file
|
# The value of "functionName" is consist of `<module-name>.<function-name>`.
|
||||||
if os.path.isdir(filepath):
|
moduleName, funcName = handler.split(".")
|
||||||
# add package directory path into module search path
|
|
||||||
sys.path.append(filepath)
|
|
||||||
|
|
||||||
# find module from package path we append previously.
|
# check whether the destination is a directory or a file
|
||||||
# Python will try to find module from the same name file under
|
if os.path.isdir(filepath):
|
||||||
# the package directory. If search is successful, the return
|
# add package directory path into module search path
|
||||||
# value is a 3-element tuple; otherwise, an exception "ImportError"
|
sys.path.append(filepath)
|
||||||
# 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
|
# find module from package path we append previously.
|
||||||
# Return module object is the load is successful; otherwise,
|
# Python will try to find module from the same name file under
|
||||||
# an exception is raised.
|
# the package directory. If search is successful, the return
|
||||||
try:
|
# value is a 3-element tuple; otherwise, an exception "ImportError"
|
||||||
mod = imp.load_module(moduleName, f, path, desc)
|
# is raised.
|
||||||
finally:
|
# Second parameter of find_module enforces python to find same
|
||||||
if f:
|
# name module from the given list of directories to prevent name
|
||||||
f.close()
|
# confliction with built-in modules.
|
||||||
else:
|
f, path, desc = imp.find_module(moduleName, [filepath])
|
||||||
# load source from destination python file
|
|
||||||
mod = imp.load_source(moduleName, filepath)
|
|
||||||
|
|
||||||
# load user function from module
|
# load module
|
||||||
userfunc = getattr(mod, funcName)
|
# 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)
|
||||||
|
|
||||||
return ""
|
# load user function from module
|
||||||
|
self.userfunc = getattr(mod, funcName)
|
||||||
|
|
||||||
@app.route('/healthz', methods=['GET'])
|
return ""
|
||||||
def healthz():
|
|
||||||
return "", 200
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET', 'POST', 'PUT', 'HEAD', 'OPTIONS', 'DELETE'])
|
@self.route('/healthz', methods=['GET'])
|
||||||
def f():
|
def healthz():
|
||||||
if userfunc == None:
|
return "", 200
|
||||||
print("Generic container: no requests supported")
|
|
||||||
abort(500)
|
|
||||||
#
|
|
||||||
# Customizing the request context
|
|
||||||
#
|
|
||||||
# If you want to pass something to the function, you can add it to 'g':
|
|
||||||
# g.myKey = myValue
|
|
||||||
# And the user func can then access that (after doing a "from flask import g").
|
|
||||||
#
|
|
||||||
return userfunc()
|
|
||||||
|
|
||||||
#
|
@self.route('/', methods=['GET', 'POST', 'PUT', 'HEAD', 'OPTIONS', 'DELETE'])
|
||||||
# Logging setup. TODO: Loglevel hard-coded for now. We could allow
|
def f():
|
||||||
# functions/routes to override this somehow; or we could create
|
if self.userfunc == None:
|
||||||
# separate dev vs. prod environments.
|
print("Generic container: no requests supported")
|
||||||
#
|
abort(500)
|
||||||
def setup_logger(loglevel):
|
#
|
||||||
global app
|
# Customizing the request context
|
||||||
root = logging.getLogger()
|
#
|
||||||
root.setLevel(loglevel)
|
# If you want to pass something to the function, you can add it to 'g':
|
||||||
ch = logging.StreamHandler(sys.stdout)
|
# g.myKey = myValue
|
||||||
ch.setLevel(loglevel)
|
# And the user func can then access that (after doing a "from flask import g").
|
||||||
ch.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
#
|
||||||
app.logger.addHandler(ch)
|
return self.userfunc()
|
||||||
|
|
||||||
|
app = FuncApp(__name__, logging.DEBUG)
|
||||||
|
|
||||||
#
|
#
|
||||||
# TODO: this starts the built-in server, which isn't the most
|
# TODO: this starts the built-in server, which isn't the most
|
||||||
# efficient. We should use something better.
|
# efficient. We should use something better.
|
||||||
#
|
#
|
||||||
setup_logger(logging.DEBUG)
|
if os.environ.get("WSGI_FRAMEWORK") == "GEVENT":
|
||||||
app.logger.info("Starting server")
|
app.logger.info("Starting gevent based server")
|
||||||
app.run(host='0.0.0.0', port=8888)
|
svc = WSGIServer(('0.0.0.0', 8888), app)
|
||||||
|
svc.serve_forever()
|
||||||
|
else:
|
||||||
|
app.logger.info("Starting bjoern based server")
|
||||||
|
bjoern.run(app, '0.0.0.0', 8888, reuse_port=True)
|
||||||
|
|||||||
@@ -32,19 +32,19 @@ func MakeError(code int, msg string) Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MakeErrorFromHTTP(resp *http.Response) error {
|
func MakeErrorFromHTTP(resp *http.Response) error {
|
||||||
if resp.StatusCode == 200 {
|
if resp.StatusCode == http.StatusOK {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var errCode int
|
var errCode int
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
case 400:
|
case http.StatusBadRequest:
|
||||||
errCode = ErrorInvalidArgument
|
errCode = ErrorInvalidArgument
|
||||||
case 403:
|
case http.StatusForbidden:
|
||||||
errCode = ErrorNotAuthorized
|
errCode = ErrorNotAuthorized
|
||||||
case 404:
|
case http.StatusNotFound:
|
||||||
errCode = ErrorNotFound
|
errCode = ErrorNotFound
|
||||||
case 409:
|
case http.StatusConflict:
|
||||||
errCode = ErrorNameExists
|
errCode = ErrorNameExists
|
||||||
default:
|
default:
|
||||||
errCode = ErrorInternal
|
errCode = ErrorInternal
|
||||||
@@ -64,15 +64,15 @@ func (err Error) HTTPStatus() int {
|
|||||||
var code int
|
var code int
|
||||||
switch err.Code {
|
switch err.Code {
|
||||||
case ErrorInvalidArgument:
|
case ErrorInvalidArgument:
|
||||||
code = 400
|
code = http.StatusBadRequest
|
||||||
case ErrorNotAuthorized:
|
case ErrorNotAuthorized:
|
||||||
code = 403
|
code = http.StatusForbidden
|
||||||
case ErrorNotFound:
|
case ErrorNotFound:
|
||||||
code = 404
|
code = http.StatusNotFound
|
||||||
case ErrorNameExists:
|
case ErrorNameExists:
|
||||||
code = 409
|
code = http.StatusConflict
|
||||||
default:
|
default:
|
||||||
code = 500
|
code = http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ func GetHTTPError(err error) (int, string) {
|
|||||||
code = fe.HTTPStatus()
|
code = fe.HTTPStatus()
|
||||||
msg = fe.Message
|
msg = fe.Message
|
||||||
} else {
|
} else {
|
||||||
code = 500
|
code = http.StatusInternalServerError
|
||||||
msg = err.Error()
|
msg = err.Error()
|
||||||
}
|
}
|
||||||
return code, msg
|
return code, msg
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/golang/example/stringutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is the entry point for this fission function
|
||||||
|
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
msg := stringutil.Reverse(stringutil.Reverse("Vendor Example Test"))
|
||||||
|
w.Write([]byte(msg))
|
||||||
|
}
|
||||||
+202
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
# Go example projects
|
||||||
|
|
||||||
|
This repository contains a collection of Go programs and libraries that
|
||||||
|
demonstrate the language, standard libraries, and tools.
|
||||||
|
|
||||||
|
## The examples
|
||||||
|
|
||||||
|
### [hello](hello/) ([godoc](//godoc.org/github.com/golang/example/hello)) and [stringutil](stringutil/) ([godoc](//godoc.org/github.com/golang/example/stringutil))
|
||||||
|
|
||||||
|
go get github.com/golang/example/hello
|
||||||
|
|
||||||
|
A trivial "Hello, world" program that uses a stringutil package.
|
||||||
|
|
||||||
|
Command [hello](hello/) covers:
|
||||||
|
|
||||||
|
* The basic form of an executable command
|
||||||
|
* Importing packages (from the standard library and the local repository)
|
||||||
|
* Printing strings ([fmt](//golang.org/pkg/fmt/))
|
||||||
|
|
||||||
|
Library [stringutil](stringutil/) covers:
|
||||||
|
|
||||||
|
* The basic form of a library
|
||||||
|
* Conversion between string and []rune
|
||||||
|
* Table-driven unit tests ([testing](//golang.org/pkg/testing/))
|
||||||
|
|
||||||
|
### [outyet](outyet/) ([godoc](//godoc.org/github.com/golang/example/outyet))
|
||||||
|
|
||||||
|
go get github.com/golang/example/outyet
|
||||||
|
|
||||||
|
A web server that answers the question: "Is Go 1.x out yet?"
|
||||||
|
|
||||||
|
Topics covered:
|
||||||
|
|
||||||
|
* Command-line flags ([flag](//golang.org/pkg/flag/))
|
||||||
|
* Web servers ([net/http](//golang.org/pkg/net/http/))
|
||||||
|
* HTML Templates ([html/template](//golang.org/pkg/html/template/))
|
||||||
|
* Logging ([log](//golang.org/pkg/log/))
|
||||||
|
* Long-running background processes
|
||||||
|
* Synchronizing data access between goroutines ([sync](//golang.org/pkg/sync/))
|
||||||
|
* Exporting server state for monitoring ([expvar](//golang.org/pkg/expvar/))
|
||||||
|
* Unit and integration tests ([testing](//golang.org/pkg/testing/))
|
||||||
|
* Dependency injection
|
||||||
|
* Time ([time](//golang.org/pkg/time/))
|
||||||
|
|
||||||
|
### [appengine-hello](appengine-hello/) ([godoc](//godoc.org/github.com/golang/example/appengine-hello))
|
||||||
|
|
||||||
|
goapp get github.com/golang/example/appengine-hello
|
||||||
|
|
||||||
|
A trivial "Hello, world" App Engine application intended to be used as the
|
||||||
|
starting point for your own code.
|
||||||
|
|
||||||
|
_Note_: The `goapp` tool is part of the [Google App Engine SDK for Go](https://cloud.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go).
|
||||||
|
|
||||||
|
### [gotypes](gotypes/) ([godoc](//godoc.org/github.com/golang/example/gotypes))
|
||||||
|
|
||||||
|
The `go/types` package is a type-checker for Go programs. It is one of the most
|
||||||
|
complex packages in Go's standard library, so we have provided this tutorial to
|
||||||
|
help you find your bearings. It comes with several example programs that you
|
||||||
|
can obtain using `go get` and play with as you learn to build tools that analyze
|
||||||
|
or manipulate Go programs.
|
||||||
|
|
||||||
|
### [template](template/) ([godoc](//godoc.org/github.com/golang/example/template))
|
||||||
|
|
||||||
|
A trivial web server that demonstrates the use of the
|
||||||
|
[`template` package](https://golang.org/pkg/text/template/)'s `block` feature.
|
||||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
This code is a starting point for your Google App Engine application in
|
||||||
|
Go.
|
||||||
|
|
||||||
|
To run the application locally you need the install the [Go Cloud
|
||||||
|
SDK](https://cloud.google.com/appengine/docs/go/#Go_tools) and execute the next
|
||||||
|
command from the directory containing this file:
|
||||||
|
|
||||||
|
$ goapp serve app.yaml
|
||||||
|
|
||||||
|
To deploy the application you have to first create an App Engine project
|
||||||
|
and use it as the application file in all the yaml files.
|
||||||
Generated
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2015 Google Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package hello is a simple App Engine application that replies to requests
|
||||||
|
// on /hello with a welcoming message.
|
||||||
|
package hello
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// init is run before the application starts serving.
|
||||||
|
func init() {
|
||||||
|
// Handle all requests with path /hello with the helloHandler function.
|
||||||
|
http.HandleFunc("/hello", helloHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func helloHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "Hello from the Go app")
|
||||||
|
}
|
||||||
Generated
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
# Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
# 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 writing, software distributed
|
||||||
|
# under the License is distributed on a "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.
|
||||||
|
|
||||||
|
application: you-application-id
|
||||||
|
version: 1
|
||||||
|
runtime: go
|
||||||
|
api_version: go1
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- url: /hello
|
||||||
|
script: _go_app
|
||||||
|
|
||||||
|
- url: /favicon.ico
|
||||||
|
static_files: static/favicon.ico
|
||||||
|
upload: static/favicon.ico
|
||||||
|
|
||||||
|
- url: /
|
||||||
|
static_files: static/index.html
|
||||||
|
upload: static/index.html
|
||||||
|
|
||||||
|
- url: /
|
||||||
|
static_dir: static
|
||||||
Generated
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Generated
Vendored
+34
@@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Copyright 2015 Google Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Hello, world</title>
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
<script src="/script.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Hello, world</h1>
|
||||||
|
|
||||||
|
<button onclick="fetchMessage()">Fetch Message</button>
|
||||||
|
<p id="message">Click on the button to fetch the message.</p>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function fetchMessage() {
|
||||||
|
var xmlHttp = new XMLHttpRequest();
|
||||||
|
xmlHttp.open("GET", "/hello", false);
|
||||||
|
xmlHttp.send(null);
|
||||||
|
document.getElementById("message").innerHTML = xmlHttp.responseText;
|
||||||
|
}
|
||||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
all: README.md
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
README.md: go-types.md weave.go $(wildcard */*.go)
|
||||||
|
go run weave.go go-types.md >README.md
|
||||||
|
|
||||||
|
# This is for previewing using github.
|
||||||
|
# $HOME/markdown must be a github client.
|
||||||
|
test: README.md
|
||||||
|
cp README.md $$HOME/markdown/
|
||||||
|
(cd $$HOME/markdown/ && git commit -m . README.md && git push)
|
||||||
|
|
||||||
+2554
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/importer"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const hello = `package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello, world")
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
//!+
|
||||||
|
func PrintDefsUses(fset *token.FileSet, files ...*ast.File) error {
|
||||||
|
conf := types.Config{Importer: importer.Default()}
|
||||||
|
info := &types.Info{
|
||||||
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
|
Uses: make(map[*ast.Ident]types.Object),
|
||||||
|
}
|
||||||
|
_, err := conf.Check("hello", fset, files, info)
|
||||||
|
if err != nil {
|
||||||
|
return err // type error
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, obj := range info.Defs {
|
||||||
|
fmt.Printf("%s: %q defines %v\n",
|
||||||
|
fset.Position(id.Pos()), id.Name, obj)
|
||||||
|
}
|
||||||
|
for id, obj := range info.Uses {
|
||||||
|
fmt.Printf("%s: %q uses %v\n",
|
||||||
|
fset.Position(id.Pos()), id.Name, obj)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//!-
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Parse one file.
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
f, err := parser.ParseFile(fset, "hello.go", hello, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err) // parse error
|
||||||
|
}
|
||||||
|
if err := PrintDefsUses(fset, f); err != nil {
|
||||||
|
log.Fatal(err) // type error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//!+output
|
||||||
|
$ go build github.com/golang/example/gotypes/defsuses
|
||||||
|
$ ./defsuses
|
||||||
|
hello.go:1:9: "main" defines <nil>
|
||||||
|
hello.go:5:6: "main" defines func hello.main()
|
||||||
|
hello.go:6:9: "fmt" uses package fmt
|
||||||
|
hello.go:6:13: "Println" uses func fmt.Println(a ...interface{}) (n int, err error)
|
||||||
|
//!-output
|
||||||
|
*/
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
// The doc command prints the doc comment of a package-level object.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
// TODO: these will use std go/types after Feb 2016
|
||||||
|
"golang.org/x/tools/go/loader"
|
||||||
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) != 3 {
|
||||||
|
log.Fatal("Usage: doc <package> <object>")
|
||||||
|
}
|
||||||
|
//!+part1
|
||||||
|
pkgpath, name := os.Args[1], os.Args[2]
|
||||||
|
|
||||||
|
// The loader loads a complete Go program from source code.
|
||||||
|
conf := loader.Config{ParserMode: parser.ParseComments}
|
||||||
|
conf.Import(pkgpath)
|
||||||
|
lprog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err) // load error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the package and package-level object.
|
||||||
|
pkg := lprog.Package(pkgpath).Pkg
|
||||||
|
obj := pkg.Scope().Lookup(name)
|
||||||
|
if obj == nil {
|
||||||
|
log.Fatalf("%s.%s not found", pkg.Path(), name)
|
||||||
|
}
|
||||||
|
//!-part1
|
||||||
|
//!+part2
|
||||||
|
|
||||||
|
// Print the object and its methods (incl. location of definition).
|
||||||
|
fmt.Println(obj)
|
||||||
|
for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
|
||||||
|
fmt.Printf("%s: %s\n", lprog.Fset.Position(sel.Obj().Pos()), sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the path from the root of the AST to the object's position.
|
||||||
|
// Walk up to the enclosing ast.Decl for the doc comment.
|
||||||
|
_, path, _ := lprog.PathEnclosingInterval(obj.Pos(), obj.Pos())
|
||||||
|
for _, n := range path {
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *ast.GenDecl:
|
||||||
|
fmt.Println("\n", n.Doc.Text())
|
||||||
|
return
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
fmt.Println("\n", n.Doc.Text())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//!-part2
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//!+output
|
||||||
|
$ ./doc net/http File
|
||||||
|
type net/http.File interface{Readdir(count int) ([]os.FileInfo, error); Seek(offset int64, whence int) (int64, error); Stat() (os.FileInfo, error); io.Closer; io.Reader}
|
||||||
|
/go/src/io/io.go:92:2: method (net/http.File) Close() error
|
||||||
|
/go/src/io/io.go:71:2: method (net/http.File) Read(p []byte) (n int, err error)
|
||||||
|
/go/src/net/http/fs.go:65:2: method (net/http.File) Readdir(count int) ([]os.FileInfo, error)
|
||||||
|
/go/src/net/http/fs.go:66:2: method (net/http.File) Seek(offset int64, whence int) (int64, error)
|
||||||
|
/go/src/net/http/fs.go:67:2: method (net/http.File) Stat() (os.FileInfo, error)
|
||||||
|
|
||||||
|
A File is returned by a FileSystem's Open method and can be
|
||||||
|
served by the FileServer implementation.
|
||||||
|
|
||||||
|
The methods should behave the same as those on an *os.File.
|
||||||
|
//!-output
|
||||||
|
*/
|
||||||
+2055
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
//!+
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello, 世界")
|
||||||
|
}
|
||||||
|
|
||||||
|
//!-
|
||||||
Generated
Vendored
+81
@@ -0,0 +1,81 @@
|
|||||||
|
// The hugeparam command identifies by-value parameters that are larger than n bytes.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// $ ./hugeparams encoding/xml
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/loader"
|
||||||
|
)
|
||||||
|
|
||||||
|
//!+
|
||||||
|
var bytesFlag = flag.Int("bytes", 48, "maximum parameter size in bytes")
|
||||||
|
|
||||||
|
var sizeof = (&types.StdSizes{8, 8}).Sizeof // the sizeof function
|
||||||
|
|
||||||
|
func PrintHugeParams(fset *token.FileSet, info *types.Info, files []*ast.File) {
|
||||||
|
checkTuple := func(descr string, tuple *types.Tuple) {
|
||||||
|
for i := 0; i < tuple.Len(); i++ {
|
||||||
|
v := tuple.At(i)
|
||||||
|
if sz := sizeof(v.Type()); sz > int64(*bytesFlag) {
|
||||||
|
fmt.Printf("%s: %q %s: %s = %d bytes\n",
|
||||||
|
fset.Position(v.Pos()),
|
||||||
|
v.Name(), descr, v.Type(), sz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkSig := func(sig *types.Signature) {
|
||||||
|
checkTuple("parameter", sig.Params())
|
||||||
|
checkTuple("result", sig.Results())
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
ast.Inspect(file, func(n ast.Node) bool {
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
checkSig(info.Defs[n.Name].Type().(*types.Signature))
|
||||||
|
case *ast.FuncLit:
|
||||||
|
checkSig(info.Types[n.Type].Type.(*types.Signature))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//!-
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// The loader loads a complete Go program from source code.
|
||||||
|
var conf loader.Config
|
||||||
|
_, err := conf.FromArgs(flag.Args(), false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err) // command syntax error
|
||||||
|
}
|
||||||
|
lprog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err) // load error
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, info := range lprog.InitialPackages() {
|
||||||
|
PrintHugeParams(lprog.Fset, &info.Info, info.Files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//!+output
|
||||||
|
% ./hugeparam encoding/xml
|
||||||
|
/go/src/encoding/xml/marshal.go:167:50: "start" parameter: encoding/xml.StartElement = 56 bytes
|
||||||
|
/go/src/encoding/xml/marshal.go:734:97: "" result: encoding/xml.StartElement = 56 bytes
|
||||||
|
/go/src/encoding/xml/marshal.go:761:51: "start" parameter: encoding/xml.StartElement = 56 bytes
|
||||||
|
/go/src/encoding/xml/marshal.go:781:68: "start" parameter: encoding/xml.StartElement = 56 bytes
|
||||||
|
/go/src/encoding/xml/xml.go:72:30: "" result: encoding/xml.StartElement = 56 bytes
|
||||||
|
//!-output
|
||||||
|
*/
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user