Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a3e326f36 | ||
|
|
f0dc2554a1 | ||
|
|
2ece0db13f | ||
|
|
0e3a270d27 | ||
|
|
6a7381c575 | ||
|
|
0aff2f214d | ||
|
|
dec4a39c15 | ||
|
|
7577339912 | ||
|
|
6375819195 | ||
|
|
0b34abcada | ||
|
|
772fdfa15c | ||
|
|
7198b24028 | ||
|
|
eb696432d8 | ||
|
|
2d359f879a | ||
|
|
94e2f11ea6 | ||
|
|
ff01cf2452 | ||
|
|
400e19a48f | ||
|
|
0194f53e12 | ||
|
|
fef1469c61 | ||
|
|
cf53e17be5 | ||
|
|
449f49515a | ||
|
|
d01b309fc7 | ||
|
|
12299df811 | ||
|
|
95a50dbbd6 | ||
|
|
8014c83b02 | ||
|
|
db2f620121 | ||
|
|
f9ca626940 | ||
|
|
1dbf8f9243 | ||
|
|
9406720e8a | ||
|
|
2a06705320 | ||
|
|
d39944e04d | ||
|
|
6f6b52438a | ||
|
|
5baf67f143 | ||
|
|
6d61f6e5eb | ||
|
|
6ab902e7a3 | ||
|
|
ed7d088bbc | ||
|
|
c0058153a0 | ||
|
|
a8482a8b89 | ||
|
|
a23f8acffc | ||
|
|
77be3379b2 | ||
|
|
03046170dc | ||
|
|
c0acaa74c3 | ||
|
|
8708fdec93 | ||
|
|
6b188c053a | ||
|
|
0fd6114218 | ||
|
|
9ae06f56c8 |
+3
-1
@@ -39,9 +39,11 @@ script:
|
|||||||
- hack/verify-govet.sh
|
- hack/verify-govet.sh
|
||||||
- hack/runtests.sh
|
- hack/runtests.sh
|
||||||
- test/build_and_test.sh
|
- test/build_and_test.sh
|
||||||
|
- test/upgrade/fission_upgrade_test.sh
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
slack:
|
slack:
|
||||||
secure: YZ34vsfw1TtftJypg1MyP4+ihONI4gaxeS3FghBQli6+EezjzcxOXyj5VD+x0ucXfDeTaDrFfmVN0SAGObOoZUE+ea9KAoTo50tRLaD9kwOTACiekZalC4uuBguH0D1/A6vlbU8dchsr9mvIhbisG6mTncdPtqGYYHtyBQme6ngmmHbVAQFZcIBHmNuDb/HWhSr8KMEuyB6+mBXLYELHnXnf26cOhdGNaagqCOJTiemX85RGwIuOPxyBhKDFMLyHohDT7FJMH/qijveE6YgOYTQC5nYc2Np1KvC7hQkIu4nuyczyYlrNQl/TWv+SVI8PjIs0PYuCuD3gUqoVEi8d94HbrOPzFEpbwDS9P4qL39DGmco1Q56Vqxe6sRI0vDWPb5gCP1lSgs3PMECVn7Wor/pTvcL+C+U2jLwWJUl0vbyWCL7ngl/3iTssV7qBpUrI7Oclwp8LrQo9fPj0DL4gE9rNanpEWjQ6yPGaysIL1zLHtRghhm52A22NJGp71jkS2KEpLi6ZWFYjMeuXw5eOQFhqFlzyRJOmLYa3B607TLWuyo2L2CxAfMmq0FGfemvrkLZIWtlQKK4y9ImpsURwaGT2XCtThFtHl77wEss913nC+T2dX3O5Bl0UmxFd5S3mVM109I8c4lDosxnAjRfS9MheFlrG0gjSJSBCw57x7f0=
|
rooms:
|
||||||
|
secure: YZ34vsfw1TtftJypg1MyP4+ihONI4gaxeS3FghBQli6+EezjzcxOXyj5VD+x0ucXfDeTaDrFfmVN0SAGObOoZUE+ea9KAoTo50tRLaD9kwOTACiekZalC4uuBguH0D1/A6vlbU8dchsr9mvIhbisG6mTncdPtqGYYHtyBQme6ngmmHbVAQFZcIBHmNuDb/HWhSr8KMEuyB6+mBXLYELHnXnf26cOhdGNaagqCOJTiemX85RGwIuOPxyBhKDFMLyHohDT7FJMH/qijveE6YgOYTQC5nYc2Np1KvC7hQkIu4nuyczyYlrNQl/TWv+SVI8PjIs0PYuCuD3gUqoVEi8d94HbrOPzFEpbwDS9P4qL39DGmco1Q56Vqxe6sRI0vDWPb5gCP1lSgs3PMECVn7Wor/pTvcL+C+U2jLwWJUl0vbyWCL7ngl/3iTssV7qBpUrI7Oclwp8LrQo9fPj0DL4gE9rNanpEWjQ6yPGaysIL1zLHtRghhm52A22NJGp71jkS2KEpLi6ZWFYjMeuXw5eOQFhqFlzyRJOmLYa3B607TLWuyo2L2CxAfMmq0FGfemvrkLZIWtlQKK4y9ImpsURwaGT2XCtThFtHl77wEss913nC+T2dX3O5Bl0UmxFd5S3mVM109I8c4lDosxnAjRfS9MheFlrG0gjSJSBCw57x7f0=
|
||||||
on_success: change
|
on_success: change
|
||||||
on_failure: always
|
on_failure: always
|
||||||
|
|||||||
+100
-9
@@ -1,17 +1,107 @@
|
|||||||
# 0.5.0
|
# 0.7.1
|
||||||
[Documentation](http://fission.io/docs/0.5.0/)
|
[Documentation](http://fission.io/docs/0.7.1/)
|
||||||
## Downloads for 0.5.0
|
## Downloads for 0.7.1
|
||||||
|
|
||||||
|
|
||||||
filename | sha256 hash
|
filename | sha256 hash
|
||||||
-------- | -----------
|
-------- | -----------
|
||||||
[fission-core-0.5.0.tgz](https://github.com/fission/fission/releases/download/0.5.0/fission-core-0.5.0.tgz) | `6ee668de0870a52d16a051bb5d60c12e523a87c54269b321bdab6dd97f883ee4`
|
[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.5.0.tgz](https://github.com/fission/fission/releases/download/0.5.0/fission-all-0.5.0.tgz) | `2f72fe4ba836d84e60538ecf401a508384bb194c979688be69f1370af0c071bb`
|
[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.5.0/fission-cli-osx) | `1b7de8a5bbf7710ba66997003e7a63f9887ef89d7f7e027c31a78f2f51a11536`
|
[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.5.0/fission-cli-linux) | `7bbff6d166daf16f6e08b82753cbd6bd0d407e12372a0c6612b6f1a11ed39e09`
|
[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.5.0/fission-cli-windows.exe) | `5db18472bd071daf39ad681c6ad3b4fd4e89ccb243f81e7ea67785e0c7b2f209`
|
[fission-cli-windows.exe](https://github.com/fission/fission/releases/download/0.7.1/fission-cli-windows.exe) | `c237b1ee1a8f2b26038555388a438e4fff5725c90d4128baaa32a77ccf990d2e`
|
||||||
|
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Prevent releasing idle connections because transport is shared. [\#609](https://github.com/fission/fission/pull/609) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Fix components crash before crds creation [\#602](https://github.com/fission/fission/pull/602) ([life1347](https://github.com/life1347))
|
||||||
|
- updates to changelog. [\#598](https://github.com/fission/fission/pull/598) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- changes needed for release 0.7.0 [\#597](https://github.com/fission/fission/pull/597) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- `fission X create --spec` flags for env and trigger create commands [\#607](https://github.com/fission/fission/pull/607) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Updating releasing guideliness with a few more details. [\#599](https://github.com/fission/fission/pull/599) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Add deprecated message to subcommand pods [\#592](https://github.com/fission/fission/pull/592) ([life1347](https://github.com/life1347))
|
||||||
|
- Add validate function to crd resource and do validate before creation/update [\#580](https://github.com/fission/fission/pull/580) ([life1347](https://github.com/life1347))
|
||||||
|
- Invalidate stale router cache entry with podIP's for deleted pods. [\#546](https://github.com/fission/fission/pull/546) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Use a separate controller loop to watch functions change and create a service [\#544](https://github.com/fission/fission/pull/544) ([life1347](https://github.com/life1347))
|
||||||
|
- E2E test for NATS-streaming trigger [\#338](https://github.com/fission/fission/pull/338) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
|
||||||
|
## [0.7.0](https://github.com/fission/fission/tree/0.7.0) (2018-04-02)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/0.6.1...0.7.0)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- bug fix: spec dir flag [\#595](https://github.com/fission/fission/pull/595) ([xiekeyang](https://github.com/xiekeyang))
|
||||||
|
- Add steps to set FISSION\_ROUTER env variable & update docs [\#593](https://github.com/fission/fission/pull/593) ([life1347](https://github.com/life1347))
|
||||||
|
- Adding routerUrl parameter for kubewatch, timer, message queue trigge… [\#591](https://github.com/fission/fission/pull/591) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Uses proper way to get server URL [\#587](https://github.com/fission/fission/pull/587) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Check if the requested file already exists in fetcher and skip fetch [\#584](https://github.com/fission/fission/pull/584) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Add golang example to installation guide [\#578](https://github.com/fission/fission/pull/578) ([clee](https://github.com/clee))
|
||||||
|
- Fixes the issue \#559 with env versions [\#569](https://github.com/fission/fission/pull/569) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Add post-upgrade-job to track fission upgrade [\#564](https://github.com/fission/fission/pull/564) ([life1347](https://github.com/life1347))
|
||||||
|
- Prepending a slash to user input url if missing. [\#547](https://github.com/fission/fission/pull/547) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Add verbosity flag and verbose logs for portforwarder [\#575](https://github.com/fission/fission/pull/575) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Spec validator, better errors, apply waits for previous build [\#560](https://github.com/fission/fission/pull/560) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Tests for function update [\#550](https://github.com/fission/fission/pull/550) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Show fission deployment version with cli [\#538](https://github.com/fission/fission/pull/538) ([life1347](https://github.com/life1347))
|
||||||
|
|
||||||
|
## [0.6.1](https://github.com/fission/fission/tree/0.6.1) (2018-03-22)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/0.6.0...0.6.1)
|
||||||
|
|
||||||
|
**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))
|
||||||
|
- \[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))
|
||||||
|
- Docs update [\#542](https://github.com/fission/fission/pull/542) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Release script updates [\#541](https://github.com/fission/fission/pull/541) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Show warning when trying to create a route with non-existent function \(\#238\) [\#539](https://github.com/fission/fission/pull/539) ([life1347](https://github.com/life1347))
|
||||||
|
- Fix executor failed to clean cache & kubeobjs after function deleted \(\#533\) [\#534](https://github.com/fission/fission/pull/534) ([life1347](https://github.com/life1347))
|
||||||
|
- Delete healthz log [\#525](https://github.com/fission/fission/pull/525) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Always retry when istio is enabled. [\#536](https://github.com/fission/fission/pull/536) ([life1347](https://github.com/life1347))
|
||||||
|
- Fix executor tries to create a new deployment when a function is updated [\#524](https://github.com/fission/fission/pull/524) ([life1347](https://github.com/life1347))
|
||||||
|
- Add container spec config options to \(build\) environments [\#413](https://github.com/fission/fission/pull/413) ([erwinvaneyk](https://github.com/erwinvaneyk))
|
||||||
|
|
||||||
|
## [0.6.0](https://github.com/fission/fission/tree/0.6.0) (2018-03-01)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/latest...0.6.0)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Detect fission namespace in cli [\#519](https://github.com/fission/fission/pull/519) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Default values for FISSION\_\* env vars [\#518](https://github.com/fission/fission/pull/518) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Add chart version to job name [\#516](https://github.com/fission/fission/pull/516) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Fix CLI not update function's secret/configmap correctly [\#512](https://github.com/fission/fission/pull/512) ([life1347](https://github.com/life1347))
|
||||||
|
- Adds latest tags and pushes to dockerhub for fetcher and fission-bundle [\#509](https://github.com/fission/fission/pull/509) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Fixes the backward compatibility with older environment versions [\#508](https://github.com/fission/fission/pull/508) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Update Fn: Executor New Deployment [\#504](https://github.com/fission/fission/pull/504) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Adds default resources for fetcher pod [\#500](https://github.com/fission/fission/pull/500) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Documentation Revamp [\#496](https://github.com/fission/fission/pull/496) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
|
- Delete and list orphan pkgs [\#468](https://github.com/fission/fission/pull/468) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Service type ClusterIP - Controller port forward through CLI [\#431](https://github.com/fission/fission/pull/431) ([prithviramesh](https://github.com/prithviramesh))
|
||||||
|
- Istio integration [\#421](https://github.com/fission/fission/pull/421) ([life1347](https://github.com/life1347))
|
||||||
|
- Implement support for Azure storage message queue triggers [\#371](https://github.com/fission/fission/pull/371) ([peterhuene](https://github.com/peterhuene))
|
||||||
|
|
||||||
|
## [latest](https://github.com/fission/fission/tree/latest) (2018-03-01)
|
||||||
|
[Full Changelog](https://github.com/fission/fission/compare/0.5.0...latest)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Release checklist [\#522](https://github.com/fission/fission/pull/522) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Fix post-install-job container failure due to command not found [\#514](https://github.com/fission/fission/pull/514) ([life1347](https://github.com/life1347))
|
||||||
|
- Replace the release with the latest tag. [\#513](https://github.com/fission/fission/pull/513) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Go: Set image to right version, update example readme [\#497](https://github.com/fission/fission/pull/497) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Remove a noisy log from router [\#495](https://github.com/fission/fission/pull/495) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- Improve release script [\#494](https://github.com/fission/fission/pull/494) ([life1347](https://github.com/life1347))
|
||||||
|
- Update SHA256 HASH in CHANGELOG.md due to binaries update [\#493](https://github.com/fission/fission/pull/493) ([life1347](https://github.com/life1347))
|
||||||
|
- Go builder for single file functions [\#492](https://github.com/fission/fission/pull/492) ([soamvasani](https://github.com/soamvasani))
|
||||||
|
- CI modifications [\#491](https://github.com/fission/fission/pull/491) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
|
- Add upgrade guide from 0.4.x to 0.5.0 [\#490](https://github.com/fission/fission/pull/490) ([life1347](https://github.com/life1347))
|
||||||
|
- Version -\> 0.5.0 [\#489](https://github.com/fission/fission/pull/489) ([life1347](https://github.com/life1347))
|
||||||
|
|
||||||
## [0.5.0](https://github.com/fission/fission/tree/0.5.0) (2018-02-07)
|
## [0.5.0](https://github.com/fission/fission/tree/0.5.0) (2018-02-07)
|
||||||
[Full Changelog](https://github.com/fission/fission/compare/0.4.1...0.5.0)
|
[Full Changelog](https://github.com/fission/fission/compare/0.4.1...0.5.0)
|
||||||
|
|
||||||
@@ -31,6 +121,7 @@ filename | sha256 hash
|
|||||||
- NewDeploy Doc [\#432](https://github.com/fission/fission/pull/432) ([vishal-biyani](https://github.com/vishal-biyani))
|
- NewDeploy Doc [\#432](https://github.com/fission/fission/pull/432) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||||
- Add go vet check [\#430](https://github.com/fission/fission/pull/430) ([life1347](https://github.com/life1347))
|
- Add go vet check [\#430](https://github.com/fission/fission/pull/430) ([life1347](https://github.com/life1347))
|
||||||
- Fix potential nil pointer problem [\#485](https://github.com/fission/fission/pull/485) ([life1347](https://github.com/life1347))
|
- Fix potential nil pointer problem [\#485](https://github.com/fission/fission/pull/485) ([life1347](https://github.com/life1347))
|
||||||
|
- Add simple usage doc for accessing secret/configmap in function [\#484](https://github.com/fission/fission/pull/484) ([life1347](https://github.com/life1347))
|
||||||
- Helm hook bugfixes: run on upgrade, delete on completion [\#473](https://github.com/fission/fission/pull/473) ([soamvasani](https://github.com/soamvasani))
|
- Helm hook bugfixes: run on upgrade, delete on completion [\#473](https://github.com/fission/fission/pull/473) ([soamvasani](https://github.com/soamvasani))
|
||||||
- Archive pruner [\#471](https://github.com/fission/fission/pull/471) ([smruthi2187](https://github.com/smruthi2187))
|
- Archive pruner [\#471](https://github.com/fission/fission/pull/471) ([smruthi2187](https://github.com/smruthi2187))
|
||||||
- Build and push fluentd image on release; update chart to use that image [\#462](https://github.com/fission/fission/pull/462) ([soamvasani](https://github.com/soamvasani))
|
- Build and push fluentd image on release; update chart to use that image [\#462](https://github.com/fission/fission/pull/462) ([soamvasani](https://github.com/soamvasani))
|
||||||
@@ -236,7 +327,7 @@ filename | sha256 hash
|
|||||||
- Upgrade node environment to Node.js 7.6.0+ [\#151](https://github.com/fission/fission/pull/151) ([RobertHerhold](https://github.com/RobertHerhold))
|
- Upgrade node environment to Node.js 7.6.0+ [\#151](https://github.com/fission/fission/pull/151) ([RobertHerhold](https://github.com/RobertHerhold))
|
||||||
- use fmt.Errorf instead of error.New\(\) [\#149](https://github.com/fission/fission/pull/149) ([maxwell92](https://github.com/maxwell92))
|
- use fmt.Errorf instead of error.New\(\) [\#149](https://github.com/fission/fission/pull/149) ([maxwell92](https://github.com/maxwell92))
|
||||||
- Return 201 for created resources [\#148](https://github.com/fission/fission/pull/148) ([RobertHerhold](https://github.com/RobertHerhold))
|
- Return 201 for created resources [\#148](https://github.com/fission/fission/pull/148) ([RobertHerhold](https://github.com/RobertHerhold))
|
||||||
- Set correct Content-Type in the http response [\#147](https://github.com/fission/fission/pull/147) ([LingxianKong](https://github.com/LingxianKong))
|
- Set correct Content-Type in the http response [\#147](https://github.com/fission/fission/pull/147) ([lingxiankong](https://github.com/lingxiankong))
|
||||||
- Make it more clear where to clone this repo [\#145](https://github.com/fission/fission/pull/145) ([RobertHerhold](https://github.com/RobertHerhold))
|
- Make it more clear where to clone this repo [\#145](https://github.com/fission/fission/pull/145) ([RobertHerhold](https://github.com/RobertHerhold))
|
||||||
- Fix function delete with uid [\#142](https://github.com/fission/fission/pull/142) ([yqf3139](https://github.com/yqf3139))
|
- Fix function delete with uid [\#142](https://github.com/fission/fission/pull/142) ([yqf3139](https://github.com/yqf3139))
|
||||||
- Fixed pod has no ip \(\#139\) [\#141](https://github.com/fission/fission/pull/141) ([life1347](https://github.com/life1347))
|
- Fixed pod has no ip \(\#139\) [\#141](https://github.com/fission/fission/pull/141) ([life1347](https://github.com/life1347))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
baseURL = "http://fission.io/docs/0.5.0"
|
baseURL = "http://fission.io/docs/0.6.1"
|
||||||
languageCode = "en-US"
|
languageCode = "en-US"
|
||||||
defaultContentLanguage = "en"
|
defaultContentLanguage = "en"
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Usage
|
|||||||
$ fission route create --method GET --url /hello --function hello
|
$ fission route create --method GET --url /hello --function hello
|
||||||
|
|
||||||
# Run the function. This takes about 100msec the first time.
|
# Run the function. This takes about 100msec the first time.
|
||||||
$ curl http://$FISSION_ROUTER/hello
|
$ fission function test --name hello
|
||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,19 @@ $ fission function test --name hello
|
|||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For a compiled language like Go:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ fission env create --name go --image fission/go-env:0.6.0 --builder fission/go-builder:0.6.0
|
||||||
|
|
||||||
|
$ curl -LO https://raw.githubusercontent.com/fission/fission/master/examples/go/hello.go
|
||||||
|
|
||||||
|
$ fission function create --name gohello --env go --src hello.go --entrypoint Handler
|
||||||
|
|
||||||
|
$ fission function test --name gohello
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
### What's next?
|
### What's next?
|
||||||
|
|
||||||
If something went wrong, we'd love to help -- please [drop by the
|
If something went wrong, we'd love to help -- please [drop by the
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ weight: 42
|
|||||||
You can create an environment on your cluster from an image for that language. Optionally, you can specify CPU and memory resource limits. You can also specify the number of initially pre-warmed pods, which is called the poolsize.
|
You can create an environment on your cluster from an image for that language. Optionally, you can specify CPU and memory resource limits. You can also specify the number of initially pre-warmed pods, which is called the poolsize.
|
||||||
|
|
||||||
```
|
```
|
||||||
fission env create --name node --image fission/node-env:0.4.0 --mincpu 40 --maxcpu 80 --minmemory 64 --maxmemory 128 --poolsize 4
|
$ fission env create --name node --image fission/node-env:0.4.0 --mincpu 40 --maxcpu 80 --minmemory 64 --maxmemory 128 --poolsize 4
|
||||||
```
|
```
|
||||||
|
|
||||||
In case of pool based executor, the resources specified for environment are used for function pod as well. In case of new deployment executor, you can override the resources when you create a function.
|
In case of pool based executor, the resources specified for environment are used for function pod as well. In case of new deployment executor, you can override the resources when you create a function.
|
||||||
@@ -19,7 +19,7 @@ In case of pool based executor, the resources specified for environment are used
|
|||||||
When you create an environment, you can specify a builder image and builder command which will be used for building from source code. You can override the build command when creating a function. For more details on builder and packages you should check out examples in [Functions](../functions) and [packages](../package)
|
When you create an environment, you can specify a builder image and builder command which will be used for building from source code. You can override the build command when creating a function. For more details on builder and packages you should check out examples in [Functions](../functions) and [packages](../package)
|
||||||
|
|
||||||
```
|
```
|
||||||
fission env create --name python --image fission/python-env:latest --builder fission/python-builder:latest
|
$ fission env create --name python --image fission/python-env:latest --builder fission/python-builder:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Viewing environment information
|
### Viewing environment information
|
||||||
@@ -30,7 +30,7 @@ You can list the environments or view information of an individual environment:
|
|||||||
$ fission env list
|
$ fission env list
|
||||||
NAME UID IMAGE POOLSIZE MINCPU MAXCPU MINMEMORY MAXMEMORY
|
NAME UID IMAGE POOLSIZE MINCPU MAXCPU MINMEMORY MAXMEMORY
|
||||||
node ac84d62e-001f-11e8-85c9-42010aa00010 fission/node-env:0.4.0 4 40m 80m 64Mi 128Mi
|
node ac84d62e-001f-11e8-85c9-42010aa00010 fission/node-env:0.4.0 4 40m 80m 64Mi 128Mi
|
||||||
$
|
|
||||||
$ fission env get --name node
|
$ fission env get --name node
|
||||||
NAME UID IMAGE
|
NAME UID IMAGE
|
||||||
node ac84d62e-001f-11e8-85c9-42010aa00010 fission/node-env:0.4.0
|
node ac84d62e-001f-11e8-85c9-42010aa00010 fission/node-env:0.4.0
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ Status code distribution:
|
|||||||
While the load is being generated, we will watch the HorizontalPodAutoscaler and how it scales over period of time. As you can notice, the number of pods is scaled from 1 to 3 after the load rises from 8 - 103%. After the load generator stops, it takes a few iterations to scale down from 3 to 1 pod.
|
While the load is being generated, we will watch the HorizontalPodAutoscaler and how it scales over period of time. As you can notice, the number of pods is scaled from 1 to 3 after the load rises from 8 - 103%. After the load generator stops, it takes a few iterations to scale down from 3 to 1 pod.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ k -n fission-function get hpa -w
|
$ kubectl -n fission-function get hpa -w
|
||||||
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
|
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
|
||||||
hello-qoxmothj Deployment/hello-qoxmothj 5% / 50% 1 6 1 3m
|
hello-qoxmothj Deployment/hello-qoxmothj 5% / 50% 1 6 1 3m
|
||||||
hello-qoxmothj Deployment/hello-qoxmothj 8% / 50% 1 6 1 3m
|
hello-qoxmothj Deployment/hello-qoxmothj 8% / 50% 1 6 1 3m
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Before creating a function the environment should be created, we will assume tha
|
|||||||
|
|
||||||
Let's create a simple code snippet in nodejs which will output Hello world:
|
Let's create a simple code snippet in nodejs which will output Hello world:
|
||||||
|
|
||||||
```
|
``` js
|
||||||
module.exports = async function(context) {
|
module.exports = async function(context) {
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -19,36 +19,65 @@ module.exports = async function(context) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Let's create a function based on pool based executor.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
$ fission fn create --name hello --code hello.js --env node --executortype poolmgr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Access function
|
||||||
|
|
||||||
|
Before accessing function, we need to set the `FISSION_ROUTER` environment variable first. It is needed for the examples below to work
|
||||||
|
|
||||||
|
#### Minikube
|
||||||
|
|
||||||
|
If you're using minikube, use these commands:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
$ export FISSION_ROUTER=$(minikube ip):$(kubectl -n fission get svc router -o jsonpath='{...nodePort}')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cloud setups
|
||||||
|
|
||||||
|
Save the external IP addresses of router services in FISSION_ROUTER.
|
||||||
|
|
||||||
|
Wait for services to get IP addresses (check this with `kubectl --namespace fission get svc`). Then:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# AWS
|
||||||
|
$ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..hostname}')
|
||||||
|
|
||||||
|
# GCP
|
||||||
|
$ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create a HTTP trigger
|
||||||
|
|
||||||
Let's create a route for the function which can be used for making HTTP requests:
|
Let's create a route for the function which can be used for making HTTP requests:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ fission route create --function hello --url /hello
|
$ fission route create --function hello --url /hello
|
||||||
trigger '5327e9a7-6d87-4533-a4fb-c67f55b1e492' created
|
trigger '5327e9a7-6d87-4533-a4fb-c67f55b1e492' created
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's create a function based on pool based executor.
|
|
||||||
|
|
||||||
```
|
|
||||||
fission fn create --name hello --code hello.js --env node --executortype poolmgr
|
|
||||||
```
|
|
||||||
|
|
||||||
When you hit this function's URL , you get a response:
|
When you hit this function's URL , you get a response:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ curl http://$FISSION_ROUTER/hello
|
$ curl http://$FISSION_ROUTER/hello
|
||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
|
|
||||||
Similarly you can create a new deployment executor type function and provide minmum and maximum scale for the function.
|
Similarly you can create a new deployment executor type function and provide minmum and maximum scale for the function.
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
fission fn create --name hello --code hello.js --env node --minscale 1 --maxscale 5 --executortype newdeploy
|
$ fission fn create --name hello --code hello.js --env node --minscale 1 --maxscale 5 --executortype newdeploy
|
||||||
```
|
```
|
||||||
|
|
||||||
### View & update function source code
|
### View & update function source code
|
||||||
|
|
||||||
You can look at the source code associated with given function:
|
You can look at the source code associated with given function:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ fission fn get --name hello
|
$ fission fn get --name hello
|
||||||
module.exports = async function(context) {
|
module.exports = async function(context) {
|
||||||
return {
|
return {
|
||||||
@@ -60,7 +89,7 @@ module.exports = async function(context) {
|
|||||||
|
|
||||||
Let's say you want to update the function to output "Hello Fission" instead of "Hello world", you can update the source file and update the source code for function:
|
Let's say you want to update the function to output "Hello Fission" instead of "Hello world", you can update the source file and update the source code for function:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ fission fn update --name hello --code ../hello.js
|
$ fission fn update --name hello --code ../hello.js
|
||||||
package 'hello-js-ku9s' updated
|
package 'hello-js-ku9s' updated
|
||||||
function 'hello' updated
|
function 'hello' updated
|
||||||
@@ -68,7 +97,7 @@ function 'hello' updated
|
|||||||
|
|
||||||
Let's verify that the function now respond with a different output than earlier:
|
Let's verify that the function now respond with a different output than earlier:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ curl http://$FISSION_ROUTER/hello
|
$ curl http://$FISSION_ROUTER/hello
|
||||||
Hello, Fission!
|
Hello, Fission!
|
||||||
```
|
```
|
||||||
@@ -76,13 +105,13 @@ Hello, Fission!
|
|||||||
### Test and debug function
|
### Test and debug function
|
||||||
|
|
||||||
You can directly test a function using test command. If the function call succeeds, it will output the function's response.
|
You can directly test a function using test command. If the function call succeeds, it will output the function's response.
|
||||||
```
|
``` bash
|
||||||
$ fission fn test --name hello
|
$ fission fn test --name hello
|
||||||
Hello, Fission!
|
Hello, Fission!
|
||||||
```
|
```
|
||||||
|
|
||||||
But if there is an error in function execution then the logs of function execution are displayed:
|
But if there is an error in function execution then the logs of function execution are displayed:
|
||||||
```
|
``` bash
|
||||||
$ fission fn test --name hello
|
$ fission fn test --name hello
|
||||||
Error calling function hello: 500 Internal server error (fission)
|
Error calling function hello: 500 Internal server error (fission)
|
||||||
|
|
||||||
@@ -97,7 +126,7 @@ user code load error: SyntaxError: Unexpected token function
|
|||||||
```
|
```
|
||||||
|
|
||||||
You can also look at function execution logs explicitly:
|
You can also look at function execution logs explicitly:
|
||||||
```
|
``` bash
|
||||||
$ fission fn logs --name hello
|
$ fission fn logs --name hello
|
||||||
[2018-02-16 08:41:43 +0000 UTC] 2018/02/16 08:41:43 fetcher received fetch request and started downloading: {1 {hello-js-rqew default 0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[] map[] [] nil [] } user [] []}
|
[2018-02-16 08:41:43 +0000 UTC] 2018/02/16 08:41:43 fetcher received fetch request and started downloading: {1 {hello-js-rqew default 0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[] map[] [] nil [] } user [] []}
|
||||||
[2018-02-16 08:41:43 +0000 UTC] 2018/02/16 08:41:43 Successfully placed at /userfunc/user
|
[2018-02-16 08:41:43 +0000 UTC] 2018/02/16 08:41:43 Successfully placed at /userfunc/user
|
||||||
@@ -119,15 +148,17 @@ You can attach the source/deployment packages to a function or explicitly create
|
|||||||
|
|
||||||
Let's take a simple python function which has dependency on a python pyyaml module. We can specify the dependencies in requirements.txt and a simple command to build from source. The tree structure of directory looks like:
|
Let's take a simple python function which has dependency on a python pyyaml module. We can specify the dependencies in requirements.txt and a simple command to build from source. The tree structure of directory looks like:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
sourcepkg/
|
sourcepkg/
|
||||||
├── __init__.py
|
├── __init__.py
|
||||||
├── build.sh
|
├── build.sh
|
||||||
├── requirements.txt
|
├── requirements.txt
|
||||||
└── user.py
|
└── user.py
|
||||||
```
|
```
|
||||||
|
|
||||||
And the file contents:
|
And the file contents:
|
||||||
```
|
|
||||||
|
``` bash
|
||||||
$ cat user.py
|
$ cat user.py
|
||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
@@ -152,13 +183,14 @@ pip3 install -r ${SRC_PKG}/requirements.txt -t ${SRC_PKG} && cp -r ${SRC_PKG} ${
|
|||||||
|
|
||||||
You first need to create an environment with environment image and python-builder image specified:
|
You first need to create an environment with environment image and python-builder image specified:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
$ fission env create --name python --image fission/python-env:latest --builder fission/python-builder:latest --mincpu 40 --maxcpu 80 --minmemory 64 --maxmemory 128 --poolsize 2
|
||||||
```
|
```
|
||||||
$fission env create --name python --image fission/python-env:latest --builder fission/python-builder:latest --mincpu 40 --maxcpu 80 --minmemory 64 --maxmemory 128 --poolsize 2
|
|
||||||
```
|
|
||||||
Now let's zip the directory containing the source files and create a function with source package:
|
Now let's zip the directory containing the source files and create a function with source package:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$zip -jr demo-src-pkg.zip sourcepkg/
|
$ zip -jr demo-src-pkg.zip sourcepkg/
|
||||||
adding: __init__.py (stored 0%)
|
adding: __init__.py (stored 0%)
|
||||||
adding: build.sh (deflated 24%)
|
adding: build.sh (deflated 24%)
|
||||||
adding: requirements.txt (stored 0%)
|
adding: requirements.txt (stored 0%)
|
||||||
@@ -169,10 +201,11 @@ function 'hellopy' created
|
|||||||
|
|
||||||
$ fission route create --function hellopy --url /hellopy
|
$ fission route create --function hellopy --url /hellopy
|
||||||
```
|
```
|
||||||
|
|
||||||
Once we create the function, the build process is started. You can check logs of the builder in fission-builder namespace:
|
Once we create the function, the build process is started. You can check logs of the builder in fission-builder namespace:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ k -n fission-builder logs -f py3-4214348-59555d9bd8-ks7m4 builder
|
$ kubectl -n fission-builder logs -f py3-4214348-59555d9bd8-ks7m4 builder
|
||||||
2018/02/16 11:44:21 Builder received request: {demo-src-pkg-zip-ninf-djtswo ./build.sh}
|
2018/02/16 11:44:21 Builder received request: {demo-src-pkg-zip-ninf-djtswo ./build.sh}
|
||||||
2018/02/16 11:44:21 Starting build...
|
2018/02/16 11:44:21 Starting build...
|
||||||
|
|
||||||
@@ -189,8 +222,8 @@ Successfully installed pyyaml-3.12
|
|||||||
```
|
```
|
||||||
|
|
||||||
Once the build has succeeded, you can hit the function URL to test the function:
|
Once the build has succeeded, you can hit the function URL to test the function:
|
||||||
```
|
``` bash
|
||||||
$curl http://$FISSION_ROUTER/hellopy
|
$ curl http://$FISSION_ROUTER/hellopy
|
||||||
a: 1
|
a: 1
|
||||||
b: {c: 3, d: 4}
|
b: {c: 3, d: 4}
|
||||||
```
|
```
|
||||||
@@ -201,7 +234,7 @@ In some cases you have a pre-built deployment package which you need to deploy t
|
|||||||
|
|
||||||
We will use a simple python file in a directory and turn it into a deployment package:
|
We will use a simple python file in a directory and turn it into a deployment package:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ cat testDir/hello.py
|
$ cat testDir/hello.py
|
||||||
def main():
|
def main():
|
||||||
return "Hello, world!"
|
return "Hello, world!"
|
||||||
@@ -209,9 +242,10 @@ def main():
|
|||||||
$zip -jr demo-deploy-pkg.zip testDir/
|
$zip -jr demo-deploy-pkg.zip testDir/
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's use the deployment package to create a function and route and then test it.
|
Let's use the deployment package to create a function and route and then test it.
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ fission fn create --name hellopy --env python --deploy demo-deploy-pkg.zip --entrypoint "hello.main"
|
$ fission fn create --name hellopy --env python --deploy demo-deploy-pkg.zip --entrypoint "hello.main"
|
||||||
function 'hellopy' created
|
function 'hellopy' created
|
||||||
|
|
||||||
@@ -225,7 +259,7 @@ Hello, world!
|
|||||||
|
|
||||||
You can retrieve metadata information of a single function or list all functions to look at basic information of functions:
|
You can retrieve metadata information of a single function or list all functions to look at basic information of functions:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
$ fission fn getmeta --name hello
|
$ fission fn getmeta --name hello
|
||||||
NAME UID ENV
|
NAME UID ENV
|
||||||
hello 34234b50-12f5-11e8-85c9-42010aa00010 node
|
hello 34234b50-12f5-11e8-85c9-42010aa00010 node
|
||||||
@@ -234,5 +268,4 @@ $ fission fn list
|
|||||||
NAME UID ENV EXECUTORTYPE MINSCALE MAXSCALE TARGETCPU
|
NAME UID ENV EXECUTORTYPE MINSCALE MAXSCALE TARGETCPU
|
||||||
hello 34234b50-12f5-11e8-85c9-42010aa00010 node poolmgr 0 1 80
|
hello 34234b50-12f5-11e8-85c9-42010aa00010 node poolmgr 0 1 80
|
||||||
hello2 e37a46e3-12f4-11e8-85c9-42010aa00010 node newdeploy 1 5 80
|
hello2 e37a46e3-12f4-11e8-85c9-42010aa00010 node newdeploy 1 5 80
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -123,7 +123,7 @@ Build Logs:
|
|||||||
Finally you can create a function with the package and test the function:
|
Finally you can create a function with the package and test the function:
|
||||||
|
|
||||||
```
|
```
|
||||||
$fission fn create --name deploypy --pkg demo-deploy-pkg-zip-whzl --entrypoint "hello.main"
|
$ fission fn create --name deploypy --pkg demo-deploy-pkg-zip-whzl --entrypoint "hello.main"
|
||||||
|
|
||||||
$curl http://$FISSION_ROUTER/deploypy
|
$curl http://$FISSION_ROUTER/deploypy
|
||||||
Hello, world!
|
Hello, world!
|
||||||
|
|||||||
@@ -0,0 +1,209 @@
|
|||||||
|
# Testing Proposal
|
||||||
|
|
||||||
|
This proposal was initially started as a upgrade testing proposal but soon problems that were posed resulted in a bigger proposal.
|
||||||
|
### Kinds of testing
|
||||||
|
|
||||||
|
Most of current integration tests are CLI driven. Fission CLI is used to test execute various test cases. In future we would have to also focus on API level testing as a UI is built for Fission.
|
||||||
|
## Needs & patterns
|
||||||
|
|
||||||
|
This section only explains the problems/best practices without going into tooling and language used for implementation.
|
||||||
|
### Separating the test & data
|
||||||
|
|
||||||
|
Seperating the tests from test data has two aspects - one is separation of concerns and second is scaling the tests without touching the test logic. The test data is a simple data structure which holds all information and test can take data and execute the logic.
|
||||||
|
|
||||||
|
As an example today we test "Hello world" for nodejs environment with a simple hello.js like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
fission env create --name nodejs --image fission/node-env
|
||||||
|
fission fn create --name $fn --env nodejs --code $ROOT/examples/nodejs/hello.js
|
||||||
|
fission route create --function $fn --url /$fn --method GET
|
||||||
|
response=$(curl http://$FISSION_ROUTER/$fn)
|
||||||
|
```
|
||||||
|
The variables here are environment image, function code & route URL.
|
||||||
|
|
||||||
|
If tomorrow if we had to scale this test for all environments, we will have to repeat ourselves. (Violate DRY principle). Instead of that if we encapsulate the test setup & test in a simple function:
|
||||||
|
|
||||||
|
```
|
||||||
|
test_hello_env(envImage, codePath, routeURL){
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And feed it with a dictionary which has all possible combination of tests:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
node: {"fission/node-env", "test/hello.js", "/hellonode"},
|
||||||
|
python: {"fission/python-env", "test/hello.py", "/hellopy"},
|
||||||
|
golang: {"fission/go-env", "test/hello.go", "/hellogo"},
|
||||||
|
binray: {"fission/binary-env", "test/hello.sh", "/hellobinary"},
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
This would achieve a few things:
|
||||||
|
|
||||||
|
- Separate the test execution logic from the data it needs clearly.
|
||||||
|
- For adding new kind of environments, you just need to add one more entry into data structure.
|
||||||
|
|
||||||
|
Testing all environments may not be most apt example for this, but there can be potential use cases like this.
|
||||||
|
### Separating the test & setup/teardown
|
||||||
|
|
||||||
|
When we run a test there are typically three distinct phases:
|
||||||
|
|
||||||
|
- Setup (Create env, fn, route)
|
||||||
|
- Test (Curl the function)
|
||||||
|
- Cleanup (Delete fn, route & env)
|
||||||
|
|
||||||
|
It should be possible to separate the before test and after test parts from actual tests at two levels:
|
||||||
|
- Each test
|
||||||
|
- A whole test suite
|
||||||
|
|
||||||
|
The ability to have clean and separate before and after blocks, apart from separation of concerns, enables:
|
||||||
|
|
||||||
|
- Running a suite of tests for same setup (See tagging for suite of tests)
|
||||||
|
### Tagging tests & running a selection
|
||||||
|
|
||||||
|
Over a period of time as tests grow, there will be unit, smkoe, integration, performance, soak tests and so on. Ability to run a perticular test suite only or a combination of them makes it easy to run for specific purpose.
|
||||||
|
### Measuring test times
|
||||||
|
|
||||||
|
[Good to have, not a must] Measuring time for tests and reporting somewhere helps over time to monitor trends. Although this job is better done by performance/benchmark tests so it is not a strict requirement
|
||||||
|
|
||||||
|
### Cleaner Logging
|
||||||
|
It would be good to have cleaner/relevant logging as part of build & test. For example something that Ginkgo framework does is it shows error logs only for failed tests.
|
||||||
|
### Tests in Parallel
|
||||||
|
|
||||||
|
It would be good to be able to run tests in parallel.
|
||||||
|
|
||||||
|
## Evalutaing the tools/alternatives
|
||||||
|
### BATS
|
||||||
|
|
||||||
|
Bash Automated Testing System is like a enhanced version of bash with support for @test tags and before and after steps & ability to skip tests etc. While it enhances the bash to certain extent, the overall improvement is only marginal.
|
||||||
|
|
||||||
|
```
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
@test "addition using bc" {
|
||||||
|
result="$(echo 2+2 | bc)"
|
||||||
|
[ "$result" -eq 4 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
$ bats addition.bats
|
||||||
|
✓ addition using bc
|
||||||
|
✓ addition using dc
|
||||||
|
|
||||||
|
2 tests, 0 failures
|
||||||
|
|
||||||
|
```
|
||||||
|
#### Links
|
||||||
|
- Bats repo: https://github.com/sstephenson/bats
|
||||||
|
- Runc uses Bats https://github.com/opencontainers/runc/tree/master/tests/integration
|
||||||
|
### Go Test
|
||||||
|
|
||||||
|
The testing package of Go also is quite feature rich for most of the use cases we need. Go 1.7 onwards there is support for setup & teardown parts and parallelism etc.
|
||||||
|
#### Go - Testing package
|
||||||
|
|
||||||
|
- Support for setup and teardown based on https://golang.org/pkg/testing/#hdr-Main
|
||||||
|
- Go testing already supports and has examples of table driven tests (Separating test & data), measuring test times and parallel tests
|
||||||
|
#### Shell execution: Go's Exec Library
|
||||||
|
GO provides a built in Exec library for working with CLI commands. The package seems good enough for us to work, though a few working examples will help decide better
|
||||||
|
|
||||||
|
https://golang.org/pkg/os/exec
|
||||||
|
|
||||||
|
### Using CLI package
|
||||||
|
|
||||||
|
Currently we build a CLI and then execute the tests. The tests basically call one of functions from CLI package. If we decide to use a go lang based framework, then we can import the CLI package and then call those functions by providing them context. This is as good as calling the Fission from CLI, with added benefit of programmibility of Go langugage.
|
||||||
|
|
||||||
|
```
|
||||||
|
func TestSomething(t *testing.T) {
|
||||||
|
// Build the Cli context with flags etc.
|
||||||
|
ctx := cli.Context{}
|
||||||
|
|
||||||
|
// Pass the ctx to create function
|
||||||
|
fnCreate(ctx)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Some of benefits of using above pattern are:
|
||||||
|
- We can build a small framework around above core where we can pass various flag combinations etc. and exercise all flags in great detail
|
||||||
|
- We can use rest of Go testing library and other libraries to build matchers, looping, parallelism etc.
|
||||||
|
- It allows us to exercise the logic in CLI as well as validate the API at the same time.
|
||||||
|
|
||||||
|
### Ginkgo & Gomega
|
||||||
|
|
||||||
|
Ginkgo is a BDD framework which works with Gomega matcher library. I will state relevant portions of these two frameworks which can be utilized:
|
||||||
|
|
||||||
|
From Ginkgo:
|
||||||
|
|
||||||
|
- Global `BeforeSuite` and after `AfterSuite` can be used to have global setup and tear down phases
|
||||||
|
- For tests `BeforeEach` and `AfterEach` and more such varients to do before and after test tasks.
|
||||||
|
|
||||||
|
From Gomega:
|
||||||
|
|
||||||
|
Gomega is a matcher library but the `gexec` library makes it really easy to interact with OS execution environment. Some working examples:
|
||||||
|
|
||||||
|
- Build and cleanup the Fission CLI before & after the tests
|
||||||
|
```
|
||||||
|
var fissionCli string
|
||||||
|
|
||||||
|
BeforeSuite(func() {
|
||||||
|
var err error
|
||||||
|
fissionCli, err = gexec.Build("github.com/fission/fission")
|
||||||
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterSuite(func() {
|
||||||
|
gexec.CleanupBuildArtifacts()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- Following will run Fission commands with Fission CLI and print error if there is one (verbosity is configurable)
|
||||||
|
|
||||||
|
```
|
||||||
|
command := exec.Command(fissionCli, "fission env create --name nodejs --image fission/node-env")
|
||||||
|
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||||
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use Fission CLI's output to validate test results
|
||||||
|
|
||||||
|
```
|
||||||
|
Eventually(session.Out).Should(gbytes.Say("hello [A-Za-z], world"))
|
||||||
|
```
|
||||||
|
#### Gomega Matchers
|
||||||
|
|
||||||
|
- Gomega provides quite a few built in matchers - so you don't have to code those small usual checks, for example:
|
||||||
|
```
|
||||||
|
Ω(ACTUAL).Should(BeTrue()) // The output should be true
|
||||||
|
|
||||||
|
Ω(ACTUAL).Should(BeAnExistingFile()) // The file should already exist
|
||||||
|
|
||||||
|
```
|
||||||
|
There are many more matchers which cane be found here: http://onsi.github.io/gomega/#provided-matchers
|
||||||
|
|
||||||
|
- We can build custom mathers in Go language for reusable logic.
|
||||||
|
|
||||||
|
#### Links
|
||||||
|
Ginkgo: http://onsi.github.io/ginkgo/
|
||||||
|
Gomega: http://onsi.github.io/gomega/
|
||||||
|
## Thoughts & Next actions
|
||||||
|
|
||||||
|
Based on the discussion with team, here are current thoughts and next action items:
|
||||||
|
|
||||||
|
### Thoughts
|
||||||
|
- As far as possible we should stick to Go's built in testing package
|
||||||
|
- Ginkgo's cleaner logging feature (Onlu log if there are errors) - is very useful. We can decide to incorporate this in future.
|
||||||
|
- Gomega's (gexec)[http://onsi.github.io/gomega/#gexec-testing-external-processes] is really neat and some matchers can be used if necessary
|
||||||
|
|
||||||
|
### Action items
|
||||||
|
- How will upgrade test for Fission fit in the framework?
|
||||||
|
- How will migration of tests happen over time:
|
||||||
|
- Aim is to keep existing tests around so that enough validation is in place
|
||||||
|
- May be migrate one test at a time
|
||||||
|
- How much of current setup etc. will move into framework? For example it is clear that helm commands should be part of test framework as part of setup/teardown. But other sections may or may not be. A RCA needs to be done to analyze and come up with clear demarkation.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- EngineYard uses BATS: https://www.engineyard.com/blog/bats-test-command-line-tools
|
||||||
|
- AWS CLI Tests, written in Python (CLI itself is also in Python): https://github.com/aws/aws-cli/tree/develop/tests
|
||||||
|
- Kubernetes uses Ginkgo and Gomega extensively: https://github.com/kubernetes/kubernetes/search?l=Go&q=onsi&type=
|
||||||
|
- Hashicorp's Mitchell's talk on Advanced testing with go talks about some good patterns to use: https://www.youtube.com/watch?v=8hQG7QlcLBk
|
||||||
@@ -88,7 +88,7 @@ Usage
|
|||||||
$ fission route create --method GET --url /hello --function hello
|
$ fission route create --method GET --url /hello --function hello
|
||||||
|
|
||||||
# Run the function. This takes about 100msec the first time.
|
# Run the function. This takes about 100msec the first time.
|
||||||
$ curl http://$FISSION_ROUTER/hello
|
$ fission function test --name hello
|
||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -97,12 +97,12 @@ See the [examples](examples) directory for more.
|
|||||||
Running Fission on your Cluster
|
Running Fission on your Cluster
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
See the [installation guide](http://fission.io/docs/latest/installation/).
|
See the [installation guide](http://docs.fission.io/latest/installation).
|
||||||
|
|
||||||
Compiling Fission
|
Compiling Fission
|
||||||
=================
|
=================
|
||||||
|
|
||||||
See the [compilation guide](http://fission.io/docs/latest/compilation/).
|
See the [compilation guide](http://docs.fission.io/latest/contributing/compiling/).
|
||||||
|
|
||||||
Status
|
Status
|
||||||
======
|
======
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ func Start(storageSvcUrl string, envBuilderNamespace string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = fissionClient.WaitForCRDs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error waiting for CRDs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
envWatcher := makeEnvironmentWatcher(fissionClient, kubernetesClient, envBuilderNamespace)
|
envWatcher := makeEnvironmentWatcher(fissionClient, kubernetesClient, envBuilderNamespace)
|
||||||
go envWatcher.watchEnvironments()
|
go envWatcher.watchEnvironments()
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ const (
|
|||||||
LABEL_ENV_RESOURCEVERSION = "envResourceVersion"
|
LABEL_ENV_RESOURCEVERSION = "envResourceVersion"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
deletePropagation = metav1.DeletePropagationBackground
|
||||||
|
delOpt = metav1.DeleteOptions{PropagationPolicy: &deletePropagation}
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
builderInfo struct {
|
builderInfo struct {
|
||||||
envMetadata *metav1.ObjectMeta
|
envMetadata *metav1.ObjectMeta
|
||||||
@@ -341,17 +346,9 @@ func (envw *environmentWatcher) deleteBuilderService(sel map[string]string) erro
|
|||||||
}
|
}
|
||||||
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)
|
||||||
|
|
||||||
// cascading deletion
|
|
||||||
// https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/
|
|
||||||
falseVal := false
|
|
||||||
delOpt := &metav1.DeleteOptions{
|
|
||||||
OrphanDependents: &falseVal,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = envw.kubernetesClient.
|
err = envw.kubernetesClient.
|
||||||
Services(envw.builderNamespace).
|
Services(envw.builderNamespace).
|
||||||
Delete(svc.ObjectMeta.Name, delOpt)
|
Delete(svc.ObjectMeta.Name, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error deleting builder service: %v", err)
|
return fmt.Errorf("Error deleting builder service: %v", err)
|
||||||
}
|
}
|
||||||
@@ -366,15 +363,9 @@ func (envw *environmentWatcher) deleteBuilderDeployment(sel map[string]string) e
|
|||||||
}
|
}
|
||||||
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)
|
||||||
|
|
||||||
falseVal := false
|
|
||||||
delOpt := &metav1.DeleteOptions{
|
|
||||||
OrphanDependents: &falseVal,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = envw.kubernetesClient.ExtensionsV1beta1().
|
err = envw.kubernetesClient.ExtensionsV1beta1().
|
||||||
Deployments(envw.builderNamespace).
|
Deployments(envw.builderNamespace).
|
||||||
Delete(deploy.ObjectMeta.Name, delOpt)
|
Delete(deploy.ObjectMeta.Name, &delOpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error deleteing builder deployment: %v", err)
|
return fmt.Errorf("Error deleteing builder deployment: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,28 +67,28 @@ func makePackageWatcher(fissionClient *crd.FissionClient, getter k8sCache.Getter
|
|||||||
// 5. Update package resource in package ref of functions that share the same package
|
// 5. Update package resource in package ref of functions that share the same package
|
||||||
// 6. Update package status to succeed state
|
// 6. Update package status to succeed state
|
||||||
// *. Update package status to failed state,if any one of steps above failed/time out
|
// *. Update package status to failed state,if any one of steps above failed/time out
|
||||||
func (pkgw *packageWatcher) build(buildCache *cache.Cache, pkg *crd.Package) {
|
func (pkgw *packageWatcher) build(buildCache *cache.Cache, srcpkg *crd.Package) {
|
||||||
|
|
||||||
// Ignore non-pending state packages.
|
// Ignore non-pending state packages.
|
||||||
if pkg.Status.BuildStatus != fission.BuildStatusPending {
|
if srcpkg.Status.BuildStatus != fission.BuildStatusPending {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore duplicate build requests
|
// Ignore duplicate build requests
|
||||||
key := fmt.Sprintf("%v-%v", pkg.Metadata.Name, pkg.Metadata.ResourceVersion)
|
key := fmt.Sprintf("%v-%v", srcpkg.Metadata.Name, srcpkg.Metadata.ResourceVersion)
|
||||||
err, _ := buildCache.Set(key, pkg)
|
err, _ := buildCache.Set(key, srcpkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer buildCache.Delete(key)
|
defer buildCache.Delete(key)
|
||||||
|
|
||||||
log.Printf("Start build for package %v with resource version %v", pkg.Metadata.Name, pkg.Metadata.ResourceVersion)
|
log.Printf("Start build for package %v with resource version %v", srcpkg.Metadata.Name, srcpkg.Metadata.ResourceVersion)
|
||||||
|
|
||||||
pkg, err = updatePackage(pkgw.fissionClient, pkg, fission.BuildStatusRunning, "", nil)
|
pkg, err := updatePackage(pkgw.fissionClient, srcpkg, fission.BuildStatusRunning, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := fmt.Sprintf("Error setting package pending state: %v", err)
|
e := fmt.Sprintf("Error setting package pending state: %v", err)
|
||||||
log.Println(e)
|
log.Println(e)
|
||||||
updatePackage(pkgw.fissionClient, pkg, fission.BuildStatusFailed, e, nil)
|
updatePackage(pkgw.fissionClient, srcpkg, fission.BuildStatusFailed, e, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
name: fission-all
|
name: fission-all
|
||||||
version: 0.6.1
|
version: 0.7.1
|
||||||
description: Fission is a fast serverless framework for Kubernetes.
|
description: Fission is a fast serverless framework for Kubernetes.
|
||||||
keywords:
|
keywords:
|
||||||
- fission
|
- fission
|
||||||
@@ -12,4 +12,4 @@ maintainers:
|
|||||||
- name: Ta Ching Chen
|
- name: Ta Ching Chen
|
||||||
email: contact@tachingchen.com
|
email: contact@tachingchen.com
|
||||||
engine: gotpl
|
engine: gotpl
|
||||||
appVersion: 0.6.1
|
appVersion: 0.7.1
|
||||||
|
|||||||
@@ -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.6.1/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
|
$ 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/
|
||||||
|
|
||||||
Linux:
|
Linux:
|
||||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.6.1/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
|
$ 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/
|
||||||
|
|
||||||
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.6.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.7.1/fission-cli-windows.exe
|
||||||
|
|
||||||
2. You're ready to use Fission!
|
2. You're ready to use Fission!
|
||||||
|
|
||||||
|
|||||||
@@ -139,8 +139,6 @@ spec:
|
|||||||
env:
|
env:
|
||||||
- name: FISSION_FUNCTION_NAMESPACE
|
- name: FISSION_FUNCTION_NAMESPACE
|
||||||
value: "{{ .Values.functionNamespace }}"
|
value: "{{ .Values.functionNamespace }}"
|
||||||
- name: ENABLE_ISTIO
|
|
||||||
value: "{{ .Values.enableIstio }}"
|
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/healthz"
|
path: "/healthz"
|
||||||
@@ -300,7 +298,7 @@ spec:
|
|||||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--kubewatcher"]
|
args: ["--kubewatcher", "--routerUrl", "http://router.{{ .Release.Namespace }}"]
|
||||||
serviceAccount: fission-svc
|
serviceAccount: fission-svc
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -469,7 +467,7 @@ spec:
|
|||||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--timer"]
|
args: ["--timer", "--routerUrl", "http://router.{{ .Release.Namespace }}"]
|
||||||
serviceAccount: fission-svc
|
serviceAccount: fission-svc
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -547,7 +545,7 @@ spec:
|
|||||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--mqt"]
|
args: ["--mqt", "--routerUrl", "http://router.{{ .Release.Namespace }}"]
|
||||||
env:
|
env:
|
||||||
- name: MESSAGE_QUEUE_TYPE
|
- name: MESSAGE_QUEUE_TYPE
|
||||||
value: {{ .Values.messageQueue.type }}
|
value: {{ .Values.messageQueue.type }}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
# This is what defines this resource as a hook. Without this line, the
|
# This is what defines this resource as a hook. Without this line, the
|
||||||
# job is considered part of the release.
|
# job is considered part of the release.
|
||||||
"helm.sh/hook": post-install,post-upgrade
|
"helm.sh/hook": post-install
|
||||||
"helm.sh/hook-delete-policy": hook-succeeded
|
"helm.sh/hook-delete-policy": hook-succeeded
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{{ if .Values.analytics }}
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: {{ template "fullname" . }}-{{ .Chart.Version }}
|
||||||
|
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": post-upgrade
|
||||||
|
"helm.sh/hook-delete-policy": hook-succeeded
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: {{ template "fullname" . }}
|
||||||
|
labels:
|
||||||
|
release: {{ .Release.Name }}
|
||||||
|
app: {{ template "name" . }}
|
||||||
|
annotations:
|
||||||
|
{{- if .Values.enableIstio }}
|
||||||
|
"sidecar.istio.io/inject": "false"
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: post-upgrade-job
|
||||||
|
image: "fission/alpinecurl"
|
||||||
|
command: ["sh", "-c", "/usr/bin/curl -m 5 -H \"Content-Type: application/json\" -X POST -d '{\"type\": \"helm-post-upgrade\", \"chartName\": \"{{ .Chart.Name }}\", \"chartVersion\": \"{{ .Chart.Version }}\"}' https://g.fission.sh/analytics || true"]
|
||||||
|
{{ end }}
|
||||||
@@ -16,13 +16,13 @@ image: fission/fission-bundle
|
|||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
## Fission image version
|
## Fission image version
|
||||||
imageTag: 0.6.1
|
imageTag: 0.7.1
|
||||||
|
|
||||||
## Fission fetcher repository
|
## Fission fetcher repository
|
||||||
fetcherImage: fission/fetcher
|
fetcherImage: fission/fetcher
|
||||||
|
|
||||||
## Fission fetcher image version
|
## Fission fetcher image version
|
||||||
fetcherImageTag: 0.6.1
|
fetcherImageTag: 0.7.1
|
||||||
|
|
||||||
## 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.6.1
|
fluentdImageTag: 0.7.1
|
||||||
|
|
||||||
## 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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
name: fission-core
|
name: fission-core
|
||||||
version: 0.6.1
|
version: 0.7.1
|
||||||
description: Fission is a fast serverless framework for Kubernetes.
|
description: Fission is a fast serverless framework for Kubernetes.
|
||||||
keywords:
|
keywords:
|
||||||
- fission
|
- fission
|
||||||
@@ -12,4 +12,4 @@ maintainers:
|
|||||||
- name: Ta Ching Chen
|
- name: Ta Ching Chen
|
||||||
email: contact@tachingchen.com
|
email: contact@tachingchen.com
|
||||||
engine: gotpl
|
engine: gotpl
|
||||||
appVersion: 0.6.1
|
appVersion: 0.7.1
|
||||||
|
|||||||
@@ -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.6.1/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
|
$ 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/
|
||||||
|
|
||||||
Linux:
|
Linux:
|
||||||
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.6.1/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
|
$ 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/
|
||||||
|
|
||||||
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.6.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.7.1/fission-cli-windows.exe
|
||||||
|
|
||||||
2. You're ready to use Fission!
|
2. You're ready to use Fission!
|
||||||
|
|
||||||
|
|||||||
@@ -139,8 +139,6 @@ spec:
|
|||||||
env:
|
env:
|
||||||
- name: FISSION_FUNCTION_NAMESPACE
|
- name: FISSION_FUNCTION_NAMESPACE
|
||||||
value: "{{ .Values.functionNamespace }}"
|
value: "{{ .Values.functionNamespace }}"
|
||||||
- name: ENABLE_ISTIO
|
|
||||||
value: "{{ .Values.enableIstio }}"
|
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/healthz"
|
path: "/healthz"
|
||||||
@@ -298,7 +296,7 @@ spec:
|
|||||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--kubewatcher"]
|
args: ["--kubewatcher", "--routerUrl", "http://router.{{ .Release.Namespace }}"]
|
||||||
serviceAccount: fission-svc
|
serviceAccount: fission-svc
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -320,7 +318,7 @@ spec:
|
|||||||
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
|
||||||
imagePullPolicy: {{ .Values.pullPolicy }}
|
imagePullPolicy: {{ .Values.pullPolicy }}
|
||||||
command: ["/fission-bundle"]
|
command: ["/fission-bundle"]
|
||||||
args: ["--timer"]
|
args: ["--timer", "--routerUrl", "http://router.{{ .Release.Namespace }}"]
|
||||||
serviceAccount: fission-svc
|
serviceAccount: fission-svc
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
# This is what defines this resource as a hook. Without this line, the
|
# This is what defines this resource as a hook. Without this line, the
|
||||||
# job is considered part of the release.
|
# job is considered part of the release.
|
||||||
"helm.sh/hook": post-install,post-upgrade
|
"helm.sh/hook": post-install
|
||||||
"helm.sh/hook-delete-policy": hook-succeeded
|
"helm.sh/hook-delete-policy": hook-succeeded
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{{ if .Values.analytics }}
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: {{ template "fullname" . }}-{{ .Chart.Version }}
|
||||||
|
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": post-upgrade
|
||||||
|
"helm.sh/hook-delete-policy": hook-succeeded
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: {{ template "fullname" . }}
|
||||||
|
labels:
|
||||||
|
release: {{ .Release.Name }}
|
||||||
|
app: {{ template "name" . }}
|
||||||
|
annotations:
|
||||||
|
{{- if .Values.enableIstio }}
|
||||||
|
"sidecar.istio.io/inject": "false"
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: post-upgrade-job
|
||||||
|
image: "fission/alpinecurl"
|
||||||
|
command: ["sh", "-c", "/usr/bin/curl -m 5 -H \"Content-Type: application/json\" -X POST -d '{\"type\": \"helm-post-upgrade\", \"chartName\": \"{{ .Chart.Name }}\", \"chartVersion\": \"{{ .Chart.Version }}\"}' https://g.fission.sh/analytics || true"]
|
||||||
|
{{ end }}
|
||||||
@@ -13,7 +13,7 @@ routerServiceType: LoadBalancer
|
|||||||
image: fission/fission-bundle
|
image: fission/fission-bundle
|
||||||
|
|
||||||
## Fission image version
|
## Fission image version
|
||||||
imageTag: 0.6.1
|
imageTag: 0.7.1
|
||||||
|
|
||||||
## 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.6.1
|
fetcherImageTag: 0.7.1
|
||||||
|
|
||||||
## Port at which Fission controller service should be exposed
|
## Port at which Fission controller service should be exposed
|
||||||
controllerPort: 31313
|
controllerPort: 31313
|
||||||
|
|||||||
@@ -87,3 +87,35 @@ func MergeContainerSpecs(specs ...*apiv1.Container) apiv1.Container {
|
|||||||
}
|
}
|
||||||
return *result
|
return *result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNetworkDialError returns true if its a network dial error
|
||||||
|
func IsNetworkDialError(err error) bool {
|
||||||
|
netErr, ok := err.(net.Error)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
netOpErr, ok := netErr.(*net.OpError)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if netOpErr.Op == "dial" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReadyPod checks that all containers in a pod are ready and returns true if so
|
||||||
|
func IsReadyPod(pod *apiv1.Pod) bool {
|
||||||
|
// since its a utility function, just ensuring there is no nil pointer exception
|
||||||
|
if pod == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cStatus := range pod.Status.ContainerStatuses {
|
||||||
|
if !cStatus.Ready {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
@@ -83,14 +82,6 @@ func MakeAPI() (*API, error) {
|
|||||||
api.functionNamespace = "fission-function"
|
api.functionNamespace = "fission-function"
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(os.Getenv("ENABLE_ISTIO")) > 0 {
|
|
||||||
istio, err := strconv.ParseBool(os.Getenv("ENABLE_ISTIO"))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Failed to parse ENABLE_ISTIO")
|
|
||||||
}
|
|
||||||
api.useIstio = istio
|
|
||||||
}
|
|
||||||
|
|
||||||
return api, err
|
return api, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-4
@@ -24,6 +24,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -69,9 +70,8 @@ func assertNotFoundFailure(err error, name string) {
|
|||||||
|
|
||||||
func assertCronSpecFails(err error) {
|
func assertCronSpecFails(err error) {
|
||||||
assert(err != nil, "using an invalid cron spec must fail")
|
assert(err != nil, "using an invalid cron spec must fail")
|
||||||
fe, ok := err.(fission.Error)
|
ok := strings.Contains(err.Error(), "not a valid cron spec")
|
||||||
assert(ok, "error must be a fission Error")
|
assert(ok, "invalid cron spec must fail")
|
||||||
assert(fe.Code == fission.ErrorInvalidArgument, "error must be a invalid argument error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFunctionApi(t *testing.T) {
|
func TestFunctionApi(t *testing.T) {
|
||||||
@@ -82,10 +82,16 @@ func TestFunctionApi(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Spec: fission.FunctionSpec{
|
Spec: fission.FunctionSpec{
|
||||||
Environment: fission.EnvironmentReference{
|
Environment: fission.EnvironmentReference{
|
||||||
Name: "nodejs",
|
Name: "nodejs",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
},
|
},
|
||||||
Package: fission.FunctionPackageRef{
|
Package: fission.FunctionPackageRef{
|
||||||
FunctionName: "xxx",
|
FunctionName: "xxx",
|
||||||
|
PackageRef: fission.PackageRef{
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
Name: "xxx",
|
||||||
|
ResourceVersion: "12345",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -142,6 +148,7 @@ func TestHTTPTriggerApi(t *testing.T) {
|
|||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
},
|
},
|
||||||
Spec: fission.HTTPTriggerSpec{
|
Spec: fission.HTTPTriggerSpec{
|
||||||
|
Method: http.MethodGet,
|
||||||
RelativeURL: "/hello",
|
RelativeURL: "/hello",
|
||||||
FunctionReference: fission.FunctionReference{
|
FunctionReference: fission.FunctionReference{
|
||||||
Type: fission.FunctionReferenceTypeFunctionName,
|
Type: fission.FunctionReferenceTypeFunctionName,
|
||||||
|
|||||||
@@ -24,10 +24,16 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) EnvironmentCreate(env *crd.Environment) (*metav1.ObjectMeta, error) {
|
func (c *Client) EnvironmentCreate(env *crd.Environment) (*metav1.ObjectMeta, error) {
|
||||||
|
err := env.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("Environment", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(env)
|
reqbody, err := json.Marshal(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -78,6 +84,11 @@ 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()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("Environment", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(env)
|
reqbody, err := json.Marshal(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -24,10 +24,15 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) FunctionCreate(f *crd.Function) (*metav1.ObjectMeta, error) {
|
func (c *Client) FunctionCreate(f *crd.Function) (*metav1.ObjectMeta, error) {
|
||||||
|
err := f.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("Function", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(f)
|
reqbody, err := json.Marshal(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,6 +98,11 @@ 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()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("Function", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(f)
|
reqbody, err := json.Marshal(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -24,10 +24,16 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) HTTPTriggerCreate(t *crd.HTTPTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) HTTPTriggerCreate(t *crd.HTTPTrigger) (*metav1.ObjectMeta, error) {
|
||||||
|
err := t.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("HTTPTrigger", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -78,6 +84,11 @@ 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()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("HTTPTrigger", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) WatchCreate(w *crd.KubernetesWatchTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) WatchCreate(w *crd.KubernetesWatchTrigger) (*metav1.ObjectMeta, error) {
|
||||||
|
err := w.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("KubernetesWatchTrigger", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(w)
|
reqbody, err := json.Marshal(w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -24,10 +24,16 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) MessageQueueTriggerCreate(t *crd.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) MessageQueueTriggerCreate(t *crd.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
|
||||||
|
err := t.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("MessageQueueTrigger", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -78,6 +84,11 @@ 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()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("MessageQueueTrigger", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(mqTrigger)
|
reqbody, err := json.Marshal(mqTrigger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -24,10 +24,15 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) PackageCreate(f *crd.Package) (*metav1.ObjectMeta, error) {
|
func (c *Client) PackageCreate(f *crd.Package) (*metav1.ObjectMeta, error) {
|
||||||
|
err := f.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("Package", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(f)
|
reqbody, err := json.Marshal(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -79,6 +84,11 @@ 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()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("Package", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(f)
|
reqbody, err := json.Marshal(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -24,10 +24,16 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) TimeTriggerCreate(t *crd.TimeTrigger) (*metav1.ObjectMeta, error) {
|
func (c *Client) TimeTriggerCreate(t *crd.TimeTrigger) (*metav1.ObjectMeta, error) {
|
||||||
|
err := t.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("TimeTrigger", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -78,6 +84,11 @@ 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()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fission.AggregateValidationErrors("TimeTrigger", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqbody, err := json.Marshal(t)
|
reqbody, err := json.Marshal(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ func Start(port int) {
|
|||||||
log.Fatalf("Failed to create fission CRDs: %v", err)
|
log.Fatalf("Failed to create fission CRDs: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fc.WaitForCRDs()
|
err = fc.WaitForCRDs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error waiting for CRDs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
api, err := MakeAPI()
|
api, err := MakeAPI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ limitations under the License.
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,11 +27,3 @@ func makeCRDBackedAPI() (*API, error) {
|
|||||||
}
|
}
|
||||||
return &API{fissionClient: fissionClient, kubernetesClient: kubernetesClient}, nil
|
return &API{fissionClient: fissionClient, kubernetesClient: kubernetesClient}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateResourceName(name string) error {
|
|
||||||
re := regexp.MustCompile(`[a-z0-9]([-a-z0-9]*[a-z0-9])?`)
|
|
||||||
if len(re.FindString(name)) != len(name) {
|
|
||||||
return errors.New("Name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?'")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -60,12 +60,6 @@ func (a *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateResourceName(env.Metadata.Name)
|
|
||||||
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)
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
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/util/intstr"
|
|
||||||
"k8s.io/client-go/pkg/api"
|
"k8s.io/client-go/pkg/api"
|
||||||
apiv1 "k8s.io/client-go/pkg/api/v1"
|
apiv1 "k8s.io/client-go/pkg/api/v1"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
@@ -75,12 +73,6 @@ func (a *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateResourceName(f.Metadata.Name)
|
|
||||||
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)
|
||||||
@@ -93,62 +85,6 @@ func (a *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since istio only allows accessing pod through k8s service,
|
|
||||||
// for the functions with executor type "poolmgr" we need to
|
|
||||||
// create a service for sending requests to pod in pool.
|
|
||||||
// Functions with executor type "Newdeploy" is specialized at
|
|
||||||
// pod starts. In this case, just ignore such functions.
|
|
||||||
fnExecutorType := f.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
|
|
||||||
|
|
||||||
if a.useIstio && fnExecutorType == fission.ExecutorTypePoolmgr {
|
|
||||||
// create a same name service for function
|
|
||||||
// since istio only allows the traffic to service
|
|
||||||
|
|
||||||
sel := map[string]string{
|
|
||||||
"functionName": fnew.Metadata.Name,
|
|
||||||
"functionUid": string(fnew.Metadata.UID),
|
|
||||||
}
|
|
||||||
|
|
||||||
// service for accepting user traffic
|
|
||||||
svc := apiv1.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Namespace: a.functionNamespace,
|
|
||||||
Name: fission.GetFunctionIstioServiceName(f.Metadata.Name, f.Metadata.Namespace),
|
|
||||||
Labels: a.getIstioServiceLabels(f.Metadata.Name),
|
|
||||||
},
|
|
||||||
Spec: apiv1.ServiceSpec{
|
|
||||||
Type: apiv1.ServiceTypeClusterIP,
|
|
||||||
Ports: []apiv1.ServicePort{
|
|
||||||
// Service port name should begin with a recognized prefix, or the traffic will be
|
|
||||||
// treated as TCP traffic. (https://istio.io/docs/setup/kubernetes/sidecar-injection.html)
|
|
||||||
// Originally the ports' name are similar to "http-fetch" and "http-specialize".
|
|
||||||
// But for istio 0.5.1, istio-proxy return unexpected 431 error with such naming.
|
|
||||||
// https://github.com/istio/istio/issues/928
|
|
||||||
// Workaround: remove prefix
|
|
||||||
// TODO: prepend prefix once the bug fixed
|
|
||||||
{
|
|
||||||
Name: "fetch",
|
|
||||||
Protocol: apiv1.ProtocolTCP,
|
|
||||||
Port: 8000,
|
|
||||||
TargetPort: intstr.FromInt(8000),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "specialize",
|
|
||||||
Protocol: apiv1.ProtocolTCP,
|
|
||||||
Port: 8888,
|
|
||||||
TargetPort: intstr.FromInt(8888),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Selector: sel,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, err = a.kubernetesClient.CoreV1().Services(a.functionNamespace).Create(&svc)
|
|
||||||
if err != nil {
|
|
||||||
a.respondWithError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
a.respondWithSuccess(w, resp)
|
a.respondWithSuccess(w, resp)
|
||||||
}
|
}
|
||||||
@@ -226,20 +162,6 @@ func (a *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.useIstio {
|
|
||||||
// delete all istio services belong to the function
|
|
||||||
sel := a.getIstioServiceLabels(name)
|
|
||||||
svcList, err := a.kubernetesClient.CoreV1().Services(a.functionNamespace).List(metav1.ListOptions{
|
|
||||||
LabelSelector: labels.Set(sel).AsSelector().String(),
|
|
||||||
})
|
|
||||||
for _, svc := range svcList.Items {
|
|
||||||
err = a.kubernetesClient.CoreV1().Services(a.functionNamespace).Delete(svc.ObjectMeta.Name, &metav1.DeleteOptions{})
|
|
||||||
// log error and continue
|
|
||||||
log.Printf("Failed to delete service %v: %v", svc.ObjectMeta.Name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a.respondWithSuccess(w, []byte(""))
|
a.respondWithSuccess(w, []byte(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,12 +74,6 @@ func (a *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateResourceName(t.Metadata.Name)
|
|
||||||
if err != nil {
|
|
||||||
a.respondWithError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we don't have a duplicate HTTP route defined (same URL and method)
|
// Ensure we don't have a duplicate HTTP route defined (same URL and method)
|
||||||
err = a.checkHTTPTriggerDuplicates(&t)
|
err = a.checkHTTPTriggerDuplicates(&t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -57,12 +57,6 @@ func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateResourceName(mqTrigger.Metadata.Name)
|
|
||||||
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)
|
||||||
|
|||||||
@@ -58,12 +58,6 @@ func (a *API) PackageApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateResourceName(f.Metadata.Name)
|
|
||||||
if err != nil {
|
|
||||||
a.respondWithError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure size limits
|
// Ensure size limits
|
||||||
if len(f.Spec.Source.Literal) > 256*1024 {
|
if len(f.Spec.Source.Literal) > 256*1024 {
|
||||||
err := fission.MakeError(fission.ErrorInvalidArgument, "Package literal larger than 256K")
|
err := fission.MakeError(fission.ErrorInvalidArgument, "Package literal larger than 256K")
|
||||||
|
|||||||
@@ -59,12 +59,6 @@ func (a *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateResourceName(t.Metadata.Name)
|
|
||||||
if err != nil {
|
|
||||||
a.respondWithError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
_, err = cron.Parse(t.Spec.Cron)
|
_, err = cron.Parse(t.Spec.Cron)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -58,12 +58,6 @@ func (a *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateResourceName(watch.Metadata.Name)
|
|
||||||
if err != nil {
|
|
||||||
a.respondWithError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO check for duplicate watches
|
// TODO check for duplicate watches
|
||||||
|
|
||||||
wnew, err := a.fissionClient.KubernetesWatchTriggers(watch.Metadata.Namespace).Create(&watch)
|
wnew, err := a.fissionClient.KubernetesWatchTriggers(watch.Metadata.Namespace).Create(&watch)
|
||||||
|
|||||||
+2
-2
@@ -212,8 +212,8 @@ func (fc *FissionClient) Packages(ns string) PackageInterface {
|
|||||||
return MakePackageInterface(fc.crdClient, ns)
|
return MakePackageInterface(fc.crdClient, ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fc *FissionClient) WaitForCRDs() {
|
func (fc *FissionClient) WaitForCRDs() error {
|
||||||
waitForCRDs(fc.crdClient)
|
return waitForCRDs(fc.crdClient)
|
||||||
}
|
}
|
||||||
func (fc *FissionClient) GetCrdClient() *rest.RESTClient {
|
func (fc *FissionClient) GetCrdClient() *rest.RESTClient {
|
||||||
return fc.crdClient
|
return fc.crdClient
|
||||||
|
|||||||
+20
-20
@@ -34,31 +34,31 @@ const (
|
|||||||
// ensureCRD checks if the given CRD type exists, and creates it if
|
// ensureCRD checks if the given CRD type exists, and creates it if
|
||||||
// needed. (Note that this creates the CRD type; it doesn't create any
|
// needed. (Note that this creates the CRD type; it doesn't create any
|
||||||
// _instances_ of that type.)
|
// _instances_ of that type.)
|
||||||
func ensureCRD(clientset *apiextensionsclient.Clientset, crd *apiextensionsv1beta1.CustomResourceDefinition) error {
|
func ensureCRD(clientset *apiextensionsclient.Clientset, crd *apiextensionsv1beta1.CustomResourceDefinition) (err error) {
|
||||||
maxRetries := 5
|
maxRetries := 5
|
||||||
for i := 0; i < maxRetries; i++ {
|
|
||||||
_, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.ObjectMeta.Name, metav1.GetOptions{})
|
|
||||||
|
|
||||||
if err != nil {
|
for i := 0; i < maxRetries; i++ {
|
||||||
if errors.IsNotFound(err) {
|
_, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.ObjectMeta.Name, metav1.GetOptions{})
|
||||||
// crd resource not found error
|
if err == nil {
|
||||||
_, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
return nil
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The requests fail to connect to k8s api server before
|
|
||||||
// istio-prxoy is ready to serve traffic. Retry again.
|
|
||||||
log.Printf("Error connecting to kubernetes api service (%v), retrying", err)
|
|
||||||
time.Sleep(500 * time.Duration(2*i) * time.Millisecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resource already exists
|
if errors.IsNotFound(err) {
|
||||||
break
|
// crd resource not found error
|
||||||
|
_, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The requests fail to connect to k8s api server before
|
||||||
|
// istio-prxoy is ready to serve traffic. Retry again.
|
||||||
|
log.Printf("Error connecting to kubernetes api service (%v), retrying", err)
|
||||||
|
time.Sleep(500 * time.Duration(2*i) * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure CRDs
|
// Ensure CRDs
|
||||||
|
|||||||
+153
-20
@@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package crd
|
package crd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
@@ -151,14 +152,14 @@ func (ht *HTTPTrigger) GetObjectKind() schema.ObjectKind {
|
|||||||
func (w *KubernetesWatchTrigger) GetObjectKind() schema.ObjectKind {
|
func (w *KubernetesWatchTrigger) GetObjectKind() schema.ObjectKind {
|
||||||
return &w.TypeMeta
|
return &w.TypeMeta
|
||||||
}
|
}
|
||||||
func (w *TimeTrigger) GetObjectKind() schema.ObjectKind {
|
func (t *TimeTrigger) GetObjectKind() schema.ObjectKind {
|
||||||
return &w.TypeMeta
|
return &t.TypeMeta
|
||||||
}
|
}
|
||||||
func (w *MessageQueueTrigger) GetObjectKind() schema.ObjectKind {
|
func (m *MessageQueueTrigger) GetObjectKind() schema.ObjectKind {
|
||||||
return &w.TypeMeta
|
return &m.TypeMeta
|
||||||
}
|
}
|
||||||
func (w *Package) GetObjectKind() schema.ObjectKind {
|
func (p *Package) GetObjectKind() schema.ObjectKind {
|
||||||
return &w.TypeMeta
|
return &p.TypeMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Function) GetObjectMeta() metav1.Object {
|
func (f *Function) GetObjectMeta() metav1.Object {
|
||||||
@@ -173,14 +174,14 @@ func (ht *HTTPTrigger) GetObjectMeta() metav1.Object {
|
|||||||
func (w *KubernetesWatchTrigger) GetObjectMeta() metav1.Object {
|
func (w *KubernetesWatchTrigger) GetObjectMeta() metav1.Object {
|
||||||
return &w.Metadata
|
return &w.Metadata
|
||||||
}
|
}
|
||||||
func (w *TimeTrigger) GetObjectMeta() metav1.Object {
|
func (t *TimeTrigger) GetObjectMeta() metav1.Object {
|
||||||
return &w.Metadata
|
return &t.Metadata
|
||||||
}
|
}
|
||||||
func (w *MessageQueueTrigger) GetObjectMeta() metav1.Object {
|
func (m *MessageQueueTrigger) GetObjectMeta() metav1.Object {
|
||||||
return &w.Metadata
|
return &m.Metadata
|
||||||
}
|
}
|
||||||
func (w *Package) GetObjectMeta() metav1.Object {
|
func (p *Package) GetObjectMeta() metav1.Object {
|
||||||
return &w.Metadata
|
return &p.Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fl *FunctionList) GetObjectKind() schema.ObjectKind {
|
func (fl *FunctionList) GetObjectKind() schema.ObjectKind {
|
||||||
@@ -198,11 +199,11 @@ func (wl *KubernetesWatchTriggerList) GetObjectKind() schema.ObjectKind {
|
|||||||
func (wl *TimeTriggerList) GetObjectKind() schema.ObjectKind {
|
func (wl *TimeTriggerList) GetObjectKind() schema.ObjectKind {
|
||||||
return &wl.TypeMeta
|
return &wl.TypeMeta
|
||||||
}
|
}
|
||||||
func (wl *MessageQueueTriggerList) GetObjectKind() schema.ObjectKind {
|
func (ml *MessageQueueTriggerList) GetObjectKind() schema.ObjectKind {
|
||||||
return &wl.TypeMeta
|
return &ml.TypeMeta
|
||||||
}
|
}
|
||||||
func (wl *PackageList) GetObjectKind() schema.ObjectKind {
|
func (pl *PackageList) GetObjectKind() schema.ObjectKind {
|
||||||
return &wl.TypeMeta
|
return &pl.TypeMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fl *FunctionList) GetListMeta() metav1.List {
|
func (fl *FunctionList) GetListMeta() metav1.List {
|
||||||
@@ -220,9 +221,141 @@ func (wl *KubernetesWatchTriggerList) GetListMeta() metav1.List {
|
|||||||
func (wl *TimeTriggerList) GetListMeta() metav1.List {
|
func (wl *TimeTriggerList) GetListMeta() metav1.List {
|
||||||
return &wl.Metadata
|
return &wl.Metadata
|
||||||
}
|
}
|
||||||
func (wl *MessageQueueTriggerList) GetListMeta() metav1.List {
|
func (ml *MessageQueueTriggerList) GetListMeta() metav1.List {
|
||||||
return &wl.Metadata
|
return &ml.Metadata
|
||||||
}
|
}
|
||||||
func (wl *PackageList) GetListMeta() metav1.List {
|
func (pl *PackageList) GetListMeta() metav1.List {
|
||||||
return &wl.Metadata
|
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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,6 +217,19 @@ func (fetcher *Fetcher) FetchHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Fetch takes FetchRequest and makes the fetch call
|
// Fetch takes FetchRequest and makes the fetch call
|
||||||
// It returns the HTTP code and error if any
|
// It returns the HTTP code and error if any
|
||||||
func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
||||||
|
// check that the requested filename is not an empty string and error out if so
|
||||||
|
if len(req.Filename) == 0 {
|
||||||
|
e := fmt.Sprintf("Fetch request received for an empty file name, request: %v", req)
|
||||||
|
log.Printf(e)
|
||||||
|
return 400, errors.New(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify first if the file already exists.
|
||||||
|
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)
|
||||||
|
return 200, nil
|
||||||
|
}
|
||||||
|
|
||||||
tmpFile := req.Filename + ".tmp"
|
tmpFile := req.Filename + ".tmp"
|
||||||
tmpPath := filepath.Join(fetcher.sharedVolumePath, tmpFile)
|
tmpPath := filepath.Join(fetcher.sharedVolumePath, tmpFile)
|
||||||
|
|
||||||
@@ -241,6 +254,15 @@ func (fetcher *Fetcher) Fetch(req FetchRequest) (int, error) {
|
|||||||
if req.FetchType == FETCH_SOURCE {
|
if req.FetchType == FETCH_SOURCE {
|
||||||
archive = &pkg.Spec.Source
|
archive = &pkg.Spec.Source
|
||||||
} else if req.FetchType == FETCH_DEPLOYMENT {
|
} else if req.FetchType == FETCH_DEPLOYMENT {
|
||||||
|
// sometimes, the user may invoke the function even before the source code is built into a deploy pkg.
|
||||||
|
// this results in executor sending a fetch request of type FETCH_DEPLOYMENT and since pkg.Spec.Deployment.Url will be empty,
|
||||||
|
// we hit this "Get : unsupported protocol scheme "" error.
|
||||||
|
// it may be useful to the user if we can send a more meaningful error in such a scenario.
|
||||||
|
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)
|
||||||
|
log.Printf(e)
|
||||||
|
return 500, errors.New(e)
|
||||||
|
}
|
||||||
archive = &pkg.Spec.Deployment
|
archive = &pkg.Spec.Deployment
|
||||||
}
|
}
|
||||||
// get package data as literal or by url
|
// get package data as literal or by url
|
||||||
|
|||||||
+17
-2
@@ -57,13 +57,27 @@ func (executor *Executor) getServiceForFunctionApi(w http.ResponseWriter, r *htt
|
|||||||
w.Write([]byte(serviceName))
|
w.Write([]byte(serviceName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getServiceForFunction first checks if this function's service is cached, if yes, it validates the address.
|
||||||
|
// if it's a valid address, just returns it.
|
||||||
|
// else, invalidates its cache entry and makes a new request to create a service for this function and finally responds
|
||||||
|
// with new address or error.
|
||||||
|
//
|
||||||
|
// checking for the validity of the address causes a little more over-head than desired. but, it ensures that
|
||||||
|
// stale addresses are not returned to the router.
|
||||||
|
// To make it optimal, plan is to add an eager cache invalidator function that watches for pod deletion events and
|
||||||
|
// invalidates the cache entry if the pod address was cached.
|
||||||
func (executor *Executor) getServiceForFunction(m *metav1.ObjectMeta) (string, error) {
|
func (executor *Executor) getServiceForFunction(m *metav1.ObjectMeta) (string, error) {
|
||||||
// Check function -> svc cache
|
// Check function -> svc cache
|
||||||
log.Printf("[%v] Checking for cached function service", m.Name)
|
log.Printf("[%v] Checking for cached function service", m.Name)
|
||||||
fsvc, err := executor.fsCache.GetByFunction(m)
|
fsvc, err := executor.fsCache.GetByFunction(m)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Cached, return svc address
|
if executor.isValidAddress(fsvc) {
|
||||||
return fsvc.Address, nil
|
// Cached, return svc address
|
||||||
|
return fsvc.Address, nil
|
||||||
|
} else {
|
||||||
|
log.Printf("[%v] Deleting cache entry for invalid address : %s", m.Name, fsvc.Address)
|
||||||
|
executor.fsCache.DeleteEntry(fsvc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
respChan := make(chan *createFuncServiceResponse)
|
respChan := make(chan *createFuncServiceResponse)
|
||||||
@@ -111,6 +125,7 @@ func (executor *Executor) Serve(port int) {
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
executor.ndm.Run(ctx)
|
executor.ndm.Run(ctx)
|
||||||
|
executor.gpm.Run(ctx)
|
||||||
r.Use(fission.LoggingMiddleware)
|
r.Use(fission.LoggingMiddleware)
|
||||||
log.Fatal(http.ListenAndServe(address, r))
|
log.Fatal(http.ListenAndServe(address, r))
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-88
@@ -22,9 +22,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/pkg/api"
|
"k8s.io/client-go/pkg/api"
|
||||||
|
|
||||||
@@ -33,6 +32,11 @@ import (
|
|||||||
"github.com/fission/fission/executor/fscache"
|
"github.com/fission/fission/executor/fscache"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
deletePropagation = meta_v1.DeletePropagationBackground
|
||||||
|
delOpt = meta_v1.DeleteOptions{PropagationPolicy: &deletePropagation}
|
||||||
|
)
|
||||||
|
|
||||||
// cleanupObjects cleans up resources created by old executortype instances
|
// cleanupObjects cleans up resources created by old executortype instances
|
||||||
func cleanupObjects(kubernetesClient *kubernetes.Clientset,
|
func cleanupObjects(kubernetesClient *kubernetes.Clientset,
|
||||||
namespace string,
|
namespace string,
|
||||||
@@ -65,14 +69,6 @@ func cleanup(client *kubernetes.Clientset, namespace string, instanceId string)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// See K8s #33845 and related bugs: deleting a deployment
|
|
||||||
// through the API doesn't cause the associated ReplicaSet to
|
|
||||||
// be deleted. (Fixed recently, but we may be running a
|
|
||||||
// version before the fix.)
|
|
||||||
err = cleanupReplicaSets(client, namespace, instanceId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pods might still be running user functions, so we give them
|
// Pods might still be running user functions, so we give them
|
||||||
// a few minutes before terminating them. This time is the
|
// a few minutes before terminating them. This time is the
|
||||||
@@ -104,52 +100,46 @@ func idleObjectReaper(kubeClient *kubernetes.Clientset,
|
|||||||
log.Fatalf("Failed to get environment list: %v", err)
|
log.Fatalf("Failed to get environment list: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envList := make(map[types.UID]struct{})
|
||||||
for i := range envs.Items {
|
for i := range envs.Items {
|
||||||
env := envs.Items[i]
|
env := envs.Items[i]
|
||||||
if env.Spec.AllowedFunctionsPerContainer == fission.AllowedFunctionsPerContainerInfinite {
|
envList[env.Metadata.UID] = struct{}{}
|
||||||
continue
|
}
|
||||||
|
|
||||||
|
funcSvcs, err := fsCache.ListOld(idlePodReapTime)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error reaping idle pods: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fsvc := range funcSvcs {
|
||||||
|
if _, ok := envList[fsvc.Environment.Metadata.UID]; !ok {
|
||||||
|
log.Printf("Environment %v for function %v no longer exists",
|
||||||
|
fsvc.Environment.Metadata.Name, fsvc.Name)
|
||||||
}
|
}
|
||||||
funcSvcs, err := fsCache.ListOld(&env.Metadata, idlePodReapTime)
|
|
||||||
if err != nil {
|
if fsvc.Environment.Spec.AllowedFunctionsPerContainer == fission.AllowedFunctionsPerContainerInfinite {
|
||||||
log.Printf("Error reaping idle pods: %v", err)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fsvc := range funcSvcs {
|
// Newdeploy manager handles the function delete event and clean cache/kubeobjs itself,
|
||||||
|
// so we ignore the function service cache with newdepoy executor type here.
|
||||||
fn, err := fissionClient.Functions(fsvc.Function.Namespace).Get(fsvc.Function.Name)
|
if fsvc.Executor != fscache.NEWDEPLOY {
|
||||||
if err == nil {
|
deleted, err := fsCache.DeleteOld(fsvc, idlePodReapTime)
|
||||||
// Ignore functions of NewDeploy ExecutorType with MinScale > 0
|
if err != nil {
|
||||||
if fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale > 0 &&
|
log.Printf("Error deleting Kubernetes objects for fsvc '%v': %v", fsvc, err)
|
||||||
fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fission.ExecutorTypeNewdeploy {
|
log.Printf("Object Name| Object Kind | Object Namespace")
|
||||||
continue
|
for _, kubeobj := range fsvc.KubernetesObjects {
|
||||||
|
log.Printf("%v | %v | %v", kubeobj.Name, kubeobj.Kind, kubeobj.Namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return errors not equal to "is not found" error
|
if !deleted {
|
||||||
if err != nil && !errors.IsNotFound(err) {
|
|
||||||
log.Printf("Error getting function: %v", fsvc.Function.Name)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Newdeploy manager handles the function delete event and clean cache/kubeobjs itself,
|
for _, kubeobj := range fsvc.KubernetesObjects {
|
||||||
// so we ignore the function service cache with newdepoy executor type here.
|
deleteKubeobject(kubeClient, &kubeobj)
|
||||||
if fsvc.Executor != fscache.NEWDEPLOY {
|
|
||||||
deleted, err := fsCache.DeleteOld(fsvc, idlePodReapTime)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error deleting Kubernetes objects for fsvc '%v': %v", fsvc, err)
|
|
||||||
log.Printf("Object Name| Object Kind | Object Namespace")
|
|
||||||
for _, kubeobj := range fsvc.KubernetesObjects {
|
|
||||||
log.Printf("%v | %v | %v", kubeobj.Name, kubeobj.Kind, kubeobj.Namespace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !deleted {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, kubeobj := range fsvc.KubernetesObjects {
|
|
||||||
deleteKubeobject(kubeClient, &kubeobj)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,10 +157,8 @@ func deleteKubeobject(kubeClient *kubernetes.Clientset, kubeobj *api.ObjectRefer
|
|||||||
logErr(fmt.Sprintf("cleaning up service %v ", kubeobj.Name), err)
|
logErr(fmt.Sprintf("cleaning up service %v ", kubeobj.Name), err)
|
||||||
|
|
||||||
case "deployment":
|
case "deployment":
|
||||||
depl, err := kubeClient.ExtensionsV1beta1().Deployments(kubeobj.Namespace).Get(kubeobj.Name, meta_v1.GetOptions{})
|
err := kubeClient.ExtensionsV1beta1().Deployments(kubeobj.Namespace).Delete(kubeobj.Name, &delOpt)
|
||||||
err = kubeClient.ExtensionsV1beta1().Deployments(kubeobj.Namespace).Delete(kubeobj.Name, nil)
|
|
||||||
logErr(fmt.Sprintf("cleaning up deployment %v ", kubeobj.Name), err)
|
logErr(fmt.Sprintf("cleaning up deployment %v ", kubeobj.Name), err)
|
||||||
cleanupDeploymentObjects(kubeClient, kubeobj.Namespace, depl.Labels)
|
|
||||||
|
|
||||||
case "horizontalpodautoscaler":
|
case "horizontalpodautoscaler":
|
||||||
err := kubeClient.AutoscalingV1().HorizontalPodAutoscalers(kubeobj.Namespace).Delete(kubeobj.Name, nil)
|
err := kubeClient.AutoscalingV1().HorizontalPodAutoscalers(kubeobj.Namespace).Delete(kubeobj.Name, nil)
|
||||||
@@ -182,22 +170,6 @@ func deleteKubeobject(kubeClient *kubernetes.Clientset, kubeobj *api.ObjectRefer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanupDeploymentObjects(kubeClient *kubernetes.Clientset, namespace string, sel map[string]string) {
|
|
||||||
rsList, err := kubeClient.ExtensionsV1beta1().ReplicaSets(namespace).List(meta_v1.ListOptions{LabelSelector: labels.Set(sel).AsSelector().String()})
|
|
||||||
logErr("Getting replicaset for deployment ", err)
|
|
||||||
for _, rs := range rsList.Items {
|
|
||||||
err = kubeClient.ExtensionsV1beta1().ReplicaSets(namespace).Delete(rs.Name, nil)
|
|
||||||
logErr(fmt.Sprintf("Cleaning replicaset %v for deployment", rs.Name), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
podList, err := kubeClient.CoreV1().Pods(namespace).List(meta_v1.ListOptions{LabelSelector: labels.Set(sel).AsSelector().String()})
|
|
||||||
logErr("Getting pods for deployment ", err)
|
|
||||||
for _, pod := range podList.Items {
|
|
||||||
err = kubeClient.CoreV1().Pods(namespace).Delete(pod.Name, nil)
|
|
||||||
logErr(fmt.Sprintf("Cleaning pod %v for deployment", pod.Name), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanupDeployments(client *kubernetes.Clientset, namespace string, instanceId string) error {
|
func cleanupDeployments(client *kubernetes.Clientset, namespace string, instanceId string) error {
|
||||||
deploymentList, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.ListOptions{})
|
deploymentList, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -207,7 +179,7 @@ func cleanupDeployments(client *kubernetes.Clientset, namespace string, instance
|
|||||||
id, ok := dep.ObjectMeta.Labels[fission.EXECUTOR_INSTANCEID_LABEL]
|
id, ok := dep.ObjectMeta.Labels[fission.EXECUTOR_INSTANCEID_LABEL]
|
||||||
if ok && id != instanceId {
|
if ok && id != instanceId {
|
||||||
log.Printf("Cleaning up deployment %v", dep.ObjectMeta.Name)
|
log.Printf("Cleaning up deployment %v", dep.ObjectMeta.Name)
|
||||||
err := client.ExtensionsV1beta1().Deployments(namespace).Delete(dep.ObjectMeta.Name, nil)
|
err := client.ExtensionsV1beta1().Deployments(namespace).Delete(dep.ObjectMeta.Name, &delOpt)
|
||||||
logErr("cleaning up deployment", err)
|
logErr("cleaning up deployment", err)
|
||||||
// ignore err
|
// ignore err
|
||||||
}
|
}
|
||||||
@@ -215,7 +187,7 @@ func cleanupDeployments(client *kubernetes.Clientset, namespace string, instance
|
|||||||
pid, pok := dep.ObjectMeta.Labels[fission.POOLMGR_INSTANCEID_LABEL]
|
pid, pok := dep.ObjectMeta.Labels[fission.POOLMGR_INSTANCEID_LABEL]
|
||||||
if pok && pid != instanceId {
|
if pok && pid != instanceId {
|
||||||
log.Printf("Cleaning up deployment %v", dep.ObjectMeta.Name)
|
log.Printf("Cleaning up deployment %v", dep.ObjectMeta.Name)
|
||||||
err := client.ExtensionsV1beta1().Deployments(namespace).Delete(dep.ObjectMeta.Name, nil)
|
err := client.ExtensionsV1beta1().Deployments(namespace).Delete(dep.ObjectMeta.Name, &delOpt)
|
||||||
logErr("cleaning up deployment", err)
|
logErr("cleaning up deployment", err)
|
||||||
// ignore err
|
// ignore err
|
||||||
}
|
}
|
||||||
@@ -223,35 +195,13 @@ func cleanupDeployments(client *kubernetes.Clientset, namespace string, instance
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanupReplicaSets(client *kubernetes.Clientset, namespace string, instanceId string) error {
|
|
||||||
rsList, err := client.ExtensionsV1beta1().ReplicaSets(namespace).List(meta_v1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, rs := range rsList.Items {
|
|
||||||
id, ok := rs.ObjectMeta.Labels[fission.EXECUTOR_INSTANCEID_LABEL]
|
|
||||||
if ok && id != instanceId {
|
|
||||||
log.Printf("Cleaning up replicaset %v", rs.ObjectMeta.Name)
|
|
||||||
err := client.ExtensionsV1beta1().ReplicaSets(namespace).Delete(rs.ObjectMeta.Name, nil)
|
|
||||||
logErr("cleaning up replicaset", err)
|
|
||||||
}
|
|
||||||
// Backward compatibility with older label name
|
|
||||||
pid, pok := rs.ObjectMeta.Labels[fission.POOLMGR_INSTANCEID_LABEL]
|
|
||||||
if pok && pid != instanceId {
|
|
||||||
log.Printf("Cleaning up replicaset %v", rs.ObjectMeta.Name)
|
|
||||||
err := client.ExtensionsV1beta1().ReplicaSets(namespace).Delete(rs.ObjectMeta.Name, nil)
|
|
||||||
logErr("cleaning up replicaset", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanupPods(client *kubernetes.Clientset, namespace string, instanceId string) error {
|
func cleanupPods(client *kubernetes.Clientset, namespace string, instanceId string) error {
|
||||||
podList, err := client.CoreV1().Pods(namespace).List(meta_v1.ListOptions{})
|
podList, err := client.CoreV1().Pods(namespace).List(meta_v1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, pod := range podList.Items {
|
for _, pod := range podList.Items {
|
||||||
|
log.Printf("Clean pod: %v", pod.ObjectMeta.Name)
|
||||||
id, ok := pod.ObjectMeta.Labels[fission.EXECUTOR_INSTANCEID_LABEL]
|
id, ok := pod.ObjectMeta.Labels[fission.EXECUTOR_INSTANCEID_LABEL]
|
||||||
if ok && id != instanceId {
|
if ok && id != instanceId {
|
||||||
log.Printf("Cleaning up pod %v", pod.ObjectMeta.Name)
|
log.Printf("Cleaning up pod %v", pod.ObjectMeta.Name)
|
||||||
|
|||||||
+28
-5
@@ -119,6 +119,15 @@ func (executor *Executor) serveCreateFuncServices() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (executor *Executor) getFunctionExecutorType(meta *metav1.ObjectMeta) (fission.ExecutorType, error) {
|
||||||
|
fn, err := executor.fissionClient.Functions(meta.Namespace).Get(meta.Name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (executor *Executor) createServiceForFunction(meta *metav1.ObjectMeta) (*fscache.FuncSvc, error) {
|
func (executor *Executor) createServiceForFunction(meta *metav1.ObjectMeta) (*fscache.FuncSvc, error) {
|
||||||
log.Printf("[%v] No cached function service found, creating one", meta.Name)
|
log.Printf("[%v] No cached function service found, creating one", meta.Name)
|
||||||
|
|
||||||
@@ -129,14 +138,12 @@ func (executor *Executor) createServiceForFunction(meta *metav1.ObjectMeta) (*fs
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fn, err := executor.fissionClient.
|
executorType, err := executor.getFunctionExecutorType(meta)
|
||||||
Functions(meta.Namespace).
|
|
||||||
Get(meta.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType {
|
switch executorType {
|
||||||
case fission.ExecutorTypeNewdeploy:
|
case fission.ExecutorTypeNewdeploy:
|
||||||
fs, err := executor.ndm.GetFuncSvc(meta)
|
fs, err := executor.ndm.GetFuncSvc(meta)
|
||||||
return fs, err
|
return fs, err
|
||||||
@@ -182,6 +189,15 @@ func (executor *Executor) getFunctionEnv(m *metav1.ObjectMeta) (*crd.Environment
|
|||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isValidAddress invokes isValidService or isValidPod depending on the type of executor
|
||||||
|
func (executor *Executor) isValidAddress(fsvc *fscache.FuncSvc) bool {
|
||||||
|
if fsvc.Executor == fscache.NEWDEPLOY {
|
||||||
|
return executor.ndm.IsValidService(fsvc.Address)
|
||||||
|
} else {
|
||||||
|
return executor.gpm.IsValidPod(fsvc.KubernetesObjects, fsvc.Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func dumpStackTrace() {
|
func dumpStackTrace() {
|
||||||
debug.PrintStack()
|
debug.PrintStack()
|
||||||
}
|
}
|
||||||
@@ -193,6 +209,12 @@ func StartExecutor(fissionNamespace string, functionNamespace string, port int)
|
|||||||
fission.SetupStackTraceHandler()
|
fission.SetupStackTraceHandler()
|
||||||
|
|
||||||
fissionClient, kubernetesClient, _, err := crd.MakeFissionClient()
|
fissionClient, kubernetesClient, _, err := crd.MakeFissionClient()
|
||||||
|
|
||||||
|
err = fissionClient.WaitForCRDs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error waiting for CRDs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
restClient := fissionClient.GetCrdClient()
|
restClient := fissionClient.GetCrdClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get kubernetes client: %v", err)
|
log.Printf("Failed to get kubernetes client: %v", err)
|
||||||
@@ -204,8 +226,9 @@ func StartExecutor(fissionNamespace string, functionNamespace string, port int)
|
|||||||
poolID := strings.ToLower(uniuri.NewLen(8))
|
poolID := strings.ToLower(uniuri.NewLen(8))
|
||||||
cleanupObjects(kubernetesClient, functionNamespace, poolID)
|
cleanupObjects(kubernetesClient, functionNamespace, poolID)
|
||||||
go idleObjectReaper(kubernetesClient, fissionClient, fsCache, time.Minute*2)
|
go idleObjectReaper(kubernetesClient, fissionClient, fsCache, time.Minute*2)
|
||||||
|
|
||||||
gpm := poolmgr.MakeGenericPoolManager(
|
gpm := poolmgr.MakeGenericPoolManager(
|
||||||
fissionClient, kubernetesClient, fissionNamespace,
|
fissionClient, kubernetesClient,
|
||||||
functionNamespace, fsCache, poolID)
|
functionNamespace, fsCache, poolID)
|
||||||
|
|
||||||
ndm := newdeploy.MakeNewDeploy(
|
ndm := newdeploy.MakeNewDeploy(
|
||||||
|
|||||||
@@ -138,7 +138,11 @@ func TestExecutor(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("failed to ensure crds: %v", err)
|
log.Panicf("failed to ensure crds: %v", err)
|
||||||
}
|
}
|
||||||
fissionClient.WaitForCRDs()
|
|
||||||
|
err = fissionClient.WaitForCRDs()
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("failed to wait crds: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// create an env on the cluster
|
// create an env on the cluster
|
||||||
env, err := fissionClient.Environments(fissionNs).Create(&crd.Environment{
|
env, err := fissionClient.Environments(fissionNs).Create(&crd.Environment{
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ type (
|
|||||||
address string
|
address string
|
||||||
kubernetesObjects []api.ObjectReference
|
kubernetesObjects []api.ObjectReference
|
||||||
age time.Duration
|
age time.Duration
|
||||||
env *metav1.ObjectMeta // used for ListOld
|
|
||||||
responseChannel chan *fscResponse
|
responseChannel chan *fscResponse
|
||||||
}
|
}
|
||||||
fscResponse struct {
|
fscResponse struct {
|
||||||
@@ -117,8 +116,7 @@ func (fsc *FunctionServiceCache) service() {
|
|||||||
funcObjects := make([]*FuncSvc, 0)
|
funcObjects := make([]*FuncSvc, 0)
|
||||||
for _, funcSvc := range fscs {
|
for _, funcSvc := range fscs {
|
||||||
fsvc := funcSvc.(*FuncSvc)
|
fsvc := funcSvc.(*FuncSvc)
|
||||||
if fsvc.Environment.Metadata.UID == req.env.UID &&
|
if time.Since(fsvc.Atime) > req.age {
|
||||||
time.Since(fsvc.Atime) > req.age {
|
|
||||||
funcObjects = append(funcObjects, fsvc)
|
funcObjects = append(funcObjects, fsvc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,24 +245,27 @@ func (fsc *FunctionServiceCache) _touchByAddress(address string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fsc *FunctionServiceCache) DeleteEntry(fsvc *FuncSvc) {
|
||||||
|
fsc.byFunction.Delete(crd.CacheKey(fsvc.Function))
|
||||||
|
fsc.byAddress.Delete(fsvc.Address)
|
||||||
|
fsc.byFunctionUID.Delete(fsvc.Function.UID)
|
||||||
|
}
|
||||||
|
|
||||||
func (fsc *FunctionServiceCache) DeleteOld(fsvc *FuncSvc, minAge time.Duration) (bool, error) {
|
func (fsc *FunctionServiceCache) DeleteOld(fsvc *FuncSvc, minAge time.Duration) (bool, error) {
|
||||||
if time.Since(fsvc.Atime) < minAge {
|
if time.Since(fsvc.Atime) < minAge {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fsc.byFunction.Delete(crd.CacheKey(fsvc.Function))
|
fsc.DeleteEntry(fsvc)
|
||||||
fsc.byAddress.Delete(fsvc.Address)
|
|
||||||
fsc.byFunctionUID.Delete(fsvc.Function.UID)
|
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fsc *FunctionServiceCache) ListOld(env *metav1.ObjectMeta, age time.Duration) ([]*FuncSvc, error) {
|
func (fsc *FunctionServiceCache) ListOld(age time.Duration) ([]*FuncSvc, error) {
|
||||||
responseChannel := make(chan *fscResponse)
|
responseChannel := make(chan *fscResponse)
|
||||||
fsc.requestChannel <- &fscRequest{
|
fsc.requestChannel <- &fscRequest{
|
||||||
requestType: LISTOLD,
|
requestType: LISTOLD,
|
||||||
age: age,
|
age: age,
|
||||||
env: env,
|
|
||||||
responseChannel: responseChannel,
|
responseChannel: responseChannel,
|
||||||
}
|
}
|
||||||
resp := <-responseChannel
|
resp := <-responseChannel
|
||||||
|
|||||||
@@ -116,7 +116,10 @@ func (deploy *NewDeploy) getDeploymentSpec(fn *crd.Function, env *crd.Environmen
|
|||||||
replicas = 1
|
replicas = 1
|
||||||
}
|
}
|
||||||
targetFilename := "user"
|
targetFilename := "user"
|
||||||
var gracePeriodSeconds int64 = 6 * 60
|
gracePeriodSeconds := int64(6 * 60)
|
||||||
|
if env.Spec.TerminationGracePeriod > 0 {
|
||||||
|
gracePeriodSeconds = env.Spec.TerminationGracePeriod
|
||||||
|
}
|
||||||
|
|
||||||
fetchReq := &fetcher.FetchRequest{
|
fetchReq := &fetcher.FetchRequest{
|
||||||
FetchType: fetcher.FETCH_DEPLOYMENT,
|
FetchType: fetcher.FETCH_DEPLOYMENT,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -479,9 +480,14 @@ func (deploy *NewDeploy) fnDelete(fn *crd.Function) (*fscache.FuncSvc, error) {
|
|||||||
|
|
||||||
var delError error
|
var delError error
|
||||||
|
|
||||||
fsvc, err := deploy.fsCache.GetByFunction(&fn.Metadata)
|
// GetByFunction uses resource version as part of cache key, however,
|
||||||
|
// the resource version in function metadata will be changed when a function
|
||||||
|
// is deleted and cause newdeploy backend fails to delete the entry.
|
||||||
|
// Use GetByFunctionUID instead of GetByFunction here to find correct
|
||||||
|
// fsvc entry.
|
||||||
|
fsvc, err := deploy.fsCache.GetByFunctionUID(fn.Metadata.UID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("fsvc not fonud in cache: %v", fn.Metadata)
|
log.Printf("fsvc not found in cache: %v", fn.Metadata)
|
||||||
delError = err
|
delError = err
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -553,3 +559,18 @@ func (deploy *NewDeploy) updateKubeObjRefRV(fsvc *fscache.FuncSvc, objKind strin
|
|||||||
func updateStatus(fn *crd.Function, err error, message string) {
|
func updateStatus(fn *crd.Function, err error, message string) {
|
||||||
log.Printf(message, err)
|
log.Printf(message, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsValidService does a get on the service address to ensure it's a valid service. returns true if it is, else false.
|
||||||
|
func (deploy *NewDeploy) IsValidService(svc string) bool {
|
||||||
|
service := strings.Split(svc, ".")
|
||||||
|
if len(service) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
svcObj, err := deploy.kubernetesClient.CoreV1().Services(service[1]).Get(service[0], metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
log.Printf("Valid service address : %s", svcObj.Spec.ClusterIP)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
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 poolmgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
apiv1 "k8s.io/client-go/pkg/api/v1"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
k8sCache "k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
|
"github.com/fission/fission"
|
||||||
|
"github.com/fission/fission/crd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getIstioServiceLabels(fnName string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"functionName": fnName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFuncIstioServiceRegister(crdClient *rest.RESTClient,
|
||||||
|
kubernetesClient *kubernetes.Clientset, fnNamespace string) k8sCache.Controller {
|
||||||
|
|
||||||
|
resyncPeriod := 30 * time.Second
|
||||||
|
lw := k8sCache.NewListWatchFromClient(crdClient, "functions", metav1.NamespaceDefault, fields.Everything())
|
||||||
|
_, controller := k8sCache.NewInformer(lw, &crd.Function{}, resyncPeriod,
|
||||||
|
k8sCache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: func(obj interface{}) {
|
||||||
|
|
||||||
|
fn := obj.(*crd.Function)
|
||||||
|
|
||||||
|
// Since istio only allows accessing pod through k8s service,
|
||||||
|
// for the functions with executor type "poolmgr" we need to
|
||||||
|
// create a service for sending requests to pod in pool.
|
||||||
|
// Functions with executor type "Newdeploy" is specialized at
|
||||||
|
// pod starts. In this case, just ignore such functions.
|
||||||
|
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
|
||||||
|
if fnExecutorType != fission.ExecutorTypePoolmgr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a same name service for function
|
||||||
|
// since istio only allows the traffic to service
|
||||||
|
sel := map[string]string{
|
||||||
|
"functionName": fn.Metadata.Name,
|
||||||
|
"functionUid": string(fn.Metadata.UID),
|
||||||
|
}
|
||||||
|
|
||||||
|
svcName := fission.GetFunctionIstioServiceName(fn.Metadata.Name, fn.Metadata.Namespace)
|
||||||
|
|
||||||
|
// service for accepting user traffic
|
||||||
|
svc := apiv1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: fnNamespace,
|
||||||
|
Name: svcName,
|
||||||
|
Labels: getIstioServiceLabels(fn.Metadata.Name),
|
||||||
|
},
|
||||||
|
Spec: apiv1.ServiceSpec{
|
||||||
|
Type: apiv1.ServiceTypeClusterIP,
|
||||||
|
Ports: []apiv1.ServicePort{
|
||||||
|
// Service port name should begin with a recognized prefix, or the traffic will be
|
||||||
|
// treated as TCP traffic. (https://istio.io/docs/setup/kubernetes/sidecar-injection.html)
|
||||||
|
// Originally the ports' name are similar to "http-fetch" and "http-specialize".
|
||||||
|
// But for istio 0.5.1, istio-proxy return unexpected 431 error with such naming.
|
||||||
|
// https://github.com/istio/istio/issues/928
|
||||||
|
// Workaround: remove prefix
|
||||||
|
// TODO: prepend prefix once the bug fixed
|
||||||
|
{
|
||||||
|
Name: "fetch",
|
||||||
|
Protocol: apiv1.ProtocolTCP,
|
||||||
|
Port: 8000,
|
||||||
|
TargetPort: intstr.FromInt(8000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "specialize",
|
||||||
|
Protocol: apiv1.ProtocolTCP,
|
||||||
|
Port: 8888,
|
||||||
|
TargetPort: intstr.FromInt(8888),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Selector: sel,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// create function istio service if it does not exist
|
||||||
|
_, err := kubernetesClient.CoreV1().Services(fnNamespace).Create(&svc)
|
||||||
|
if err != nil && !kerrors.IsAlreadyExists(err) {
|
||||||
|
log.Printf("Error creating function istio service: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DeleteFunc: func(obj interface{}) {
|
||||||
|
fn := obj.(*crd.Function)
|
||||||
|
svcName := fission.GetFunctionIstioServiceName(fn.Metadata.Name, fn.Metadata.Namespace)
|
||||||
|
// delete function istio service
|
||||||
|
err := kubernetesClient.CoreV1().Services(fnNamespace).Delete(svcName, nil)
|
||||||
|
if err != nil && !kerrors.IsNotFound(err) {
|
||||||
|
log.Printf("Error deleting function istio service: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateFunc: func(oldObj, newObj interface{}) {},
|
||||||
|
})
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
+13
-45
@@ -28,7 +28,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -105,7 +104,8 @@ func MakeGenericPool(
|
|||||||
initialReplicas int32,
|
initialReplicas int32,
|
||||||
namespace string,
|
namespace string,
|
||||||
fsCache *fscache.FunctionServiceCache,
|
fsCache *fscache.FunctionServiceCache,
|
||||||
instanceId string) (*GenericPool, error) {
|
instanceId string,
|
||||||
|
enableIstio bool) (*GenericPool, error) {
|
||||||
|
|
||||||
log.Printf("Creating pool for environment %v", env.Metadata)
|
log.Printf("Creating pool for environment %v", env.Metadata)
|
||||||
|
|
||||||
@@ -122,16 +122,6 @@ func MakeGenericPool(
|
|||||||
runtimeImagePullPolicy = "IfNotPresent"
|
runtimeImagePullPolicy = "IfNotPresent"
|
||||||
}
|
}
|
||||||
|
|
||||||
enableIstio := false
|
|
||||||
|
|
||||||
if len(os.Getenv("ENABLE_ISTIO")) > 0 {
|
|
||||||
istio, err := strconv.ParseBool(os.Getenv("ENABLE_ISTIO"))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Failed to parse ENABLE_ISTIO")
|
|
||||||
}
|
|
||||||
enableIstio = istio
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: in general we need to provide the user a way to configure pools. Initial
|
// TODO: in general we need to provide the user a way to configure pools. Initial
|
||||||
// replicas, autoscaling params, various timeouts, etc.
|
// replicas, autoscaling params, various timeouts, etc.
|
||||||
gp := &GenericPool{
|
gp := &GenericPool{
|
||||||
@@ -479,7 +469,10 @@ func (gp *GenericPool) createPool() error {
|
|||||||
|
|
||||||
// Use long terminationGracePeriodSeconds for connection draining in case that
|
// Use long terminationGracePeriodSeconds for connection draining in case that
|
||||||
// pod still runs user functions.
|
// pod still runs user functions.
|
||||||
var gracePeriodSeconds int64 = 6 * 60
|
gracePeriodSeconds := int64(6 * 60)
|
||||||
|
if gp.env.Spec.TerminationGracePeriod > 0 {
|
||||||
|
gracePeriodSeconds = gp.env.Spec.TerminationGracePeriod
|
||||||
|
}
|
||||||
|
|
||||||
podAnnotation := make(map[string]string)
|
podAnnotation := make(map[string]string)
|
||||||
|
|
||||||
@@ -750,7 +743,7 @@ func (gp *GenericPool) GetFuncSvc(m *metav1.ObjectMeta) (*fscache.FuncSvc, error
|
|||||||
log.Printf("Specialized pod: %v", pod.ObjectMeta.Name)
|
log.Printf("Specialized pod: %v", pod.ObjectMeta.Name)
|
||||||
|
|
||||||
var svcHost string
|
var svcHost string
|
||||||
if gp.useSvc {
|
if gp.useSvc && !gp.useIstio {
|
||||||
svcName := fmt.Sprintf("svc-%v", m.Name)
|
svcName := fmt.Sprintf("svc-%v", m.Name)
|
||||||
if len(m.UID) > 0 {
|
if len(m.UID) > 0 {
|
||||||
svcName = fmt.Sprintf("%s-%v", svcName, m.UID)
|
svcName = fmt.Sprintf("%s-%v", svcName, m.UID)
|
||||||
@@ -809,40 +802,15 @@ func (gp *GenericPool) GetFuncSvc(m *metav1.ObjectMeta) (*fscache.FuncSvc, error
|
|||||||
|
|
||||||
// destroys the pool -- the deployment, replicaset and pods
|
// destroys the pool -- the deployment, replicaset and pods
|
||||||
func (gp *GenericPool) destroy() error {
|
func (gp *GenericPool) destroy() error {
|
||||||
// Destroy deployment
|
deletePropagation := metav1.DeletePropagationBackground
|
||||||
err := gp.kubernetesClient.ExtensionsV1beta1().Deployments(gp.namespace).Delete(gp.deployment.ObjectMeta.Name, nil)
|
delOpt := metav1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePropagation,
|
||||||
|
}
|
||||||
|
err := gp.kubernetesClient.ExtensionsV1beta1().
|
||||||
|
Deployments(gp.namespace).Delete(gp.deployment.ObjectMeta.Name, &delOpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error destroying deployment: %v", err)
|
log.Printf("Error destroying deployment: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy ReplicaSet. Pre-1.6 K8s versions don't do this
|
|
||||||
// automatically but post-1.6 K8s will, and may beat us to it,
|
|
||||||
// so don't error out if we fail.
|
|
||||||
rsList, err := gp.kubernetesClient.ExtensionsV1beta1().ReplicaSets(gp.namespace).List(metav1.ListOptions{
|
|
||||||
LabelSelector: labels.Set(gp.labelsForPool).AsSelector().String(),
|
|
||||||
})
|
|
||||||
if len(rsList.Items) >= 0 {
|
|
||||||
for _, rs := range rsList.Items {
|
|
||||||
err = gp.kubernetesClient.ExtensionsV1beta1().ReplicaSets(gp.namespace).Delete(rs.ObjectMeta.Name, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error deleting replicaset, ignoring: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy Pods. See note above.
|
|
||||||
podList, err := gp.kubernetesClient.CoreV1().Pods(gp.namespace).List(metav1.ListOptions{
|
|
||||||
LabelSelector: labels.Set(gp.labelsForPool).AsSelector().String(),
|
|
||||||
})
|
|
||||||
if len(podList.Items) >= 0 {
|
|
||||||
for _, pod := range podList.Items {
|
|
||||||
err = gp.kubernetesClient.CoreV1().Pods(gp.namespace).Delete(pod.ObjectMeta.Name, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error deleting pod, ignoring: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-2
@@ -17,11 +17,17 @@ limitations under the License.
|
|||||||
package poolmgr
|
package poolmgr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/pkg/api"
|
||||||
|
k8sCache "k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
@@ -45,6 +51,9 @@ type (
|
|||||||
fsCache *fscache.FunctionServiceCache
|
fsCache *fscache.FunctionServiceCache
|
||||||
instanceId string
|
instanceId string
|
||||||
requestChannel chan *request
|
requestChannel chan *request
|
||||||
|
|
||||||
|
enableIstio bool
|
||||||
|
istioServiceRegister k8sCache.Controller
|
||||||
}
|
}
|
||||||
request struct {
|
request struct {
|
||||||
requestType
|
requestType
|
||||||
@@ -61,7 +70,6 @@ type (
|
|||||||
func MakeGenericPoolManager(
|
func MakeGenericPoolManager(
|
||||||
fissionClient *crd.FissionClient,
|
fissionClient *crd.FissionClient,
|
||||||
kubernetesClient *kubernetes.Clientset,
|
kubernetesClient *kubernetes.Clientset,
|
||||||
fissionNamespace string,
|
|
||||||
functionNamespace string,
|
functionNamespace string,
|
||||||
fsCache *fscache.FunctionServiceCache,
|
fsCache *fscache.FunctionServiceCache,
|
||||||
instanceId string) *GenericPoolManager {
|
instanceId string) *GenericPoolManager {
|
||||||
@@ -78,9 +86,28 @@ func MakeGenericPoolManager(
|
|||||||
go gpm.service()
|
go gpm.service()
|
||||||
go gpm.eagerPoolCreator()
|
go gpm.eagerPoolCreator()
|
||||||
|
|
||||||
|
if len(os.Getenv("ENABLE_ISTIO")) > 0 {
|
||||||
|
istio, err := strconv.ParseBool(os.Getenv("ENABLE_ISTIO"))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to parse ENABLE_ISTIO")
|
||||||
|
}
|
||||||
|
gpm.enableIstio = istio
|
||||||
|
|
||||||
|
if gpm.enableIstio {
|
||||||
|
gpm.istioServiceRegister = makeFuncIstioServiceRegister(
|
||||||
|
gpm.fissionClient.GetCrdClient(), gpm.kubernetesClient, functionNamespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return gpm
|
return gpm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gpm *GenericPoolManager) Run(ctx context.Context) {
|
||||||
|
if gpm.enableIstio && gpm.istioServiceRegister != nil {
|
||||||
|
go gpm.istioServiceRegister.Run(ctx.Done())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (gpm *GenericPoolManager) service() {
|
func (gpm *GenericPoolManager) service() {
|
||||||
for {
|
for {
|
||||||
req := <-gpm.requestChannel
|
req := <-gpm.requestChannel
|
||||||
@@ -97,7 +124,7 @@ func (gpm *GenericPoolManager) service() {
|
|||||||
|
|
||||||
pool, err = MakeGenericPool(
|
pool, err = MakeGenericPool(
|
||||||
gpm.fissionClient, gpm.kubernetesClient, req.env, poolsize,
|
gpm.fissionClient, gpm.kubernetesClient, req.env, poolsize,
|
||||||
gpm.namespace, gpm.fsCache, gpm.instanceId)
|
gpm.namespace, gpm.fsCache, gpm.instanceId, gpm.enableIstio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.responseChannel <- &response{error: err}
|
req.responseChannel <- &response{error: err}
|
||||||
continue
|
continue
|
||||||
@@ -192,3 +219,18 @@ func (gpm *GenericPoolManager) getEnvPoolsize(env *crd.Environment) int32 {
|
|||||||
}
|
}
|
||||||
return poolsize
|
return poolsize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsValidPod checks if pod is not deleted and that it has the address passed as the argument. Also checks that all the
|
||||||
|
// containers in it are reporting a ready status for the healthCheck.
|
||||||
|
func (gpm *GenericPoolManager) IsValidPod(kubeObjects []api.ObjectReference, podAddress string) bool {
|
||||||
|
for _, obj := range kubeObjects {
|
||||||
|
if obj.Kind == "pod" {
|
||||||
|
pod, err := gpm.kubernetesClient.CoreV1().Pods(obj.Namespace).Get(obj.Name, metav1.GetOptions{})
|
||||||
|
if err == nil && strings.Contains(podAddress, pod.Status.PodIP) && fission.IsReadyPod(pod) {
|
||||||
|
log.Printf("Valid pod address : %s", podAddress)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
+13
-2
@@ -40,6 +40,11 @@ import (
|
|||||||
storageSvcClient "github.com/fission/fission/storagesvc/client"
|
storageSvcClient "github.com/fission/fission/storagesvc/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// global verbosity of our CLI
|
||||||
|
verbosity int
|
||||||
|
)
|
||||||
|
|
||||||
func fatal(msg string) {
|
func fatal(msg string) {
|
||||||
os.Stderr.WriteString(msg + "\n")
|
os.Stderr.WriteString(msg + "\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -49,10 +54,16 @@ func warn(msg string) {
|
|||||||
os.Stderr.WriteString(msg + "\n")
|
os.Stderr.WriteString(msg + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getClient(serverUrl string) *client.Client {
|
func verbose(msglevel int, format string, args ...interface{}) {
|
||||||
|
if verbosity >= msglevel {
|
||||||
|
fmt.Printf(format+"\n", args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClient(serverUrl string) *client.Client {
|
||||||
if len(serverUrl) == 0 {
|
if len(serverUrl) == 0 {
|
||||||
fatal("Need --server or FISSION_URL set to your fission server.")
|
// starts local portforwarder etc.
|
||||||
|
serverUrl = getServerUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
isHTTPS := strings.Index(serverUrl, "https://") == 0
|
isHTTPS := strings.Index(serverUrl, "https://") == 0
|
||||||
|
|||||||
+81
-53
@@ -55,22 +55,28 @@ func envCreate(c *cli.Context) error {
|
|||||||
envBuilderImg := c.String("builder")
|
envBuilderImg := c.String("builder")
|
||||||
envBuildCmd := c.String("buildcmd")
|
envBuildCmd := c.String("buildcmd")
|
||||||
envExternalNetwork := c.Bool("externalnetwork")
|
envExternalNetwork := c.Bool("externalnetwork")
|
||||||
|
envGracePeriod := c.Int64("period")
|
||||||
|
if envGracePeriod <= 0 {
|
||||||
|
envGracePeriod = 360
|
||||||
|
}
|
||||||
|
|
||||||
if len(envBuilderImg) > 0 {
|
if len(envBuilderImg) > 0 {
|
||||||
envVersion = 2
|
if !c.IsSet("version") {
|
||||||
|
envVersion = 2
|
||||||
|
}
|
||||||
if len(envBuildCmd) == 0 {
|
if len(envBuildCmd) == 0 {
|
||||||
envBuildCmd = "build"
|
envBuildCmd = "build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceReq := getResourceReq(c)
|
|
||||||
|
|
||||||
// Environment API interface version is not specified and
|
// Environment API interface version is not specified and
|
||||||
// builder image is empty, set default interface version
|
// builder image is empty, set default interface version
|
||||||
if envVersion == 0 {
|
if envVersion == 0 {
|
||||||
envVersion = 1
|
envVersion = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceReq := getResourceReq(c, v1.ResourceRequirements{})
|
||||||
|
|
||||||
env := &crd.Environment{
|
env := &crd.Environment{
|
||||||
Metadata: metav1.ObjectMeta{
|
Metadata: metav1.ObjectMeta{
|
||||||
Name: envName,
|
Name: envName,
|
||||||
@@ -88,9 +94,18 @@ func envCreate(c *cli.Context) error {
|
|||||||
Poolsize: poolsize,
|
Poolsize: poolsize,
|
||||||
Resources: resourceReq,
|
Resources: resourceReq,
|
||||||
AllowAccessToExternalNetwork: envExternalNetwork,
|
AllowAccessToExternalNetwork: envExternalNetwork,
|
||||||
|
TerminationGracePeriod: envGracePeriod,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we're writing a spec, don't call the API
|
||||||
|
if c.Bool("spec") {
|
||||||
|
specFile := fmt.Sprintf("env-%v.yaml", envName)
|
||||||
|
err := specSave(*env, specFile)
|
||||||
|
checkErr(err, "create environment spec")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err := client.EnvironmentCreate(env)
|
_, err := client.EnvironmentCreate(env)
|
||||||
checkErr(err, "create environment")
|
checkErr(err, "create environment")
|
||||||
|
|
||||||
@@ -163,6 +178,10 @@ func envUpdate(c *cli.Context) error {
|
|||||||
env.Spec.Poolsize = c.Int("poolsize")
|
env.Spec.Poolsize = c.Int("poolsize")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.IsSet("period") {
|
||||||
|
env.Spec.TerminationGracePeriod = c.Int64("period")
|
||||||
|
}
|
||||||
|
|
||||||
env.Spec.AllowAccessToExternalNetwork = envExternalNetwork
|
env.Spec.AllowAccessToExternalNetwork = envExternalNetwork
|
||||||
|
|
||||||
_, err = client.EnvironmentUpdate(env)
|
_, err = client.EnvironmentUpdate(env)
|
||||||
@@ -198,66 +217,75 @@ func envList(c *cli.Context) error {
|
|||||||
checkErr(err, "list environments")
|
checkErr(err, "list environments")
|
||||||
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||||
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", "NAME", "UID", "IMAGE", "POOLSIZE", "MINCPU", "MAXCPU", "MINMEMORY", "MAXMEMORY")
|
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", "NAME", "UID", "IMAGE", "POOLSIZE", "MINCPU", "MAXCPU", "MINMEMORY", "MAXMEMORY", "EXTNET", "GRACETIME")
|
||||||
for _, env := range envs {
|
for _, env := range envs {
|
||||||
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
|
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
|
||||||
env.Metadata.Name, env.Metadata.UID, env.Spec.Runtime.Image, env.Spec.Poolsize,
|
env.Metadata.Name, env.Metadata.UID, env.Spec.Runtime.Image, env.Spec.Poolsize,
|
||||||
env.Spec.Resources.Requests.Cpu(), env.Spec.Resources.Limits.Cpu(),
|
env.Spec.Resources.Requests.Cpu(), env.Spec.Resources.Limits.Cpu(),
|
||||||
env.Spec.Resources.Requests.Memory(), env.Spec.Resources.Limits.Memory())
|
env.Spec.Resources.Requests.Memory(), env.Spec.Resources.Limits.Memory(),
|
||||||
|
env.Spec.AllowAccessToExternalNetwork, env.Spec.TerminationGracePeriod)
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getResourceReq(c *cli.Context) v1.ResourceRequirements {
|
func getResourceReq(c *cli.Context, resources v1.ResourceRequirements) v1.ResourceRequirements {
|
||||||
if c.IsSet("mincpu") || c.IsSet("maxcpu") || c.IsSet("minmemory") || c.IsSet("maxmemory") {
|
|
||||||
mincpu := c.Int("mincpu")
|
|
||||||
maxcpu := c.Int("maxcpu")
|
|
||||||
minmem := c.Int("minmemory")
|
|
||||||
maxmem := c.Int("maxmemory")
|
|
||||||
|
|
||||||
requestResources := make(map[v1.ResourceName]resource.Quantity)
|
var requestResources map[v1.ResourceName]resource.Quantity
|
||||||
|
|
||||||
if mincpu != 0 {
|
if len(resources.Requests) == 0 {
|
||||||
cpuRequest, err := resource.ParseQuantity(strconv.Itoa(mincpu) + "m")
|
requestResources = make(map[v1.ResourceName]resource.Quantity)
|
||||||
if err != nil {
|
} else {
|
||||||
fatal("Failed to parse mincpu")
|
requestResources = resources.Requests
|
||||||
}
|
|
||||||
requestResources[v1.ResourceCPU] = cpuRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
if minmem != 0 {
|
|
||||||
memRequest, err := resource.ParseQuantity(strconv.Itoa(minmem) + "Mi")
|
|
||||||
if err != nil {
|
|
||||||
fatal("Failed to parse minmemory")
|
|
||||||
}
|
|
||||||
requestResources[v1.ResourceMemory] = memRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
limitResources := make(map[v1.ResourceName]resource.Quantity)
|
|
||||||
|
|
||||||
if maxcpu != 0 {
|
|
||||||
cpuLimit, err := resource.ParseQuantity(strconv.Itoa(maxcpu) + "m")
|
|
||||||
if err != nil {
|
|
||||||
fatal("Failed to parse maxcpu")
|
|
||||||
}
|
|
||||||
limitResources[v1.ResourceCPU] = cpuLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
if maxmem != 0 {
|
|
||||||
memLimit, err := resource.ParseQuantity(strconv.Itoa(maxmem) + "Mi")
|
|
||||||
if err != nil {
|
|
||||||
fatal("Failed to parse maxmemory")
|
|
||||||
}
|
|
||||||
limitResources[v1.ResourceMemory] = memLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
resources := v1.ResourceRequirements{
|
|
||||||
Requests: requestResources,
|
|
||||||
Limits: limitResources,
|
|
||||||
}
|
|
||||||
return resources
|
|
||||||
}
|
}
|
||||||
return v1.ResourceRequirements{}
|
|
||||||
|
if c.IsSet("mincpu") {
|
||||||
|
mincpu := c.Int("mincpu")
|
||||||
|
cpuRequest, err := resource.ParseQuantity(strconv.Itoa(mincpu) + "m")
|
||||||
|
if err != nil {
|
||||||
|
fatal("Failed to parse mincpu")
|
||||||
|
}
|
||||||
|
requestResources[v1.ResourceCPU] = cpuRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("minmemory") {
|
||||||
|
minmem := c.Int("minmemory")
|
||||||
|
memRequest, err := resource.ParseQuantity(strconv.Itoa(minmem) + "Mi")
|
||||||
|
if err != nil {
|
||||||
|
fatal("Failed to parse minmemory")
|
||||||
|
}
|
||||||
|
requestResources[v1.ResourceMemory] = memRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
var limitResources map[v1.ResourceName]resource.Quantity
|
||||||
|
if len(resources.Limits) == 0 {
|
||||||
|
limitResources = make(map[v1.ResourceName]resource.Quantity)
|
||||||
|
} else {
|
||||||
|
limitResources = resources.Limits
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("maxcpu") {
|
||||||
|
maxcpu := c.Int("maxcpu")
|
||||||
|
cpuLimit, err := resource.ParseQuantity(strconv.Itoa(maxcpu) + "m")
|
||||||
|
if err != nil {
|
||||||
|
fatal("Failed to parse maxcpu")
|
||||||
|
}
|
||||||
|
limitResources[v1.ResourceCPU] = cpuLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("maxmemory") {
|
||||||
|
maxmem := c.Int("maxmemory")
|
||||||
|
memLimit, err := resource.ParseQuantity(strconv.Itoa(maxmem) + "Mi")
|
||||||
|
if err != nil {
|
||||||
|
fatal("Failed to parse maxmemory")
|
||||||
|
}
|
||||||
|
limitResources[v1.ResourceMemory] = memLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
resources = v1.ResourceRequirements{
|
||||||
|
Requests: requestResources,
|
||||||
|
Limits: limitResources,
|
||||||
|
}
|
||||||
|
return resources
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-58
@@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/satori/go.uuid"
|
"github.com/satori/go.uuid"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
apiv1 "k8s.io/client-go/pkg/api/v1"
|
||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
@@ -43,7 +44,7 @@ func printPodLogs(c *cli.Context) error {
|
|||||||
fatal("Need --name argument.")
|
fatal("Need --name argument.")
|
||||||
}
|
}
|
||||||
|
|
||||||
queryURL, err := url.Parse(c.GlobalString("server"))
|
queryURL, err := url.Parse(getServerUrl())
|
||||||
checkErr(err, "parse the base URL")
|
checkErr(err, "parse the base URL")
|
||||||
queryURL.Path = fmt.Sprintf("/proxy/logs/%s", fnName)
|
queryURL.Path = fmt.Sprintf("/proxy/logs/%s", fnName)
|
||||||
|
|
||||||
@@ -117,10 +118,6 @@ func getTargetCPU(c *cli.Context) int {
|
|||||||
func fnCreate(c *cli.Context) error {
|
func fnCreate(c *cli.Context) error {
|
||||||
client := getClient(c.GlobalString("server"))
|
client := getClient(c.GlobalString("server"))
|
||||||
|
|
||||||
if len(c.String("package")) > 0 {
|
|
||||||
fatal("--package is deprecated, please use --deploy instead.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fnName := c.String("name")
|
fnName := c.String("name")
|
||||||
if len(fnName) == 0 {
|
if len(fnName) == 0 {
|
||||||
fatal("Need --name argument.")
|
fatal("Need --name argument.")
|
||||||
@@ -200,7 +197,7 @@ func fnCreate(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
invokeStrategy := getInvokeStrategy(c.Int("minscale"), c.Int("maxscale"), c.String("executortype"), getTargetCPU(c))
|
invokeStrategy := getInvokeStrategy(c.Int("minscale"), c.Int("maxscale"), c.String("executortype"), getTargetCPU(c))
|
||||||
resourceReq := getResourceReq(c)
|
resourceReq := getResourceReq(c, apiv1.ResourceRequirements{})
|
||||||
if (c.IsSet("mincpu") || c.IsSet("maxcpu") || c.IsSet("minmemory") || c.IsSet("maxmemory")) &&
|
if (c.IsSet("mincpu") || c.IsSet("maxcpu") || c.IsSet("minmemory") || c.IsSet("maxmemory")) &&
|
||||||
invokeStrategy.ExecutionStrategy.ExecutorType == fission.ExecutorTypePoolmgr {
|
invokeStrategy.ExecutionStrategy.ExecutorType == fission.ExecutorTypePoolmgr {
|
||||||
warn("CPU/Memory specified for function with pool manager executor will be ignored in favor of resources specified at environment")
|
warn("CPU/Memory specified for function with pool manager executor will be ignored in favor of resources specified at environment")
|
||||||
@@ -274,6 +271,10 @@ func fnCreate(c *cli.Context) error {
|
|||||||
if len(triggerUrl) == 0 {
|
if len(triggerUrl) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if !strings.HasPrefix(triggerUrl, "/") {
|
||||||
|
triggerUrl = fmt.Sprintf("/%s", triggerUrl)
|
||||||
|
}
|
||||||
|
|
||||||
method := c.String("method")
|
method := c.String("method")
|
||||||
if len(method) == 0 {
|
if len(method) == 0 {
|
||||||
method = "GET"
|
method = "GET"
|
||||||
@@ -387,10 +388,8 @@ func fnUpdate(c *cli.Context) error {
|
|||||||
secretNameSpace := c.String("secretNamespace")
|
secretNameSpace := c.String("secretNamespace")
|
||||||
cfgMapNameSpace := c.String("configmapNamespace")
|
cfgMapNameSpace := c.String("configmapNamespace")
|
||||||
|
|
||||||
if len(envName) == 0 && len(deployArchiveName) == 0 && len(srcArchiveName) == 0 && len(pkgName) == 0 &&
|
if len(srcArchiveName) > 0 && len(deployArchiveName) > 0 {
|
||||||
len(entrypoint) == 0 && len(buildcmd) == 0 && len(secretName) == 0 && len(secretNameSpace) == 0 &&
|
fatal("Need either of --src or --deploy and not both arguments.")
|
||||||
len(cfgMapName) == 0 && len(cfgMapNameSpace) == 0 {
|
|
||||||
fatal("Need --env or --deploy or --src or --pkg or --entrypoint or --buildcmd or --secret or --secretNamespace or --configmap or --configmapNamespace argument.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(secretName) > 0 {
|
if len(secretName) > 0 {
|
||||||
@@ -471,9 +470,11 @@ func fnUpdate(c *cli.Context) error {
|
|||||||
ResourceVersion: pkgMetadata.ResourceVersion,
|
ResourceVersion: pkgMetadata.ResourceVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
function.Spec.Resources = getResourceReq(c)
|
function.Spec.Resources = getResourceReq(c, function.Spec.Resources)
|
||||||
|
|
||||||
function.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent = getTargetCPU(c)
|
if c.IsSet("targetcpu") {
|
||||||
|
function.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent = getTargetCPU(c)
|
||||||
|
}
|
||||||
|
|
||||||
if c.IsSet("minscale") {
|
if c.IsSet("minscale") {
|
||||||
minscale := c.Int("minscale")
|
minscale := c.Int("minscale")
|
||||||
@@ -498,7 +499,7 @@ func fnUpdate(c *cli.Context) error {
|
|||||||
function.Spec.InvokeStrategy.ExecutionStrategy.MaxScale = maxscale
|
function.Spec.InvokeStrategy.ExecutionStrategy.MaxScale = maxscale
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.String("executortype") != "" {
|
if c.IsSet("executortype") {
|
||||||
var fnExecutor fission.ExecutorType
|
var fnExecutor fission.ExecutorType
|
||||||
switch c.String("executortype") {
|
switch c.String("executortype") {
|
||||||
case "":
|
case "":
|
||||||
@@ -552,13 +553,19 @@ func fnList(c *cli.Context) error {
|
|||||||
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||||
|
|
||||||
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\n", "NAME", "UID", "ENV", "EXECUTORTYPE", "MINSCALE", "MAXSCALE", "TARGETCPU")
|
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", "NAME", "UID", "ENV", "EXECUTORTYPE", "MINSCALE", "MAXSCALE", "MINCPU", "MAXCPU", "MINMEMORY", "MAXMEMORY", "TARGETCPU")
|
||||||
for _, f := range fns {
|
for _, f := range fns {
|
||||||
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
|
mincpu := f.Spec.Resources.Requests.Cpu
|
||||||
|
mincpu().Value()
|
||||||
|
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
|
||||||
f.Metadata.Name, f.Metadata.UID, f.Spec.Environment.Name,
|
f.Metadata.Name, f.Metadata.UID, f.Spec.Environment.Name,
|
||||||
f.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType,
|
f.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType,
|
||||||
f.Spec.InvokeStrategy.ExecutionStrategy.MinScale,
|
f.Spec.InvokeStrategy.ExecutionStrategy.MinScale,
|
||||||
f.Spec.InvokeStrategy.ExecutionStrategy.MaxScale,
|
f.Spec.InvokeStrategy.ExecutionStrategy.MaxScale,
|
||||||
|
f.Spec.Resources.Requests.Cpu().String(),
|
||||||
|
f.Spec.Resources.Limits.Cpu().String(),
|
||||||
|
f.Spec.Resources.Requests.Memory().String(),
|
||||||
|
f.Spec.Resources.Limits.Memory().String(),
|
||||||
f.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent)
|
f.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent)
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
@@ -595,7 +602,7 @@ func fnLogs(c *cli.Context) error {
|
|||||||
checkErr(err, "get function")
|
checkErr(err, "get function")
|
||||||
|
|
||||||
// request the controller to establish a proxy server to the database.
|
// request the controller to establish a proxy server to the database.
|
||||||
logDB, err := logdb.GetLogDB(dbType, c.GlobalString("server"))
|
logDB, err := logdb.GetLogDB(dbType, getServerUrl())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("failed to connect log database")
|
fatal("failed to connect log database")
|
||||||
}
|
}
|
||||||
@@ -647,48 +654,6 @@ func fnLogs(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnPods(c *cli.Context) error {
|
|
||||||
client := getClient(c.GlobalString("server"))
|
|
||||||
|
|
||||||
fnName := c.String("name")
|
|
||||||
if len(fnName) == 0 {
|
|
||||||
fatal("Need name of function, use --name")
|
|
||||||
}
|
|
||||||
|
|
||||||
dbType := c.String("dbtype")
|
|
||||||
if len(dbType) == 0 {
|
|
||||||
dbType = logdb.INFLUXDB
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &metav1.ObjectMeta{Name: fnName}
|
|
||||||
|
|
||||||
f, err := client.FunctionGet(m)
|
|
||||||
checkErr(err, "get function")
|
|
||||||
|
|
||||||
// client first sends db query to the controller, then the controller
|
|
||||||
// will establish a proxy server that bridges the client and the database.
|
|
||||||
logDB, err := logdb.GetLogDB(dbType, c.GlobalString("server"))
|
|
||||||
if err != nil {
|
|
||||||
fatal("failed to connect log database")
|
|
||||||
}
|
|
||||||
|
|
||||||
logFilter := logdb.LogFilter{
|
|
||||||
Function: f.Metadata.Name,
|
|
||||||
FuncUid: string(f.Metadata.UID),
|
|
||||||
}
|
|
||||||
pods, err := logDB.GetPods(logFilter)
|
|
||||||
if err != nil {
|
|
||||||
fatal("failed to get pods of function")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("NAME\t\n")
|
|
||||||
for _, pod := range pods {
|
|
||||||
fmt.Println(pod)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func fnTest(c *cli.Context) error {
|
func fnTest(c *cli.Context) error {
|
||||||
fnName := c.String("name")
|
fnName := c.String("name")
|
||||||
if len(fnName) == 0 {
|
if len(fnName) == 0 {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ func checkFunctionExistence(fissionClient *client.Client, fnName string) {
|
|||||||
Name: fnName,
|
Name: fnName,
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := fissionClient.FunctionGet(meta)
|
_, err := fissionClient.FunctionGet(meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("function '%v' does not exist, use 'fission function create --name %v ...' to create the function\n", fnName, fnName)
|
fmt.Printf("function '%v' does not exist, use 'fission function create --name %v ...' to create the function\n", fnName, fnName)
|
||||||
@@ -80,6 +81,10 @@ func htCreate(c *cli.Context) error {
|
|||||||
if len(triggerUrl) == 0 {
|
if len(triggerUrl) == 0 {
|
||||||
fatal("Need a trigger URL, use --url")
|
fatal("Need a trigger URL, use --url")
|
||||||
}
|
}
|
||||||
|
if !strings.HasPrefix(triggerUrl, "/") {
|
||||||
|
triggerUrl = fmt.Sprintf("/%s", triggerUrl)
|
||||||
|
}
|
||||||
|
|
||||||
method := c.String("method")
|
method := c.String("method")
|
||||||
if len(method) == 0 {
|
if len(method) == 0 {
|
||||||
method = "GET"
|
method = "GET"
|
||||||
@@ -105,6 +110,14 @@ func htCreate(c *cli.Context) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we're writing a spec, don't call the API
|
||||||
|
if c.Bool("spec") {
|
||||||
|
specFile := fmt.Sprintf("route-%v.yaml", triggerName)
|
||||||
|
err := specSave(*ht, specFile)
|
||||||
|
checkErr(err, "create HTTP trigger spec")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err := client.HTTPTriggerCreate(ht)
|
_, err := client.HTTPTriggerCreate(ht)
|
||||||
checkErr(err, "create HTTP trigger")
|
checkErr(err, "create HTTP trigger")
|
||||||
|
|
||||||
|
|||||||
@@ -45,28 +45,6 @@ type InfluxDB struct {
|
|||||||
endpoint string
|
endpoint string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (influx InfluxDB) GetPods(filter LogFilter) ([]string, error) {
|
|
||||||
parameters := make(map[string]interface{})
|
|
||||||
parameters["funcuid"] = filter.FuncUid
|
|
||||||
|
|
||||||
queryCmd := "select * from \"log\" where \"funcuid\" = $funcuid group by \"pod\""
|
|
||||||
query := influxdbClient.NewQueryWithParameters(queryCmd, INFLUXDB_DATABASE, "", parameters)
|
|
||||||
|
|
||||||
response, err := influx.query(query)
|
|
||||||
if err != nil /*|| response.Err != ""*/ {
|
|
||||||
return []string{}, err
|
|
||||||
}
|
|
||||||
pods := []string{}
|
|
||||||
for _, r := range response.Results {
|
|
||||||
for _, series := range r.Series {
|
|
||||||
for _, pod := range series.Tags {
|
|
||||||
pods = append(pods, pod)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pods, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeIndexMap(cols []string) map[string]int {
|
func makeIndexMap(cols []string) map[string]int {
|
||||||
indexMap := make(map[string]int, len(cols))
|
indexMap := make(map[string]int, len(cols))
|
||||||
for i := range cols {
|
for i := range cols {
|
||||||
@@ -98,7 +76,7 @@ func (influx InfluxDB) GetLogs(filter LogFilter) ([]LogEntry, error) {
|
|||||||
logEntries := []LogEntry{}
|
logEntries := []LogEntry{}
|
||||||
response, err := influx.query(query)
|
response, err := influx.query(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return logEntries, nil
|
return logEntries, err
|
||||||
}
|
}
|
||||||
for _, r := range response.Results {
|
for _, r := range response.Results {
|
||||||
for _, series := range r.Series {
|
for _, series := range r.Series {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LogDatabase interface {
|
type LogDatabase interface {
|
||||||
GetPods(LogFilter) ([]string, error)
|
|
||||||
GetLogs(LogFilter) ([]LogEntry, error)
|
GetLogs(LogFilter) ([]LogEntry, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-32
@@ -60,29 +60,38 @@ func getFissionAPIVersion(apiUrl string) (string, error) {
|
|||||||
return strings.TrimRight(string(body), "\n"), nil
|
return strings.TrimRight(string(body), "\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func getServerUrl() string {
|
||||||
app := cli.NewApp()
|
var serverUrl string
|
||||||
app.Name = "fission"
|
// Use FISSION_URL env variable if set; otherwise, port-forward to controller.
|
||||||
app.Usage = "Serverless functions for Kubernetes"
|
|
||||||
app.Version = version.Version
|
|
||||||
|
|
||||||
// fetch the FISSION_URL env variable. If not set, port-forward to controller.
|
|
||||||
var value string
|
|
||||||
fissionUrl := os.Getenv("FISSION_URL")
|
fissionUrl := os.Getenv("FISSION_URL")
|
||||||
if len(fissionUrl) == 0 {
|
if len(fissionUrl) == 0 {
|
||||||
fissionNamespace := getFissionNamespace()
|
fissionNamespace := getFissionNamespace()
|
||||||
kubeConfig := getKubeConfigPath()
|
kubeConfig := getKubeConfigPath()
|
||||||
localPort := setupPortForward(
|
localPort := setupPortForward(
|
||||||
kubeConfig, fissionNamespace, "application=fission-api")
|
kubeConfig, fissionNamespace, "application=fission-api")
|
||||||
value = "http://127.0.0.1:" + localPort
|
serverUrl = "http://127.0.0.1:" + localPort
|
||||||
} else {
|
} else {
|
||||||
value = fissionUrl
|
serverUrl = fissionUrl
|
||||||
}
|
}
|
||||||
|
return serverUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
func cliHook(c *cli.Context) error {
|
||||||
|
verbosity = c.Int("verbosity")
|
||||||
|
verbose(2, "Verbosity = 2")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "fission"
|
||||||
|
app.Usage = "Serverless functions for Kubernetes"
|
||||||
|
app.Version = version.Version
|
||||||
|
|
||||||
cli.VersionPrinter = func(c *cli.Context) {
|
cli.VersionPrinter = func(c *cli.Context) {
|
||||||
clientVer := version.VersionInfo().String()
|
clientVer := version.VersionInfo().String()
|
||||||
fmt.Printf("Client Version: %v\n", clientVer)
|
fmt.Printf("Client Version: %v\n", clientVer)
|
||||||
serverVer, err := getFissionAPIVersion(value)
|
serverVer, err := getFissionAPIVersion(getServerUrl())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error getting Fission API version: %v", err)
|
fmt.Printf("Error getting Fission API version: %v", err)
|
||||||
} else {
|
} else {
|
||||||
@@ -91,11 +100,15 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
cli.StringFlag{Name: "server", Value: value, Usage: "Fission server URL"},
|
cli.StringFlag{Name: "server", Value: "", Usage: "Fission server URL"},
|
||||||
|
cli.IntFlag{Name: "verbosity", Value: 1, Usage: "CLI verbosity (0 is quiet, 1 is the default, 2 is verbose.)"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all resource create commands accept --spec
|
||||||
|
specSaveFlag := cli.BoolFlag{Name: "spec", Usage: "Save to the spec directory instead of creating on cluster"}
|
||||||
|
|
||||||
// trigger method and url flags (used in function and route CLIs)
|
// trigger method and url flags (used in function and route CLIs)
|
||||||
htMethodFlag := cli.StringFlag{Name: "method", Usage: "HTTP Method: GET|POST|PUT|DELETE|HEAD; defaults to GET"}
|
htMethodFlag := cli.StringFlag{Name: "method", Value: "GET", Usage: "HTTP Method: GET|POST|PUT|DELETE|HEAD"}
|
||||||
htUrlFlag := cli.StringFlag{Name: "url", Usage: "URL pattern (See gorilla/mux supported patterns)"}
|
htUrlFlag := cli.StringFlag{Name: "url", Usage: "URL pattern (See gorilla/mux supported patterns)"}
|
||||||
|
|
||||||
// Resource & scale related flags (Used in env and function)
|
// Resource & scale related flags (Used in env and function)
|
||||||
@@ -105,13 +118,12 @@ func main() {
|
|||||||
maxMem := cli.StringFlag{Name: "maxmemory", Usage: "Maximum memory to be assigned to pod (In megabyte)"}
|
maxMem := cli.StringFlag{Name: "maxmemory", Usage: "Maximum memory to be assigned to pod (In megabyte)"}
|
||||||
minScale := cli.StringFlag{Name: "minscale", Usage: "Minimum number of pods (Uses resource inputs to configure HPA)"}
|
minScale := cli.StringFlag{Name: "minscale", Usage: "Minimum number of pods (Uses resource inputs to configure HPA)"}
|
||||||
maxScale := cli.StringFlag{Name: "maxscale", Usage: "Maximum number of pods (Uses resource inputs to configure HPA)"}
|
maxScale := cli.StringFlag{Name: "maxscale", Usage: "Maximum number of pods (Uses resource inputs to configure HPA)"}
|
||||||
targetcpu := cli.StringFlag{Name: "targetcpu", Usage: "Target average CPU across pods for scaling (In percentage, defaults to 80)"}
|
targetcpu := cli.IntFlag{Name: "targetcpu", Value: 80, Usage: "Target average CPU usage percentage across pods for scaling"}
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"}
|
fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"}
|
||||||
fnEnvNameFlag := cli.StringFlag{Name: "env", Usage: "environment name for function"}
|
fnEnvNameFlag := cli.StringFlag{Name: "env", Usage: "environment name for function"}
|
||||||
fnCodeFlag := cli.StringFlag{Name: "code", Usage: "local path or URL for source code"}
|
fnCodeFlag := cli.StringFlag{Name: "code", Usage: "local path or URL for source code"}
|
||||||
fnPackageFlag := cli.StringFlag{Name: "package", Usage: "(Deprecated) local path or URL for binary package"}
|
|
||||||
fnDeployArchiveFlag := cli.StringFlag{Name: "deployarchive, deploy", Usage: "local path or URL for deployment archive"}
|
fnDeployArchiveFlag := cli.StringFlag{Name: "deployarchive, deploy", Usage: "local path or URL for deployment archive"}
|
||||||
fnSrcArchiveFlag := cli.StringFlag{Name: "sourcearchive, src", Usage: "local path or URL for source archive"}
|
fnSrcArchiveFlag := cli.StringFlag{Name: "sourcearchive, src", Usage: "local path or URL for source archive"}
|
||||||
fnPkgNameFlag := cli.StringFlag{Name: "pkgname, pkg", Usage: "Name of the existing package (--deploy and --src and --env will be ignored)"}
|
fnPkgNameFlag := cli.StringFlag{Name: "pkgname, pkg", Usage: "Name of the existing package (--deploy and --src and --env will be ignored)"}
|
||||||
@@ -129,26 +141,24 @@ func main() {
|
|||||||
fnCfgMapnsFlag := cli.StringFlag{Name: "configmapNamespace", Usage: "namespace of configmap"}
|
fnCfgMapnsFlag := cli.StringFlag{Name: "configmapNamespace", Usage: "namespace of configmap"}
|
||||||
fnLogCountFlag := cli.StringFlag{Name: "recordcount", Usage: "the n most recent log records"}
|
fnLogCountFlag := cli.StringFlag{Name: "recordcount", Usage: "the n most recent log records"}
|
||||||
fnForceFlag := cli.BoolFlag{Name: "force", Usage: "Force update a package even if it is used by one or more functions"}
|
fnForceFlag := cli.BoolFlag{Name: "force", Usage: "Force update a package even if it is used by one or more functions"}
|
||||||
fnExecutorTypeFlag := cli.StringFlag{Name: "executortype", Usage: "Executor type for execution; one of 'poolmgr', 'newdeploy' defaults to 'poolmgr'"}
|
fnExecutorTypeFlag := cli.StringFlag{Name: "executortype", Value: "poolmgr", Usage: "Executor type for execution; one of 'poolmgr', 'newdeploy'"}
|
||||||
fnSpecSaveFlag := cli.BoolFlag{Name: "spec", Usage: "Save function to the spec directory instead of creating it"}
|
|
||||||
|
|
||||||
fnSubcommands := []cli.Command{
|
fnSubcommands := []cli.Command{
|
||||||
{Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnSpecSaveFlag, fnCodeFlag, fnPackageFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnBuildCmdFlag, fnPkgNameFlag, htUrlFlag, htMethodFlag, minCpu, maxCpu, minMem, maxMem, minScale, maxScale, fnExecutorTypeFlag, targetcpu, fnCfgMapFlag, fnSecretFlag, fnSecretnsFlag, fnCfgMapnsFlag}, Action: fnCreate},
|
{Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, specSaveFlag, fnCodeFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnBuildCmdFlag, fnPkgNameFlag, htUrlFlag, htMethodFlag, minCpu, maxCpu, minMem, maxMem, minScale, maxScale, fnExecutorTypeFlag, targetcpu, fnCfgMapFlag, fnSecretFlag, fnSecretnsFlag, fnCfgMapnsFlag}, Action: fnCreate},
|
||||||
{Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag}, Action: fnGet},
|
{Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag}, Action: fnGet},
|
||||||
{Name: "getmeta", Usage: "Get function metadata", Flags: []cli.Flag{fnNameFlag}, Action: fnGetMeta},
|
{Name: "getmeta", Usage: "Get function metadata", Flags: []cli.Flag{fnNameFlag}, Action: fnGetMeta},
|
||||||
{Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnPkgNameFlag, fnBuildCmdFlag, fnForceFlag, minCpu, maxCpu, minMem, maxMem, minScale, maxScale, fnExecutorTypeFlag, targetcpu}, Action: fnUpdate},
|
{Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnPkgNameFlag, fnBuildCmdFlag, fnForceFlag, minCpu, maxCpu, minMem, maxMem, minScale, maxScale, fnExecutorTypeFlag, targetcpu}, Action: fnUpdate},
|
||||||
{Name: "delete", Usage: "Delete function", Flags: []cli.Flag{fnNameFlag}, Action: fnDelete},
|
{Name: "delete", Usage: "Delete function", Flags: []cli.Flag{fnNameFlag}, Action: fnDelete},
|
||||||
{Name: "list", Usage: "List all functions", Flags: []cli.Flag{}, Action: fnList},
|
{Name: "list", Usage: "List all functions", Flags: []cli.Flag{}, Action: fnList},
|
||||||
{Name: "logs", Usage: "Display function logs", Flags: []cli.Flag{fnNameFlag, fnPodFlag, fnFollowFlag, fnDetailFlag, fnLogDBTypeFlag, fnLogCountFlag}, Action: fnLogs},
|
{Name: "logs", Usage: "Display function logs", Flags: []cli.Flag{fnNameFlag, fnPodFlag, fnFollowFlag, fnDetailFlag, fnLogDBTypeFlag, fnLogCountFlag}, Action: fnLogs},
|
||||||
{Name: "pods", Usage: "Display function pods", Flags: []cli.Flag{fnNameFlag, fnLogDBTypeFlag}, Action: fnPods},
|
{Name: "test", Usage: "Test a function", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnSrcArchiveFlag, htMethodFlag, fnBodyFlag, fnHeaderFlag}, Action: fnTest},
|
||||||
{Name: "test", Usage: "Test a function", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag, fnSrcArchiveFlag, htMethodFlag, fnBodyFlag, fnHeaderFlag}, Action: fnTest},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// httptriggers
|
// httptriggers
|
||||||
htNameFlag := cli.StringFlag{Name: "name", Usage: "HTTP Trigger name"}
|
htNameFlag := cli.StringFlag{Name: "name", Usage: "HTTP Trigger name"}
|
||||||
htFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
|
htFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
|
||||||
htSubcommands := []cli.Command{
|
htSubcommands := []cli.Command{
|
||||||
{Name: "create", Aliases: []string{"add"}, Usage: "Create HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag, htFnNameFlag}, Action: htCreate},
|
{Name: "create", Aliases: []string{"add"}, Usage: "Create HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag, htFnNameFlag, specSaveFlag}, Action: htCreate},
|
||||||
{Name: "get", Usage: "Get HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag}, Action: htGet},
|
{Name: "get", Usage: "Get HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag}, Action: htGet},
|
||||||
{Name: "update", Usage: "Update HTTP trigger", Flags: []cli.Flag{htNameFlag, htFnNameFlag}, Action: htUpdate},
|
{Name: "update", Usage: "Update HTTP trigger", Flags: []cli.Flag{htNameFlag, htFnNameFlag}, Action: htUpdate},
|
||||||
{Name: "delete", Usage: "Delete HTTP trigger", Flags: []cli.Flag{htNameFlag}, Action: htDelete},
|
{Name: "delete", Usage: "Delete HTTP trigger", Flags: []cli.Flag{htNameFlag}, Action: htDelete},
|
||||||
@@ -160,7 +170,7 @@ func main() {
|
|||||||
ttCronFlag := cli.StringFlag{Name: "cron", Usage: "Time Trigger cron spec ('0 30 * * *', '@every 5m', '@hourly')"}
|
ttCronFlag := cli.StringFlag{Name: "cron", Usage: "Time Trigger cron spec ('0 30 * * *', '@every 5m', '@hourly')"}
|
||||||
ttFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
|
ttFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
|
||||||
ttSubcommands := []cli.Command{
|
ttSubcommands := []cli.Command{
|
||||||
{Name: "create", Aliases: []string{"add"}, Usage: "Create Time trigger", Flags: []cli.Flag{ttNameFlag, ttFnNameFlag, ttCronFlag}, Action: ttCreate},
|
{Name: "create", Aliases: []string{"add"}, Usage: "Create Time trigger", Flags: []cli.Flag{ttNameFlag, ttFnNameFlag, ttCronFlag, specSaveFlag}, Action: ttCreate},
|
||||||
{Name: "get", Usage: "Get Time trigger", Flags: []cli.Flag{}, Action: ttGet},
|
{Name: "get", Usage: "Get Time trigger", Flags: []cli.Flag{}, Action: ttGet},
|
||||||
{Name: "update", Usage: "Update Time trigger", Flags: []cli.Flag{ttNameFlag, ttCronFlag, ttFnNameFlag}, Action: ttUpdate},
|
{Name: "update", Usage: "Update Time trigger", Flags: []cli.Flag{ttNameFlag, ttCronFlag, ttFnNameFlag}, Action: ttUpdate},
|
||||||
{Name: "delete", Usage: "Delete Time trigger", Flags: []cli.Flag{ttNameFlag}, Action: ttDelete},
|
{Name: "delete", Usage: "Delete Time trigger", Flags: []cli.Flag{ttNameFlag}, Action: ttDelete},
|
||||||
@@ -170,12 +180,12 @@ func main() {
|
|||||||
// Message queue trigger
|
// Message queue trigger
|
||||||
mqtNameFlag := cli.StringFlag{Name: "name", Usage: "Message queue Trigger name"}
|
mqtNameFlag := cli.StringFlag{Name: "name", Usage: "Message queue Trigger name"}
|
||||||
mqtFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
|
mqtFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
|
||||||
mqtMQTypeFlag := cli.StringFlag{Name: "mqtype", Usage: "Message queue type, e.g. nats-streaming, azure-storage-queue (optional; uses \"nats-streaming\" if unspecified)"}
|
mqtMQTypeFlag := cli.StringFlag{Name: "mqtype", Value: "nats-streaming", Usage: "Message queue type, e.g. nats-streaming, azure-storage-queue (optional)"}
|
||||||
mqtTopicFlag := cli.StringFlag{Name: "topic", Usage: "Message queue Topic the trigger listens on"}
|
mqtTopicFlag := cli.StringFlag{Name: "topic", Usage: "Message queue Topic the trigger listens on"}
|
||||||
mqtRespTopicFlag := cli.StringFlag{Name: "resptopic", Usage: "Topic that the function response is sent on (optional; response discarded if unspecified)"}
|
mqtRespTopicFlag := cli.StringFlag{Name: "resptopic", Usage: "Topic that the function response is sent on (optional; response discarded if unspecified)"}
|
||||||
mqtMsgContentType := cli.StringFlag{Name: "contenttype, c", Usage: "Content type of messages that publish to the topic (optional; uses \"application/json\" if unspecified)"}
|
mqtMsgContentType := cli.StringFlag{Name: "contenttype, c", Value: "application/json", Usage: "Content type of messages that publish to the topic (optional)"}
|
||||||
mqtSubcommands := []cli.Command{
|
mqtSubcommands := []cli.Command{
|
||||||
{Name: "create", Aliases: []string{"add"}, Usage: "Create Message queue trigger", Flags: []cli.Flag{mqtNameFlag, mqtFnNameFlag, mqtMQTypeFlag, mqtTopicFlag, mqtRespTopicFlag, mqtMsgContentType}, Action: mqtCreate},
|
{Name: "create", Aliases: []string{"add"}, Usage: "Create Message queue trigger", Flags: []cli.Flag{mqtNameFlag, mqtFnNameFlag, mqtMQTypeFlag, mqtTopicFlag, mqtRespTopicFlag, mqtMsgContentType, specSaveFlag}, Action: mqtCreate},
|
||||||
{Name: "get", Usage: "Get message queue trigger", Flags: []cli.Flag{}, Action: mqtGet},
|
{Name: "get", Usage: "Get message queue trigger", Flags: []cli.Flag{}, Action: mqtGet},
|
||||||
{Name: "update", Usage: "Update message queue trigger", Flags: []cli.Flag{mqtNameFlag, mqtTopicFlag, mqtRespTopicFlag, mqtFnNameFlag, mqtMsgContentType}, Action: mqtUpdate},
|
{Name: "update", Usage: "Update message queue trigger", Flags: []cli.Flag{mqtNameFlag, mqtTopicFlag, mqtRespTopicFlag, mqtFnNameFlag, mqtMsgContentType}, Action: mqtUpdate},
|
||||||
{Name: "delete", Usage: "Delete message queue trigger", Flags: []cli.Flag{mqtNameFlag}, Action: mqtDelete},
|
{Name: "delete", Usage: "Delete message queue trigger", Flags: []cli.Flag{mqtNameFlag}, Action: mqtDelete},
|
||||||
@@ -184,17 +194,17 @@ func main() {
|
|||||||
|
|
||||||
// environments
|
// environments
|
||||||
envNameFlag := cli.StringFlag{Name: "name", Usage: "Environment name"}
|
envNameFlag := cli.StringFlag{Name: "name", Usage: "Environment name"}
|
||||||
envPoolsizeFlag := cli.IntFlag{Name: "poolsize", Usage: "Size of the pool, if not specified defaults to 3"}
|
envPoolsizeFlag := cli.IntFlag{Name: "poolsize", Value: 3, Usage: "Size of the pool"}
|
||||||
envImageFlag := cli.StringFlag{Name: "image", Usage: "Environment image URL"}
|
envImageFlag := cli.StringFlag{Name: "image", Usage: "Environment image URL"}
|
||||||
envBuilderImageFlag := cli.StringFlag{Name: "builder", Usage: "Environment builder image URL (optional)"}
|
envBuilderImageFlag := cli.StringFlag{Name: "builder", Usage: "Environment builder image URL (optional)"}
|
||||||
envBuildCmdFlag := cli.StringFlag{Name: "buildcmd", Usage: "Build command for environment builder to build source package (optional)"}
|
envBuildCmdFlag := cli.StringFlag{Name: "buildcmd", Usage: "Build command for environment builder to build source package (optional)"}
|
||||||
envExternalNetworkFlag := cli.BoolFlag{Name: "externalnetwork", Usage: "Allow environment access external network when istio feature enabled (optional, defaults to false)"}
|
envExternalNetworkFlag := cli.BoolFlag{Name: "externalnetwork", Usage: "Allow environment access external network when istio feature enabled (optional, defaults to false)"}
|
||||||
|
envTerminationGracePeriodFlag := cli.Int64Flag{Name: "graceperiod, period", Value: 360, Usage: "The grace time (in seconds) for pod to perform connection draining before termination (optional)"}
|
||||||
envVersionFlag := cli.IntFlag{Name: "version", Usage: "Environment API version: defaults to 1 (means v1 interface)"}
|
envVersionFlag := cli.IntFlag{Name: "version", Value: 1, Usage: "Environment API version (1 means v1 interface)"}
|
||||||
envSubcommands := []cli.Command{
|
envSubcommands := []cli.Command{
|
||||||
{Name: "create", Aliases: []string{"add"}, Usage: "Add an environment", Flags: []cli.Flag{envNameFlag, envPoolsizeFlag, envImageFlag, envBuilderImageFlag, envBuildCmdFlag, minCpu, maxCpu, minMem, maxMem, envVersionFlag, envExternalNetworkFlag}, Action: envCreate},
|
{Name: "create", Aliases: []string{"add"}, Usage: "Add an environment", Flags: []cli.Flag{envNameFlag, envPoolsizeFlag, envImageFlag, envBuilderImageFlag, envBuildCmdFlag, minCpu, maxCpu, minMem, maxMem, envVersionFlag, envExternalNetworkFlag, envTerminationGracePeriodFlag, specSaveFlag}, Action: envCreate},
|
||||||
{Name: "get", Usage: "Get environment details", Flags: []cli.Flag{envNameFlag}, Action: envGet},
|
{Name: "get", Usage: "Get environment details", Flags: []cli.Flag{envNameFlag}, Action: envGet},
|
||||||
{Name: "update", Usage: "Update environment", Flags: []cli.Flag{envNameFlag, envPoolsizeFlag, envImageFlag, envBuilderImageFlag, envBuildCmdFlag, minCpu, maxCpu, minMem, maxMem, envExternalNetworkFlag}, Action: envUpdate},
|
{Name: "update", Usage: "Update environment", Flags: []cli.Flag{envNameFlag, envPoolsizeFlag, envImageFlag, envBuilderImageFlag, envBuildCmdFlag, minCpu, maxCpu, minMem, maxMem, envExternalNetworkFlag, envTerminationGracePeriodFlag}, Action: envUpdate},
|
||||||
{Name: "delete", Usage: "Delete environment", Flags: []cli.Flag{envNameFlag}, Action: envDelete},
|
{Name: "delete", Usage: "Delete environment", Flags: []cli.Flag{envNameFlag}, Action: envDelete},
|
||||||
{Name: "list", Usage: "List all environments", Flags: []cli.Flag{}, Action: envList},
|
{Name: "list", Usage: "List all environments", Flags: []cli.Flag{}, Action: envList},
|
||||||
}
|
}
|
||||||
@@ -206,7 +216,7 @@ func main() {
|
|||||||
wObjTypeFlag := cli.StringFlag{Name: "type", Usage: "Type of resource to watch (Pod, Service, etc.)"}
|
wObjTypeFlag := cli.StringFlag{Name: "type", Usage: "Type of resource to watch (Pod, Service, etc.)"}
|
||||||
wLabelsFlag := cli.StringFlag{Name: "labels", Usage: "Label selector of the form a=b,c=d"}
|
wLabelsFlag := cli.StringFlag{Name: "labels", Usage: "Label selector of the form a=b,c=d"}
|
||||||
wSubCommands := []cli.Command{
|
wSubCommands := []cli.Command{
|
||||||
{Name: "create", Aliases: []string{"add"}, Usage: "Create a watch", Flags: []cli.Flag{wFnNameFlag, wNamespaceFlag, wObjTypeFlag, wLabelsFlag}, Action: wCreate},
|
{Name: "create", Aliases: []string{"add"}, Usage: "Create a watch", Flags: []cli.Flag{wFnNameFlag, wNamespaceFlag, wObjTypeFlag, wLabelsFlag, specSaveFlag}, Action: wCreate},
|
||||||
{Name: "get", Usage: "Get details about a watch", Flags: []cli.Flag{wNameFlag}, Action: wGet},
|
{Name: "get", Usage: "Get details about a watch", Flags: []cli.Flag{wNameFlag}, Action: wGet},
|
||||||
// TODO add update flag when supported
|
// TODO add update flag when supported
|
||||||
{Name: "delete", Usage: "Delete watch", Flags: []cli.Flag{wNameFlag}, Action: wDelete},
|
{Name: "delete", Usage: "Delete watch", Flags: []cli.Flag{wNameFlag}, Action: wDelete},
|
||||||
@@ -273,5 +283,6 @@ func main() {
|
|||||||
{Name: "tpr2crd", Aliases: []string{}, Usage: "Migrate tool for TPR to CRD", Subcommands: migrateSubCommands},
|
{Name: "tpr2crd", Aliases: []string{}, Usage: "Migrate tool for TPR to CRD", Subcommands: migrateSubCommands},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.Before = cliHook
|
||||||
app.Run(os.Args)
|
app.Run(os.Args)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -27,7 +27,6 @@ import (
|
|||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/controller/client"
|
"github.com/fission/fission/controller/client"
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
"github.com/fission/fission/mqtrigger/messageQueue"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -55,7 +54,7 @@ func migrateDumpTPRResource(client *client.Client, filename string) {
|
|||||||
checkErr(err, "dump watches")
|
checkErr(err, "dump watches")
|
||||||
timeTriggers, err := client.TimeTriggerList()
|
timeTriggers, err := client.TimeTriggerList()
|
||||||
checkErr(err, "dump time triggers")
|
checkErr(err, "dump time triggers")
|
||||||
mqTriggers, err := client.MessageQueueTriggerList(messageQueue.NATS)
|
mqTriggers, err := client.MessageQueueTriggerList(fission.MessageQueueTypeNats)
|
||||||
checkErr(err, "dump message queue triggers")
|
checkErr(err, "dump message queue triggers")
|
||||||
|
|
||||||
tprResource := TPRResource{
|
tprResource := TPRResource{
|
||||||
|
|||||||
+19
-12
@@ -27,7 +27,6 @@ import (
|
|||||||
|
|
||||||
"github.com/fission/fission"
|
"github.com/fission/fission"
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
"github.com/fission/fission/mqtrigger/messageQueue"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func mqtCreate(c *cli.Context) error {
|
func mqtCreate(c *cli.Context) error {
|
||||||
@@ -42,14 +41,14 @@ func mqtCreate(c *cli.Context) error {
|
|||||||
fatal("Need a function name to create a trigger, use --function")
|
fatal("Need a function name to create a trigger, use --function")
|
||||||
}
|
}
|
||||||
|
|
||||||
mqType := c.String("mqtype")
|
var mqType fission.MessageQueueType
|
||||||
switch mqType {
|
switch c.String("mqtype") {
|
||||||
case "":
|
case "":
|
||||||
mqType = messageQueue.NATS
|
mqType = fission.MessageQueueTypeNats
|
||||||
case messageQueue.NATS:
|
case fission.MessageQueueTypeNats:
|
||||||
mqType = messageQueue.NATS
|
mqType = fission.MessageQueueTypeNats
|
||||||
case messageQueue.ASQ:
|
case fission.MessageQueueTypeASQ:
|
||||||
mqType = messageQueue.ASQ
|
mqType = fission.MessageQueueTypeASQ
|
||||||
default:
|
default:
|
||||||
fatal("Unknown message queue type, currently only \"nats-streaming, azure-storage-queue \" is supported")
|
fatal("Unknown message queue type, currently only \"nats-streaming, azure-storage-queue \" is supported")
|
||||||
}
|
}
|
||||||
@@ -74,7 +73,7 @@ func mqtCreate(c *cli.Context) error {
|
|||||||
|
|
||||||
checkMQTopicAvailability(mqType, topic, respTopic)
|
checkMQTopicAvailability(mqType, topic, respTopic)
|
||||||
|
|
||||||
mqt := crd.MessageQueueTrigger{
|
mqt := &crd.MessageQueueTrigger{
|
||||||
Metadata: metav1.ObjectMeta{
|
Metadata: metav1.ObjectMeta{
|
||||||
Name: mqtName,
|
Name: mqtName,
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@@ -91,7 +90,15 @@ func mqtCreate(c *cli.Context) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.MessageQueueTriggerCreate(&mqt)
|
// if we're writing a spec, don't call the API
|
||||||
|
if c.Bool("spec") {
|
||||||
|
specFile := fmt.Sprintf("mqtrigger-%v.yaml", mqtName)
|
||||||
|
err := specSave(*mqt, specFile)
|
||||||
|
checkErr(err, "create message queue trigger spec")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := client.MessageQueueTriggerCreate(mqt)
|
||||||
checkErr(err, "create message queue trigger")
|
checkErr(err, "create message queue trigger")
|
||||||
|
|
||||||
fmt.Printf("trigger '%s' created\n", mqtName)
|
fmt.Printf("trigger '%s' created\n", mqtName)
|
||||||
@@ -186,9 +193,9 @@ func mqtList(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkMQTopicAvailability(mqType string, topics ...string) {
|
func checkMQTopicAvailability(mqType fission.MessageQueueType, topics ...string) {
|
||||||
for _, t := range topics {
|
for _, t := range topics {
|
||||||
if len(t) > 0 && !messageQueue.IsTopicValid(mqType, t) {
|
if len(t) > 0 && !fission.IsTopicValid(mqType, t) {
|
||||||
fatal(fmt.Sprintf("Invalid topic for %s: %s", mqType, t))
|
fatal(fmt.Sprintf("Invalid topic for %s: %s", mqType, t))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -98,6 +98,10 @@ func pkgUpdate(c *cli.Context) error {
|
|||||||
deployArchiveName := c.String("deploy")
|
deployArchiveName := c.String("deploy")
|
||||||
buildcmd := c.String("buildcmd")
|
buildcmd := c.String("buildcmd")
|
||||||
|
|
||||||
|
if len(srcArchiveName) > 0 && len(deployArchiveName) > 0 {
|
||||||
|
fatal("Need either of --src or --deploy and not both arguments.")
|
||||||
|
}
|
||||||
|
|
||||||
if len(srcArchiveName) == 0 && len(deployArchiveName) == 0 &&
|
if len(srcArchiveName) == 0 && len(deployArchiveName) == 0 &&
|
||||||
len(envName) == 0 && len(buildcmd) == 0 {
|
len(envName) == 0 && len(buildcmd) == 0 {
|
||||||
fatal("Need --env or --src or --deploy or --buildcmd argument.")
|
fatal("Need --env or --src or --deploy or --buildcmd argument.")
|
||||||
@@ -158,9 +162,8 @@ func updatePackage(client *client.Client, pkg *crd.Package, envName,
|
|||||||
pkg.Spec.Deployment = *deployArchiveMetadata
|
pkg.Spec.Deployment = *deployArchiveMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set package as pending status only when there is no
|
// Set package as pending status when needToBuild is true
|
||||||
// deploy archive.
|
if needToBuild {
|
||||||
if needToBuild && len(pkg.Spec.Deployment.Type) == 0 {
|
|
||||||
// change into pending state to trigger package build
|
// change into pending state to trigger package build
|
||||||
pkg.Status = fission.PackageStatus{
|
pkg.Status = fission.PackageStatus{
|
||||||
BuildStatus: fission.BuildStatusPending,
|
BuildStatus: fission.BuildStatusPending,
|
||||||
|
|||||||
+17
-1
@@ -52,6 +52,8 @@ func runPortForward(kubeConfig string, labelSelector string, localPort string, f
|
|||||||
fatal(fmt.Sprintf("Failed to connect to Kubernetes: %s", err))
|
fatal(fmt.Sprintf("Failed to connect to Kubernetes: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verbose(2, "Connected to Kubernetes API")
|
||||||
|
|
||||||
// if fission namespace is unset, try to find a fission pod in any namespace
|
// if fission namespace is unset, try to find a fission pod in any namespace
|
||||||
if len(fissionNamespace) == 0 {
|
if len(fissionNamespace) == 0 {
|
||||||
fissionNamespace = meta_v1.NamespaceAll
|
fissionNamespace = meta_v1.NamespaceAll
|
||||||
@@ -93,6 +95,7 @@ func runPortForward(kubeConfig string, labelSelector string, localPort string, f
|
|||||||
for _, servicePort := range service.Spec.Ports {
|
for _, servicePort := range service.Spec.Ports {
|
||||||
targetPort = servicePort.TargetPort.String()
|
targetPort = servicePort.TargetPort.String()
|
||||||
}
|
}
|
||||||
|
verbose(2, "Connecting to port %v on pod %v/%v", targetPort, podNameSpace, podNameSpace)
|
||||||
|
|
||||||
stopChannel := make(chan struct{}, 1)
|
stopChannel := make(chan struct{}, 1)
|
||||||
readyChannel := make(chan struct{})
|
readyChannel := make(chan struct{})
|
||||||
@@ -113,12 +116,17 @@ func runPortForward(kubeConfig string, labelSelector string, localPort string, f
|
|||||||
fatal(msg)
|
fatal(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fw, err := portforward.New(dialer, ports, stopChannel, readyChannel, nil, os.Stderr)
|
outStream := os.Stdout
|
||||||
|
if verbosity < 2 {
|
||||||
|
outStream = nil
|
||||||
|
}
|
||||||
|
fw, err := portforward.New(dialer, ports, stopChannel, readyChannel, outStream, os.Stderr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("portforward.new errored out :%v", err.Error())
|
msg := fmt.Sprintf("portforward.new errored out :%v", err.Error())
|
||||||
fatal(msg)
|
fatal(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verbose(2, "Starting port forwarder")
|
||||||
return fw.ForwardPorts()
|
return fw.ForwardPorts()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,11 +136,15 @@ func runPortForward(kubeConfig string, labelSelector string, localPort string, f
|
|||||||
// its targetPort. Once the port forward is started, wait for it to
|
// its targetPort. Once the port forward is started, wait for it to
|
||||||
// start accepting connections before returning.
|
// start accepting connections before returning.
|
||||||
func setupPortForward(kubeConfig, namespace, labelSelector string) string {
|
func setupPortForward(kubeConfig, namespace, labelSelector string) string {
|
||||||
|
verbose(2, "Setting up port forward to %s in namespace %s using the kubeconfig at %s",
|
||||||
|
labelSelector, namespace, kubeConfig)
|
||||||
|
|
||||||
localPort, err := findFreePort()
|
localPort, err := findFreePort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(fmt.Sprintf("Error finding unused port :%v", err.Error()))
|
fatal(fmt.Sprintf("Error finding unused port :%v", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verbose(2, "Waiting for local port %v", localPort)
|
||||||
for {
|
for {
|
||||||
conn, _ := net.DialTimeout("tcp",
|
conn, _ := net.DialTimeout("tcp",
|
||||||
net.JoinHostPort("", localPort), time.Millisecond)
|
net.JoinHostPort("", localPort), time.Millisecond)
|
||||||
@@ -144,6 +156,7 @@ func setupPortForward(kubeConfig, namespace, labelSelector string) string {
|
|||||||
time.Sleep(time.Millisecond * 50)
|
time.Sleep(time.Millisecond * 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verbose(2, "Starting port forward from local port %v", localPort)
|
||||||
go func() {
|
go func() {
|
||||||
err := runPortForward(kubeConfig, labelSelector, localPort, namespace)
|
err := runPortForward(kubeConfig, labelSelector, localPort, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -151,6 +164,7 @@ func setupPortForward(kubeConfig, namespace, labelSelector string) string {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
verbose(2, "Waiting for port forward %v to start...", localPort)
|
||||||
for {
|
for {
|
||||||
conn, _ := net.DialTimeout("tcp",
|
conn, _ := net.DialTimeout("tcp",
|
||||||
net.JoinHostPort("", localPort), time.Millisecond)
|
net.JoinHostPort("", localPort), time.Millisecond)
|
||||||
@@ -161,5 +175,7 @@ func setupPortForward(kubeConfig, namespace, labelSelector string) string {
|
|||||||
time.Sleep(time.Millisecond * 50)
|
time.Sleep(time.Millisecond * 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verbose(2, "Port forward from local port %v started", localPort)
|
||||||
|
|
||||||
return localPort
|
return localPort
|
||||||
}
|
}
|
||||||
|
|||||||
+310
-37
@@ -29,6 +29,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
"github.com/mholt/archiver"
|
"github.com/mholt/archiver"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/satori/go.uuid"
|
"github.com/satori/go.uuid"
|
||||||
@@ -100,7 +101,7 @@ type (
|
|||||||
messageQueueTriggers []crd.MessageQueueTrigger
|
messageQueueTriggers []crd.MessageQueueTrigger
|
||||||
archiveUploadSpecs []ArchiveUploadSpec
|
archiveUploadSpecs []ArchiveUploadSpec
|
||||||
|
|
||||||
sourceMap SourceMap
|
sourceMap sourceMap
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceApplyStatus struct {
|
resourceApplyStatus struct {
|
||||||
@@ -109,13 +110,18 @@ type (
|
|||||||
deleted []*metav1.ObjectMeta
|
deleted []*metav1.ObjectMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceMap struct {
|
location struct {
|
||||||
// xxx
|
path string
|
||||||
|
line int
|
||||||
|
}
|
||||||
|
sourceMap struct {
|
||||||
|
// kind -> namespace -> name -> location
|
||||||
|
locations map[string](map[string](map[string]location))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSpecDir(c *cli.Context) string {
|
func getSpecDir(c *cli.Context) string {
|
||||||
specDir := c.String("specs")
|
specDir := c.String("specdir")
|
||||||
if len(specDir) == 0 {
|
if len(specDir) == 0 {
|
||||||
specDir = "specs"
|
specDir = "specs"
|
||||||
}
|
}
|
||||||
@@ -190,28 +196,205 @@ func specInit(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validateFunctionReference checks a function reference
|
||||||
|
func (fr *FissionResources) validateFunctionReference(functions map[string]bool, kind string, meta *metav1.ObjectMeta, funcRef fission.FunctionReference) error {
|
||||||
|
if funcRef.Type == fission.FunctionReferenceTypeFunctionName {
|
||||||
|
// triggers only reference functions in their own namespace
|
||||||
|
namespace := meta.Namespace
|
||||||
|
name := funcRef.Name
|
||||||
|
m := &metav1.ObjectMeta{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
if _, ok := functions[mapKey(m)]; !ok {
|
||||||
|
return fmt.Errorf("%v: %v '%v' references unknown function '%v'",
|
||||||
|
fr.sourceMap.locations[kind][meta.Namespace][meta.Name],
|
||||||
|
kind,
|
||||||
|
meta.Name,
|
||||||
|
name)
|
||||||
|
} else {
|
||||||
|
functions[mapKey(m)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// specValidate parses a set of specs and checks for references to
|
// specValidate parses a set of specs and checks for references to
|
||||||
// resources that don't exist.
|
// resources that don't exist.
|
||||||
func specValidate(c *cli.Context) error {
|
func specValidate(c *cli.Context) error {
|
||||||
//specDir := getSpecDir(c)
|
// this will error on parse errors and on duplicates
|
||||||
|
specDir := getSpecDir(c)
|
||||||
|
fr, err := readSpecs(specDir)
|
||||||
|
checkErr(err, "read specs")
|
||||||
|
|
||||||
// parse all specs
|
// this does the rest of the checks, like dangling refs
|
||||||
// verify references:
|
err = fr.validate()
|
||||||
// functions from triggers
|
if err != nil {
|
||||||
// packages from functions
|
fmt.Printf("Error validating specs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// find unreferenced uploads
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fr *FissionResources) validate() error {
|
||||||
|
var result *multierror.Error
|
||||||
|
|
||||||
|
// check references: both dangling refs + garbage
|
||||||
|
// packages -> archives
|
||||||
|
// functions -> packages
|
||||||
|
// functions -> environments [TODO]
|
||||||
|
// triggers -> functions
|
||||||
|
|
||||||
|
// index archives
|
||||||
|
archives := make(map[string]bool)
|
||||||
|
for _, a := range fr.archiveUploadSpecs {
|
||||||
|
archives[a.Name] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// index packages, check outgoing refs, mark archives that are referenced
|
||||||
|
packages := make(map[string]bool)
|
||||||
|
for _, p := range fr.packages {
|
||||||
|
packages[mapKey(&p.Metadata)] = false
|
||||||
|
|
||||||
|
// check archive refs from package
|
||||||
|
aname := strings.TrimPrefix(p.Spec.Source.URL, ARCHIVE_URL_PREFIX)
|
||||||
|
if len(aname) > 0 {
|
||||||
|
if _, ok := archives[aname]; !ok {
|
||||||
|
result = multierror.Append(result, fmt.Errorf(
|
||||||
|
"%v: package '%v' references unknown source archive %v%v",
|
||||||
|
fr.sourceMap.locations["Package"][p.Metadata.Namespace][p.Metadata.Name],
|
||||||
|
p.Metadata.Name,
|
||||||
|
ARCHIVE_URL_PREFIX,
|
||||||
|
aname))
|
||||||
|
} else {
|
||||||
|
archives[aname] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aname = strings.TrimPrefix(p.Spec.Deployment.URL, ARCHIVE_URL_PREFIX)
|
||||||
|
if len(aname) > 0 {
|
||||||
|
if _, ok := archives[aname]; !ok {
|
||||||
|
result = multierror.Append(result, fmt.Errorf(
|
||||||
|
"%v: package '%v' references unknown deployment archive %v%v",
|
||||||
|
fr.sourceMap.locations["Package"][p.Metadata.Namespace][p.Metadata.Name],
|
||||||
|
p.Metadata.Name,
|
||||||
|
ARCHIVE_URL_PREFIX,
|
||||||
|
aname))
|
||||||
|
} else {
|
||||||
|
archives[aname] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// error on unreferenced archives
|
||||||
|
for name, referenced := range archives {
|
||||||
|
if !referenced {
|
||||||
|
result = multierror.Append(result, fmt.Errorf(
|
||||||
|
"%v: archive '%v' is not used in any package",
|
||||||
|
fr.sourceMap.locations["ArchiveUploadSpec"][""][name],
|
||||||
|
name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// index functions, check function package refs, mark referenced packages
|
||||||
|
functions := make(map[string]bool)
|
||||||
|
for _, f := range fr.functions {
|
||||||
|
functions[mapKey(&f.Metadata)] = false
|
||||||
|
|
||||||
|
// check package ref from function
|
||||||
|
pkgMeta := &metav1.ObjectMeta{
|
||||||
|
Name: f.Spec.Package.PackageRef.Name,
|
||||||
|
Namespace: f.Spec.Package.PackageRef.Namespace,
|
||||||
|
}
|
||||||
|
if _, ok := packages[mapKey(pkgMeta)]; !ok {
|
||||||
|
result = multierror.Append(result, fmt.Errorf(
|
||||||
|
"%v: function '%v' references unknown package %v/%v",
|
||||||
|
fr.sourceMap.locations["Function"][f.Metadata.Namespace][f.Metadata.Name],
|
||||||
|
f.Metadata.Name,
|
||||||
|
pkgMeta.Namespace,
|
||||||
|
pkgMeta.Name))
|
||||||
|
} else {
|
||||||
|
packages[mapKey(pkgMeta)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// error on unreferenced packages
|
||||||
|
for key, referenced := range packages {
|
||||||
|
ks := strings.Split(key, ":")
|
||||||
|
namespace, name := ks[0], ks[1]
|
||||||
|
if !referenced {
|
||||||
|
result = multierror.Append(result, fmt.Errorf(
|
||||||
|
"%v: package '%v' is not used in any function",
|
||||||
|
fr.sourceMap.locations["Package"][namespace][name],
|
||||||
|
name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check function refs from triggers
|
||||||
|
for _, t := range fr.httpTriggers {
|
||||||
|
err := fr.validateFunctionReference(functions, t.Kind, &t.Metadata, t.Spec.FunctionReference)
|
||||||
|
if err != nil {
|
||||||
|
result = multierror.Append(result, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, t := range fr.kubernetesWatchTriggers {
|
||||||
|
err := fr.validateFunctionReference(functions, t.Kind, &t.Metadata, t.Spec.FunctionReference)
|
||||||
|
if err != nil {
|
||||||
|
result = multierror.Append(result, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, t := range fr.timeTriggers {
|
||||||
|
err := fr.validateFunctionReference(functions, t.Kind, &t.Metadata, t.Spec.FunctionReference)
|
||||||
|
if err != nil {
|
||||||
|
result = multierror.Append(result, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, t := range fr.messageQueueTriggers {
|
||||||
|
err := fr.validateFunctionReference(functions, t.Kind, &t.Metadata, t.Spec.FunctionReference)
|
||||||
|
if err != nil {
|
||||||
|
result = multierror.Append(result, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not error on unreferenced functions (you can call a function through workflows,
|
||||||
|
// `fission function test`, etc.)
|
||||||
|
|
||||||
|
// (ErrorOrNil returns nil if there were no errors appended.)
|
||||||
|
return result.ErrorOrNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (loc location) String() string {
|
||||||
|
return fmt.Sprintf("%v:%v", loc.path, loc.line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of source location of resources, and track duplicates
|
||||||
|
func (fr *FissionResources) trackSourceMap(kind string, newobj *metav1.ObjectMeta, loc *location) error {
|
||||||
|
if _, exists := fr.sourceMap.locations[kind]; !exists {
|
||||||
|
fr.sourceMap.locations[kind] = make(map[string](map[string]location))
|
||||||
|
}
|
||||||
|
if _, exists := fr.sourceMap.locations[kind][newobj.Namespace]; !exists {
|
||||||
|
fr.sourceMap.locations[kind][newobj.Namespace] = make(map[string]location)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for duplicate resources
|
||||||
|
oldloc, exists := fr.sourceMap.locations[kind][newobj.Namespace][newobj.Name]
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf("%v: Duplicate %v '%v', first defined in %v", loc, kind, newobj.Name, oldloc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// track new resource
|
||||||
|
fr.sourceMap.locations[kind][newobj.Namespace][newobj.Name] = *loc
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseYaml takes one yaml document, figures out its type, parses it, and puts it in
|
// parseYaml takes one yaml document, figures out its type, parses it, and puts it in
|
||||||
// the right list in the given fission resources set.
|
// the right list in the given fission resources set.
|
||||||
func parseYaml(path string, b []byte, fr *FissionResources) error {
|
func (fr *FissionResources) parseYaml(b []byte, loc *location) error {
|
||||||
|
var m *metav1.ObjectMeta
|
||||||
|
|
||||||
// Figure out the object type by unmarshaling into the TypeMeta struct; then
|
// Figure out the object type by unmarshaling into the TypeMeta struct; then
|
||||||
// unmarshal again into the "real" struct once we know the type. There's almost
|
// unmarshal again into the "real" struct once we know the type.
|
||||||
// certainly a better way to do this...
|
|
||||||
var tm TypeMeta
|
var tm TypeMeta
|
||||||
err := yaml.Unmarshal(b, &tm)
|
err := yaml.Unmarshal(b, &tm)
|
||||||
switch tm.Kind {
|
switch tm.Kind {
|
||||||
@@ -219,57 +402,63 @@ func parseYaml(path string, b []byte, fr *FissionResources) error {
|
|||||||
var v crd.Package
|
var v crd.Package
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
m = &v.Metadata
|
||||||
fr.packages = append(fr.packages, v)
|
fr.packages = append(fr.packages, v)
|
||||||
case "Function":
|
case "Function":
|
||||||
var v crd.Function
|
var v crd.Function
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
m = &v.Metadata
|
||||||
fr.functions = append(fr.functions, v)
|
fr.functions = append(fr.functions, v)
|
||||||
case "Environment":
|
case "Environment":
|
||||||
var v crd.Environment
|
var v crd.Environment
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
m = &v.Metadata
|
||||||
fr.environments = append(fr.environments, v)
|
fr.environments = append(fr.environments, v)
|
||||||
case "HTTPTrigger":
|
case "HTTPTrigger":
|
||||||
var v crd.HTTPTrigger
|
var v crd.HTTPTrigger
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO move to validator
|
||||||
|
if !strings.HasPrefix(v.Spec.RelativeURL, "/") {
|
||||||
|
v.Spec.RelativeURL = fmt.Sprintf("/%s", v.Spec.RelativeURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
m = &v.Metadata
|
||||||
fr.httpTriggers = append(fr.httpTriggers, v)
|
fr.httpTriggers = append(fr.httpTriggers, v)
|
||||||
case "KubernetesWatchTrigger":
|
case "KubernetesWatchTrigger":
|
||||||
var v crd.KubernetesWatchTrigger
|
var v crd.KubernetesWatchTrigger
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
m = &v.Metadata
|
||||||
fr.kubernetesWatchTriggers = append(fr.kubernetesWatchTriggers, v)
|
fr.kubernetesWatchTriggers = append(fr.kubernetesWatchTriggers, v)
|
||||||
case "TimeTrigger":
|
case "TimeTrigger":
|
||||||
var v crd.TimeTrigger
|
var v crd.TimeTrigger
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
m = &v.Metadata
|
||||||
fr.timeTriggers = append(fr.timeTriggers, v)
|
fr.timeTriggers = append(fr.timeTriggers, v)
|
||||||
case "MessageQueueTrigger":
|
case "MessageQueueTrigger":
|
||||||
var v crd.MessageQueueTrigger
|
var v crd.MessageQueueTrigger
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
m = &v.Metadata
|
||||||
fr.messageQueueTriggers = append(fr.messageQueueTriggers, v)
|
fr.messageQueueTriggers = append(fr.messageQueueTriggers, v)
|
||||||
|
|
||||||
// The following are not CRDs
|
// The following are not CRDs
|
||||||
@@ -278,22 +467,32 @@ func parseYaml(path string, b []byte, fr *FissionResources) error {
|
|||||||
var v DeploymentConfig
|
var v DeploymentConfig
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
fr.deploymentConfig = v
|
fr.deploymentConfig = v
|
||||||
case "ArchiveUploadSpec":
|
case "ArchiveUploadSpec":
|
||||||
var v ArchiveUploadSpec
|
var v ArchiveUploadSpec
|
||||||
err = yaml.Unmarshal(b, &v)
|
err = yaml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warn(fmt.Sprintf("Failed to parse %v in %v: %v", tm.Kind, path, err))
|
return errors.Wrap(err, fmt.Sprintf("Failed to parse %v in %v", tm.Kind, loc))
|
||||||
return err
|
}
|
||||||
|
m = &metav1.ObjectMeta{
|
||||||
|
Name: v.Name,
|
||||||
|
Namespace: "",
|
||||||
}
|
}
|
||||||
fr.archiveUploadSpecs = append(fr.archiveUploadSpecs, v)
|
fr.archiveUploadSpecs = append(fr.archiveUploadSpecs, v)
|
||||||
default:
|
default:
|
||||||
// no need to error out just because there's some extra files around;
|
// no need to error out just because there's some extra files around;
|
||||||
// also good for compatibility.
|
// also good for compatibility.
|
||||||
warn(fmt.Sprintf("Ignoring unknown type %v in %v", tm.Kind, path))
|
warn(fmt.Sprintf("Ignoring unknown type %v in %v", tm.Kind, loc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to source map, check for duplicates
|
||||||
|
if m != nil {
|
||||||
|
err = fr.trackSourceMap(tm.Kind, m, loc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -310,8 +509,14 @@ func readSpecs(specDir string) (*FissionResources, error) {
|
|||||||
kubernetesWatchTriggers: make([]crd.KubernetesWatchTrigger, 0),
|
kubernetesWatchTriggers: make([]crd.KubernetesWatchTrigger, 0),
|
||||||
timeTriggers: make([]crd.TimeTrigger, 0),
|
timeTriggers: make([]crd.TimeTrigger, 0),
|
||||||
messageQueueTriggers: make([]crd.MessageQueueTrigger, 0),
|
messageQueueTriggers: make([]crd.MessageQueueTrigger, 0),
|
||||||
|
|
||||||
|
sourceMap: sourceMap{
|
||||||
|
locations: make(map[string](map[string](map[string]location))),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var result *multierror.Error
|
||||||
|
|
||||||
// Users can organize the specdir into subdirs if they want to.
|
// Users can organize the specdir into subdirs if they want to.
|
||||||
err := filepath.Walk(specDir, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(specDir, func(path string, info os.FileInfo, err error) error {
|
||||||
// For now just read YAML files. We'll add jsonnet at some point. Skip
|
// For now just read YAML files. We'll add jsonnet at some point. Skip
|
||||||
@@ -322,26 +527,38 @@ func readSpecs(specDir string) (*FissionResources, error) {
|
|||||||
// read
|
// read
|
||||||
b, err := ioutil.ReadFile(path)
|
b, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
result = multierror.Append(result, err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
// handle the case where there are multiple YAML docs per file. go-yaml
|
// handle the case where there are multiple YAML docs per file. go-yaml
|
||||||
// doesn't support this directly, yet.
|
// doesn't support this directly, yet.
|
||||||
docs := bytes.Split(b, []byte("\n---"))
|
docs := bytes.Split(b, []byte("\n---"))
|
||||||
|
lines := 1
|
||||||
for _, doc := range docs {
|
for _, doc := range docs {
|
||||||
d := []byte(strings.TrimSpace(string(doc)))
|
d := []byte(strings.TrimSpace(string(doc)))
|
||||||
if len(d) != 0 {
|
if len(d) != 0 {
|
||||||
// parse this document and add whatever is in it to fr
|
// parse this document and add whatever is in it to fr
|
||||||
err = parseYaml(path, d, &fr)
|
err = fr.parseYaml(d, &location{
|
||||||
|
path: path,
|
||||||
|
line: lines,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
// collect all errors so user can fix them all
|
||||||
|
result = multierror.Append(result, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// the separator occupies one line, hence the +1
|
||||||
|
lines += strings.Count(string(doc), "\n") + 1
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := result.ErrorOrNil(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &fr, nil
|
return &fr, nil
|
||||||
}
|
}
|
||||||
@@ -419,6 +636,10 @@ func specApply(c *cli.Context) error {
|
|||||||
fr, err := readSpecs(specDir)
|
fr, err := readSpecs(specDir)
|
||||||
checkErr(err, "read specs")
|
checkErr(err, "read specs")
|
||||||
|
|
||||||
|
// validate
|
||||||
|
err = fr.validate()
|
||||||
|
checkErr(err, "validate specs")
|
||||||
|
|
||||||
// make changes to the cluster based on the specs
|
// make changes to the cluster based on the specs
|
||||||
pkgMetas, as, err := applyResources(fclient, specDir, fr, deleteResources)
|
pkgMetas, as, err := applyResources(fclient, specDir, fr, deleteResources)
|
||||||
checkErr(err, "apply specs")
|
checkErr(err, "apply specs")
|
||||||
@@ -787,6 +1008,27 @@ func hasDeploymentConfig(m *metav1.ObjectMeta, fr *FissionResources) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForPackageBuild(fclient *client.Client, pkg *crd.Package) (*crd.Package, error) {
|
||||||
|
start := time.Now()
|
||||||
|
for {
|
||||||
|
if pkg.Status.BuildStatus != fission.BuildStatusRunning {
|
||||||
|
return pkg, nil
|
||||||
|
}
|
||||||
|
if time.Since(start) > 5*time.Minute {
|
||||||
|
return nil, fmt.Errorf("Package %v has been building for a while. Giving up on waiting for it.", pkg.Metadata.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO watch instead
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
pkg, err = fclient.PackageGet(&pkg.Metadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func applyPackages(fclient *client.Client, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *resourceApplyStatus, error) {
|
func applyPackages(fclient *client.Client, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *resourceApplyStatus, error) {
|
||||||
// get list
|
// get list
|
||||||
allObjs, err := fclient.PackageList()
|
allObjs, err := fclient.PackageList()
|
||||||
@@ -843,9 +1085,20 @@ func applyPackages(fclient *client.Client, fr *FissionResources, delete bool) (m
|
|||||||
} else {
|
} else {
|
||||||
// update
|
// update
|
||||||
o.Metadata.ResourceVersion = existingObj.Metadata.ResourceVersion
|
o.Metadata.ResourceVersion = existingObj.Metadata.ResourceVersion
|
||||||
newmeta, err := fclient.PackageUpdate(&o)
|
|
||||||
|
// We may be racing against the package builder to update the
|
||||||
|
// package (a previous version might have been getting built). So,
|
||||||
|
// wait for the package to have a non-running build status.
|
||||||
|
pkg, err := waitForPackageBuild(fclient, &o)
|
||||||
|
if err != nil {
|
||||||
|
// log and ignore
|
||||||
|
fmt.Printf("Error waiting for package '%v' build, ignoring\n", o.Metadata.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
newmeta, err := fclient.PackageUpdate(pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
// TODO check for resourceVersion conflict errors and retry
|
||||||
}
|
}
|
||||||
ras.updated = append(ras.updated, newmeta)
|
ras.updated = append(ras.updated, newmeta)
|
||||||
// keep track of metadata in case we need to create a reference to it
|
// keep track of metadata in case we need to create a reference to it
|
||||||
@@ -1379,7 +1632,7 @@ func applyMessageQueueTriggers(fclient *client.Client, fr *FissionResources, del
|
|||||||
return metadataMap, &ras, nil
|
return metadataMap, &ras, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from `fission function create --spec`
|
// called from `fission * create --spec`
|
||||||
func specSave(resource interface{}, specFile string) error {
|
func specSave(resource interface{}, specFile string) error {
|
||||||
specDir := "specs"
|
specDir := "specs"
|
||||||
|
|
||||||
@@ -1403,6 +1656,26 @@ func specSave(resource interface{}, specFile string) error {
|
|||||||
typedres.TypeMeta.APIVersion = SPEC_API_VERSION
|
typedres.TypeMeta.APIVersion = SPEC_API_VERSION
|
||||||
typedres.TypeMeta.Kind = "Function"
|
typedres.TypeMeta.Kind = "Function"
|
||||||
data, err = yaml.Marshal(typedres)
|
data, err = yaml.Marshal(typedres)
|
||||||
|
case crd.Environment:
|
||||||
|
typedres.TypeMeta.APIVersion = SPEC_API_VERSION
|
||||||
|
typedres.TypeMeta.Kind = "Environment"
|
||||||
|
data, err = yaml.Marshal(typedres)
|
||||||
|
case crd.HTTPTrigger:
|
||||||
|
typedres.TypeMeta.APIVersion = SPEC_API_VERSION
|
||||||
|
typedres.TypeMeta.Kind = "HTTPTrigger"
|
||||||
|
data, err = yaml.Marshal(typedres)
|
||||||
|
case crd.KubernetesWatchTrigger:
|
||||||
|
typedres.TypeMeta.APIVersion = SPEC_API_VERSION
|
||||||
|
typedres.TypeMeta.Kind = "KubernetesWatchTrigger"
|
||||||
|
data, err = yaml.Marshal(typedres)
|
||||||
|
case crd.MessageQueueTrigger:
|
||||||
|
typedres.TypeMeta.APIVersion = SPEC_API_VERSION
|
||||||
|
typedres.TypeMeta.Kind = "MessageQueueTrigger"
|
||||||
|
data, err = yaml.Marshal(typedres)
|
||||||
|
case crd.TimeTrigger:
|
||||||
|
typedres.TypeMeta.APIVersion = SPEC_API_VERSION
|
||||||
|
typedres.TypeMeta.Kind = "TimeTrigger"
|
||||||
|
data, err = yaml.Marshal(typedres)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("can't save resource %#v", resource)
|
return fmt.Errorf("can't save resource %#v", resource)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,14 @@ func ttCreate(c *cli.Context) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we're writing a spec, don't call the API
|
||||||
|
if c.Bool("spec") {
|
||||||
|
specFile := fmt.Sprintf("timetrigger-%v.yaml", name)
|
||||||
|
err := specSave(*tt, specFile)
|
||||||
|
checkErr(err, "create time trigger spec")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err := client.TimeTriggerCreate(tt)
|
_, err := client.TimeTriggerCreate(tt)
|
||||||
checkErr(err, "create Time trigger")
|
checkErr(err, "create Time trigger")
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -360,7 +360,7 @@ func upgradeRestoreState(c *cli.Context) error {
|
|||||||
Metadata: *crdMetadataFromV1Metadata(&t.Metadata, v1state.NameChanges),
|
Metadata: *crdMetadataFromV1Metadata(&t.Metadata, v1state.NameChanges),
|
||||||
Spec: fission.MessageQueueTriggerSpec{
|
Spec: fission.MessageQueueTriggerSpec{
|
||||||
FunctionReference: *functionRefFromV1Metadata(&t.Function, v1state.NameChanges),
|
FunctionReference: *functionRefFromV1Metadata(&t.Function, v1state.NameChanges),
|
||||||
MessageQueueType: t.MessageQueueType,
|
MessageQueueType: fission.MessageQueueTypeNats, // only NATS is supported at that time (v1 types)
|
||||||
Topic: t.Topic,
|
Topic: t.Topic,
|
||||||
ResponseTopic: t.ResponseTopic,
|
ResponseTopic: t.ResponseTopic,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -77,6 +77,14 @@ func wCreate(c *cli.Context) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we're writing a spec, don't call the API
|
||||||
|
if c.Bool("spec") {
|
||||||
|
specFile := fmt.Sprintf("kubewatch-%v.yaml", watchName)
|
||||||
|
err := specSave(*w, specFile)
|
||||||
|
checkErr(err, "create kubernetes watch spec")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err := client.WatchCreate(w)
|
_, err := client.WatchCreate(w)
|
||||||
checkErr(err, "create watch")
|
checkErr(err, "create watch")
|
||||||
|
|
||||||
|
|||||||
Generated
+18
-13
@@ -1,5 +1,5 @@
|
|||||||
hash: 3b15246c5a7ca26271ef45e705c92351c786cfbf8ca1c093026b458e527ceefa
|
hash: d77d204547318863d07beea96b4e29a90f0d6c106a957dd0ba40eb0345a77e42
|
||||||
updated: 2018-02-10T19:04:05.240303+01:00
|
updated: 2018-03-23T17:49:04.748453691-07:00
|
||||||
imports:
|
imports:
|
||||||
- name: cloud.google.com/go
|
- name: cloud.google.com/go
|
||||||
version: 3b1ae45394a234c385be014e9a488f2bb6eef821
|
version: 3b1ae45394a234c385be014e9a488f2bb6eef821
|
||||||
@@ -18,7 +18,7 @@ imports:
|
|||||||
- autorest/azure
|
- autorest/azure
|
||||||
- autorest/date
|
- autorest/date
|
||||||
- name: github.com/coreos/etcd
|
- name: github.com/coreos/etcd
|
||||||
version: 9c6d93056575da4da94382f473b0ecfcd9c1443b
|
version: 6a265731e10a5137b991c1aa3a83ecefdd149d50
|
||||||
subpackages:
|
subpackages:
|
||||||
- client
|
- client
|
||||||
- name: github.com/davecgh/go-spew
|
- name: github.com/davecgh/go-spew
|
||||||
@@ -55,7 +55,7 @@ imports:
|
|||||||
- name: github.com/emicklei/go-restful-swagger12
|
- name: github.com/emicklei/go-restful-swagger12
|
||||||
version: dcef7f55730566d41eae5db10e7d6981829720f6
|
version: dcef7f55730566d41eae5db10e7d6981829720f6
|
||||||
- name: github.com/fsnotify/fsnotify
|
- name: github.com/fsnotify/fsnotify
|
||||||
version: c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9
|
version: 4da3e2cfbabc9f751898f250b49f2439785783a1
|
||||||
- name: github.com/ghodss/yaml
|
- name: github.com/ghodss/yaml
|
||||||
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
||||||
- name: github.com/go-openapi/analysis
|
- name: github.com/go-openapi/analysis
|
||||||
@@ -97,6 +97,10 @@ imports:
|
|||||||
version: abb68c488872b06c5453865fa59f4818b4ea13a4
|
version: abb68c488872b06c5453865fa59f4818b4ea13a4
|
||||||
subpackages:
|
subpackages:
|
||||||
- local
|
- local
|
||||||
|
- name: github.com/hashicorp/errwrap
|
||||||
|
version: 7554cd9344cec97297fa6649b055a8c98c2a1e55
|
||||||
|
- name: github.com/hashicorp/go-multierror
|
||||||
|
version: b7773ae218740a7be65057fc60b366a49b538a44
|
||||||
- name: github.com/hashicorp/golang-lru
|
- name: github.com/hashicorp/golang-lru
|
||||||
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
|
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -133,7 +137,7 @@ imports:
|
|||||||
subpackages:
|
subpackages:
|
||||||
- pb
|
- pb
|
||||||
- name: github.com/nats-io/nats-streaming-server
|
- name: github.com/nats-io/nats-streaming-server
|
||||||
version: 33414c6f2179201f7fda8743ba89d07184e3fa77
|
version: 7889f37a10062ff8eb9ac11855ed51cddeaf4469
|
||||||
subpackages:
|
subpackages:
|
||||||
- spb
|
- spb
|
||||||
- util
|
- util
|
||||||
@@ -148,7 +152,7 @@ imports:
|
|||||||
subpackages:
|
subpackages:
|
||||||
- xxHash32
|
- xxHash32
|
||||||
- name: github.com/pkg/errors
|
- name: github.com/pkg/errors
|
||||||
version: 30136e27e2ac8d167177e8a583aa4c3fea5be833
|
version: f15c970de5b76fac0b59abb32d62c17cc7bed265
|
||||||
- name: github.com/PuerkitoBio/purell
|
- name: github.com/PuerkitoBio/purell
|
||||||
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
|
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
|
||||||
- name: github.com/PuerkitoBio/urlesc
|
- name: github.com/PuerkitoBio/urlesc
|
||||||
@@ -237,7 +241,7 @@ imports:
|
|||||||
- name: k8s.io/api
|
- name: k8s.io/api
|
||||||
version: 4b8fc5be9b77d91bbb6525d18591c43699a2b4e5
|
version: 4b8fc5be9b77d91bbb6525d18591c43699a2b4e5
|
||||||
- name: k8s.io/apiextensions-apiserver
|
- name: k8s.io/apiextensions-apiserver
|
||||||
version: fcd622fe88a4a6efcb5aea9e94ee87324ac1b036
|
version: 19d3c0f1ccfb3e4180400ff7c22fe3c879771807
|
||||||
subpackages:
|
subpackages:
|
||||||
- pkg/apis/apiextensions
|
- pkg/apis/apiextensions
|
||||||
- pkg/apis/apiextensions/v1beta1
|
- pkg/apis/apiextensions/v1beta1
|
||||||
@@ -245,7 +249,7 @@ imports:
|
|||||||
- pkg/client/clientset/clientset/scheme
|
- pkg/client/clientset/clientset/scheme
|
||||||
- pkg/client/clientset/clientset/typed/apiextensions/v1beta1
|
- pkg/client/clientset/clientset/typed/apiextensions/v1beta1
|
||||||
- name: k8s.io/apimachinery
|
- name: k8s.io/apimachinery
|
||||||
version: 8ab5f3d8a330c2e9baaf84e39042db8d49034ae2
|
version: 208a6980b14bbb263f29482482eabdbcfff9f7bb
|
||||||
subpackages:
|
subpackages:
|
||||||
- pkg/api/equality
|
- pkg/api/equality
|
||||||
- pkg/api/errors
|
- pkg/api/errors
|
||||||
@@ -278,22 +282,22 @@ imports:
|
|||||||
- pkg/util/diff
|
- pkg/util/diff
|
||||||
- pkg/util/errors
|
- pkg/util/errors
|
||||||
- pkg/util/framer
|
- pkg/util/framer
|
||||||
|
- pkg/util/httpstream
|
||||||
|
- pkg/util/httpstream/spdy
|
||||||
- pkg/util/intstr
|
- pkg/util/intstr
|
||||||
- pkg/util/json
|
- pkg/util/json
|
||||||
- pkg/util/net
|
- pkg/util/net
|
||||||
- pkg/util/rand
|
- pkg/util/rand
|
||||||
|
- pkg/util/remotecommand
|
||||||
- pkg/util/runtime
|
- pkg/util/runtime
|
||||||
- pkg/util/sets
|
- pkg/util/sets
|
||||||
- pkg/util/validation
|
- pkg/util/validation
|
||||||
- pkg/util/validation/field
|
- pkg/util/validation/field
|
||||||
- pkg/util/wait
|
- pkg/util/wait
|
||||||
- pkg/util/yaml
|
- pkg/util/yaml
|
||||||
- pkg/util/httpstream
|
|
||||||
- pkg/util/httpstream/spdy
|
|
||||||
- pkg/util/remotecommand
|
|
||||||
- third_party/forked/golang/netutil
|
|
||||||
- pkg/version
|
- pkg/version
|
||||||
- pkg/watch
|
- pkg/watch
|
||||||
|
- third_party/forked/golang/netutil
|
||||||
- third_party/forked/golang/reflect
|
- third_party/forked/golang/reflect
|
||||||
- name: k8s.io/client-go
|
- name: k8s.io/client-go
|
||||||
version: d92e8497f71b7b4e0494e5bd204b48d34bd6f254
|
version: d92e8497f71b7b4e0494e5bd204b48d34bd6f254
|
||||||
@@ -374,11 +378,12 @@ imports:
|
|||||||
- tools/clientcmd/api
|
- tools/clientcmd/api
|
||||||
- tools/clientcmd/api/latest
|
- tools/clientcmd/api/latest
|
||||||
- tools/clientcmd/api/v1
|
- tools/clientcmd/api/v1
|
||||||
|
- tools/metrics
|
||||||
- tools/portforward
|
- tools/portforward
|
||||||
- tools/remotecommand
|
- tools/remotecommand
|
||||||
- tools/metrics
|
|
||||||
- transport
|
- transport
|
||||||
- util/cert
|
- util/cert
|
||||||
|
- util/exec
|
||||||
- util/flowcontrol
|
- util/flowcontrol
|
||||||
- util/homedir
|
- util/homedir
|
||||||
- util/integer
|
- util/integer
|
||||||
|
|||||||
@@ -60,3 +60,5 @@ import:
|
|||||||
version: ~1.1.0
|
version: ~1.1.0
|
||||||
- package: github.com/imdario/mergo
|
- package: github.com/imdario/mergo
|
||||||
version: ~0.3.2
|
version: ~0.3.2
|
||||||
|
- package: github.com/hashicorp/go-multierror
|
||||||
|
- package: github.com/hashicorp/errwrap
|
||||||
|
|||||||
+41
-17
@@ -19,6 +19,18 @@ Prerequisites
|
|||||||
|
|
||||||
4. Install changelog generator tool "github_changelog_generator": https://github.com/skywinder/github-changelog-generator
|
4. Install changelog generator tool "github_changelog_generator": https://github.com/skywinder/github-changelog-generator
|
||||||
|
|
||||||
|
5. Install realpath if you don't already have it : https://github.com/harto/realpath-osx
|
||||||
|
|
||||||
|
6. Install gothub if you don't already have it : https://github.com/itchio/gothub
|
||||||
|
You just have to go get github.com/itchio/gothub, that'll install gothub in $GOPATH/bin. Ensure this is already
|
||||||
|
in $PATH env variable.
|
||||||
|
|
||||||
|
|
||||||
|
Check that the build is green!
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Fix if not, etc.
|
||||||
|
|
||||||
|
|
||||||
Update versions
|
Update versions
|
||||||
---------------
|
---------------
|
||||||
@@ -33,6 +45,18 @@ Commit your changes, push them, open a PR, merge it.
|
|||||||
Update doc versions: Docs live at github.com/fission/docs.fission.io
|
Update doc versions: Docs live at github.com/fission/docs.fission.io
|
||||||
|
|
||||||
|
|
||||||
|
Update the docs versions
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Archive existing version into dist/
|
||||||
|
|
||||||
|
Change config.toml baseURL.
|
||||||
|
|
||||||
|
Change versions in links.
|
||||||
|
|
||||||
|
git add, git commit.
|
||||||
|
|
||||||
|
|
||||||
Make the release
|
Make the release
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
@@ -47,48 +71,48 @@ Make the release
|
|||||||
|
|
||||||
./hack/release.sh <VERSION>
|
./hack/release.sh <VERSION>
|
||||||
|
|
||||||
This script checks to make sure you're releasing from the master
|
This script checks to make sure you're releasing from the vX.Y.Z
|
||||||
branch, and that your repo is clean (no modified/staged files).
|
branch, and that your repo is clean (no modified/staged files).
|
||||||
|
|
||||||
This will take a while.
|
This will take a while.
|
||||||
|
|
||||||
3. Push docs update to github
|
3. Push docs update to github
|
||||||
|
|
||||||
4. Copy over charts into charts repo, update helm chart index, push to github
|
4. Copy over charts from build dir into charts repo [see below], update helm chart index, push to github
|
||||||
|
|
||||||
5. Manually test an installation, just for a sanity check
|
5. Manually test an installation, just for a sanity check
|
||||||
|
|
||||||
6. Switch the github release from pre-release to release.
|
6. Git commit and push Changelog.md into master branch.
|
||||||
|
|
||||||
|
7. Switch the github release from pre-release to release.
|
||||||
|
|
||||||
|
8. Add links from release notes:
|
||||||
|
* Install guide in docs
|
||||||
|
* Changelog.md
|
||||||
|
|
||||||
Update chart repo
|
Update chart repo
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Copy the helm charts over to the fission-charts repo.
|
Copy the helm charts over to the fission-charts repo
|
||||||
|
|
||||||
Run `index.sh` in that repo. (Don't edit the index.yaml in this repo
|
Run `index.sh` in that repo. (Don't edit the index.yaml in this repo
|
||||||
manually. index.sh generates it, using the helm repo CLI.)
|
manually. index.sh generates it, using the helm repo CLI.)
|
||||||
|
|
||||||
git add, git commit, and git push your changes.
|
git add, git commit, and git push your changes.
|
||||||
|
|
||||||
The chart repo is a github page, so once you push the master branch,
|
The chart repo is a github page : https://github.com/fission/fission-charts, so once you push the master branch,
|
||||||
you're all set.
|
you're all set.
|
||||||
|
|
||||||
|
Ask to be added to this repo if permissions are missing.
|
||||||
Update the docs website
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Rebuild the docs website using hugo.
|
|
||||||
|
|
||||||
Create a new folder named by version under fission.io/docs. Copy the
|
|
||||||
generated docs public folder into this folder.
|
|
||||||
|
|
||||||
Update the 'latest' symlink to point to the new version of the docs.
|
|
||||||
|
|
||||||
git add, git commit, git push.
|
|
||||||
|
|
||||||
|
|
||||||
Announce the release
|
Announce the release
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Announce the release on #contributors.
|
Announce the release on #contributors.
|
||||||
|
|
||||||
|
|
||||||
|
Update Fission.io
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Tell #marketing channel to update link
|
||||||
|
|||||||
+2
-2
@@ -18,8 +18,8 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
go test -v -i $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go')
|
go test -v -i $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go' | grep -v 'benchmark')
|
||||||
|
|
||||||
# The executor unit test only works with NodePort-type services for
|
# The executor unit test only works with NodePort-type services for
|
||||||
# now. So disable it for our travis ci tests.
|
# now. So disable it for our travis ci tests.
|
||||||
go test -v $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go' | grep -v executor)
|
go test -v $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go' | grep -v executor | grep -v 'benchmark')
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package kubewatcher
|
package kubewatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
"github.com/fission/fission/publisher"
|
"github.com/fission/fission/publisher"
|
||||||
)
|
)
|
||||||
@@ -26,6 +28,12 @@ func Start(routerUrl string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = fissionClient.WaitForCRDs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error waiting for CRDs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
poster := publisher.MakeWebhookPublisher(routerUrl)
|
poster := publisher.MakeWebhookPublisher(routerUrl)
|
||||||
kubeWatch := MakeKubeWatcher(kubeClient, poster)
|
kubeWatch := MakeKubeWatcher(kubeClient, poster)
|
||||||
MakeWatchSync(fissionClient, kubeWatch)
|
MakeWatchSync(fissionClient, kubeWatch)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
user "#{ENV['INFLUXDB_USERNAME']}"
|
user "#{ENV['INFLUXDB_USERNAME']}"
|
||||||
password "#{ENV['INFLUXDB_PASSWD']}"
|
password "#{ENV['INFLUXDB_PASSWD']}"
|
||||||
use_ssl false
|
use_ssl false
|
||||||
time_precision s
|
time_precision ns
|
||||||
tag_keys ["funcuid"]
|
tag_keys ["funcuid"]
|
||||||
sequence_tag _seq
|
sequence_tag _seq
|
||||||
buffer_type file
|
buffer_type file
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ func Start(routerUrl string) error {
|
|||||||
log.Fatalf("Failed to get fission client: %v", err)
|
log.Fatalf("Failed to get fission client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = fissionClient.WaitForCRDs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error waiting for CRDs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Message queue type: nats is the only supported one for now
|
// Message queue type: nats is the only supported one for now
|
||||||
mqType := os.Getenv("MESSAGE_QUEUE_TYPE")
|
mqType := os.Getenv("MESSAGE_QUEUE_TYPE")
|
||||||
mqUrl := os.Getenv("MESSAGE_QUEUE_URL")
|
mqUrl := os.Getenv("MESSAGE_QUEUE_URL")
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (m *azureHTTPClientMock) Do(req *http.Request) (*http.Response, error) {
|
|||||||
|
|
||||||
func TestNewStorageConnectionMissingAccountName(t *testing.T) {
|
func TestNewStorageConnectionMissingAccountName(t *testing.T) {
|
||||||
connection, err := newAzureStorageConnection(DummyRouterURL, MessageQueueConfig{
|
connection, err := newAzureStorageConnection(DummyRouterURL, MessageQueueConfig{
|
||||||
MQType: ASQ,
|
MQType: fission.MessageQueueTypeASQ,
|
||||||
Url: "",
|
Url: "",
|
||||||
})
|
})
|
||||||
require.Nil(t, connection)
|
require.Nil(t, connection)
|
||||||
@@ -114,7 +114,7 @@ func TestNewStorageConnectionMissingAccountName(t *testing.T) {
|
|||||||
func TestNewStorageConnectionMissingAccessKey(t *testing.T) {
|
func TestNewStorageConnectionMissingAccessKey(t *testing.T) {
|
||||||
_ = os.Setenv("AZURE_STORAGE_ACCOUNT_NAME", "accountname")
|
_ = os.Setenv("AZURE_STORAGE_ACCOUNT_NAME", "accountname")
|
||||||
connection, err := newAzureStorageConnection(DummyRouterURL, MessageQueueConfig{
|
connection, err := newAzureStorageConnection(DummyRouterURL, MessageQueueConfig{
|
||||||
MQType: ASQ,
|
MQType: fission.MessageQueueTypeASQ,
|
||||||
Url: "",
|
Url: "",
|
||||||
})
|
})
|
||||||
_ = os.Unsetenv("AZURE_STORAGE_ACCOUNT_NAME")
|
_ = os.Unsetenv("AZURE_STORAGE_ACCOUNT_NAME")
|
||||||
@@ -291,7 +291,7 @@ func TestAzureStorageQueuePoisonMessage(t *testing.T) {
|
|||||||
Type: fission.FunctionReferenceTypeFunctionName,
|
Type: fission.FunctionReferenceTypeFunctionName,
|
||||||
Name: FunctionName,
|
Name: FunctionName,
|
||||||
},
|
},
|
||||||
MessageQueueType: ASQ,
|
MessageQueueType: fission.MessageQueueTypeASQ,
|
||||||
Topic: QueueName,
|
Topic: QueueName,
|
||||||
ContentType: ContentType,
|
ContentType: ContentType,
|
||||||
},
|
},
|
||||||
@@ -434,7 +434,7 @@ func runAzureStorageQueueTest(t *testing.T, count int, output bool) {
|
|||||||
Type: fission.FunctionReferenceTypeFunctionName,
|
Type: fission.FunctionReferenceTypeFunctionName,
|
||||||
Name: FunctionName,
|
Name: FunctionName,
|
||||||
},
|
},
|
||||||
MessageQueueType: ASQ,
|
MessageQueueType: fission.MessageQueueTypeASQ,
|
||||||
Topic: QueueName,
|
Topic: QueueName,
|
||||||
ResponseTopic: responseTopic,
|
ResponseTopic: responseTopic,
|
||||||
ContentType: ContentType,
|
ContentType: ContentType,
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package messageQueue
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -28,21 +27,12 @@ import (
|
|||||||
"github.com/fission/fission/crd"
|
"github.com/fission/fission/crd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
NATS string = "nats-streaming"
|
|
||||||
ASQ string = "azure-storage-queue"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ADD_TRIGGER requestType = iota
|
ADD_TRIGGER requestType = iota
|
||||||
DELETE_TRIGGER
|
DELETE_TRIGGER
|
||||||
GET_ALL_TRIGGERS
|
GET_ALL_TRIGGERS
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
validAzureQueueName = regexp.MustCompile("^[a-z0-9][a-z0-9\\-]*[a-z0-9]$")
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
messageQueueSubscription interface{}
|
messageQueueSubscription interface{}
|
||||||
|
|
||||||
@@ -92,9 +82,9 @@ func MakeMessageQueueTriggerManager(fissionClient *crd.FissionClient, routerUrl
|
|||||||
fissionClient: fissionClient,
|
fissionClient: fissionClient,
|
||||||
}
|
}
|
||||||
switch mqConfig.MQType {
|
switch mqConfig.MQType {
|
||||||
case NATS:
|
case fission.MessageQueueTypeNats:
|
||||||
messageQueue, err = makeNatsMessageQueue(routerUrl, mqConfig)
|
messageQueue, err = makeNatsMessageQueue(routerUrl, mqConfig)
|
||||||
case ASQ:
|
case fission.MessageQueueTypeASQ:
|
||||||
messageQueue, err = newAzureStorageConnection(routerUrl, mqConfig)
|
messageQueue, err = newAzureStorageConnection(routerUrl, mqConfig)
|
||||||
default:
|
default:
|
||||||
err = errors.New("No matched message queue type found")
|
err = errors.New("No matched message queue type found")
|
||||||
@@ -231,13 +221,3 @@ func (mqt *MessageQueueTriggerManager) syncTriggers() {
|
|||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsTopicValid(mqType string, topic string) bool {
|
|
||||||
switch mqType {
|
|
||||||
case NATS:
|
|
||||||
return isTopicValidForNats(topic)
|
|
||||||
case ASQ:
|
|
||||||
return len(topic) >= 3 && len(topic) <= 63 && validAzureQueueName.MatchString(topic)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
+123
-79
@@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/fission/fission"
|
||||||
executorClient "github.com/fission/fission/executor/client"
|
executorClient "github.com/fission/fission/executor/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,45 +38,138 @@ type functionHandler struct {
|
|||||||
function *metav1.ObjectMeta
|
function *metav1.ObjectMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fh *functionHandler) getServiceForFunction() (*url.URL, error) {
|
|
||||||
// call executor, get a url for a function
|
|
||||||
svcName, err := fh.executor.GetServiceForFunction(fh.function)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
svcUrl, err := url.Parse(fmt.Sprintf("http://%v", svcName))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return svcUrl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A layer on top of http.DefaultTransport, with retries.
|
// A layer on top of http.DefaultTransport, with retries.
|
||||||
type RetryingRoundTripper struct {
|
type RetryingRoundTripper struct {
|
||||||
maxRetries int
|
maxRetries int
|
||||||
initalTimeout time.Duration
|
initialTimeout time.Duration
|
||||||
|
funcHandler *functionHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rrt RetryingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
// RoundTrip is a custom transport with retries for http requests that forwards the request to the right serviceUrl, obtained
|
||||||
timeout := rrt.initalTimeout
|
// from router's cache or from executor if router entry is stale.
|
||||||
|
//
|
||||||
|
// It first checks if the service address for this function came from router's cache.
|
||||||
|
// If it didn't, it makes a request to executor to get a new service for function. If that succeeds, it adds the address
|
||||||
|
// to it's cache and makes a request to that address with transport.RoundTrip call.
|
||||||
|
// Initial requests to new k8s services sometimes seem to fail, but retries work. So, it retries with an exponential
|
||||||
|
// back-off for maxRetries times.
|
||||||
|
//
|
||||||
|
// Else if it came from the cache, it makes a transport.RoundTrip with that cached address. If the response received is
|
||||||
|
// a network dial error (which means that the pod doesn't exist anymore), it removes the cache entry and makes a request
|
||||||
|
// to executor to get a new service for function. It then retries transport.RoundTrip with the new address.
|
||||||
|
//
|
||||||
|
// At any point in time, if the response received from transport.RoundTrip is other than dial network error, it is
|
||||||
|
// relayed as-is to the user, without any retries.
|
||||||
|
//
|
||||||
|
// While this RoundTripper handles the case where a previously cached address of the function pod isn't valid anymore
|
||||||
|
// (probably because the pod got deleted somehow), by making a request to executor to get a new service for this function,
|
||||||
|
// it doesn't handle a case where a newly specialized pod gets deleted just after the GetServiceForFunction succeeds.
|
||||||
|
// In such a case, the RoundTripper will retry requests against the new address and give up after maxRetries.
|
||||||
|
// However, the subsequent http call for this function will ensure the cache is invalidated.
|
||||||
|
//
|
||||||
|
// If GetServiceForFunction returns an error or if RoundTripper exits with an error, it get's translated into 502
|
||||||
|
// inside ServeHttp function of the reverseProxy.
|
||||||
|
// Earlier, GetServiceForFunction was called inside handler function and fission explicitly set http status code to 500
|
||||||
|
// if it returned an error.
|
||||||
|
func (roundTripper RetryingRoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||||
|
var needExecutor, serviceUrlFromExecutor bool
|
||||||
|
var serviceUrl *url.URL
|
||||||
|
|
||||||
|
// set the timeout for transport context
|
||||||
|
timeout := roundTripper.initialTimeout
|
||||||
transport := http.DefaultTransport.(*http.Transport)
|
transport := http.DefaultTransport.(*http.Transport)
|
||||||
|
|
||||||
// Do max-1 retries; the last one uses default transport timeouts
|
// cache lookup to get serviceUrl
|
||||||
for i := rrt.maxRetries - 1; i > 0; i-- {
|
serviceUrl, err = roundTripper.funcHandler.fmap.lookup(roundTripper.funcHandler.function)
|
||||||
// update timeout in transport
|
if err != nil || serviceUrl == nil {
|
||||||
|
// cache miss or nil entry in cache
|
||||||
|
needExecutor = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < roundTripper.maxRetries-1; i++ {
|
||||||
|
if needExecutor {
|
||||||
|
log.Printf("Calling getServiceForFunction for function: %s", roundTripper.funcHandler.function.Name)
|
||||||
|
|
||||||
|
// send a request to executor to specialize a new pod
|
||||||
|
service, err := roundTripper.funcHandler.executor.GetServiceForFunction(
|
||||||
|
roundTripper.funcHandler.function)
|
||||||
|
if err != nil {
|
||||||
|
// We might want a specific error code or header for fission failures as opposed to
|
||||||
|
// user function bugs.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the address into url
|
||||||
|
serviceUrl, err = url.Parse(fmt.Sprintf("http://%v", service))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the address in router's cache
|
||||||
|
roundTripper.funcHandler.fmap.assign(roundTripper.funcHandler.function, serviceUrl)
|
||||||
|
|
||||||
|
// flag denotes that service was not obtained from cache, instead, created just now by executor
|
||||||
|
serviceUrlFromExecutor = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// modify the request to reflect the service url
|
||||||
|
// this service url may have come from the cache lookup or from executor response
|
||||||
|
req.URL.Scheme = serviceUrl.Scheme
|
||||||
|
req.URL.Host = serviceUrl.Host
|
||||||
|
|
||||||
|
// To keep the function run container simple, it
|
||||||
|
// doesn't do any routing. In the future if we have
|
||||||
|
// multiple functions per container, we could use the
|
||||||
|
// function metadata here.
|
||||||
|
// leave the query string intact (req.URL.RawQuery)
|
||||||
|
req.URL.Path = "/"
|
||||||
|
|
||||||
|
// Overwrite request host with internal host,
|
||||||
|
// or request will be blocked in some situations
|
||||||
|
// (e.g. istio-proxy)
|
||||||
|
req.Host = serviceUrl.Host
|
||||||
|
|
||||||
|
// over-riding default settings.
|
||||||
transport.DialContext = (&net.Dialer{
|
transport.DialContext = (&net.Dialer{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
KeepAlive: 30 * time.Second,
|
KeepAlive: 30 * time.Second,
|
||||||
}).DialContext
|
}).DialContext
|
||||||
|
|
||||||
resp, err := transport.RoundTrip(req)
|
// forward the request to the function service
|
||||||
|
resp, err = transport.RoundTrip(req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
// if transport.RoundTrip succeeds and it was a cached entry, then tapService
|
||||||
|
if !serviceUrlFromExecutor {
|
||||||
|
go roundTripper.funcHandler.tapService(serviceUrl)
|
||||||
|
}
|
||||||
|
// return response back to user
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout *= time.Duration(2)
|
// if transport.RoundTrip returns a non-network dial error, then relay it back to user
|
||||||
log.Printf("Retrying request to %v in %v", req.URL.Host, timeout)
|
if !fission.IsNetworkDialError(err) {
|
||||||
time.Sleep(timeout)
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// means its a newly created service and it returned a network dial error.
|
||||||
|
// just retry after backing off for timeout period.
|
||||||
|
if serviceUrlFromExecutor {
|
||||||
|
log.Printf("request to %s errored out. backing off for %v before retrying",
|
||||||
|
req.URL.Host, timeout)
|
||||||
|
timeout *= time.Duration(2)
|
||||||
|
time.Sleep(timeout)
|
||||||
|
needExecutor = false
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// if transport.RoundTrip returns a network dial error and serviceUrl was from cache,
|
||||||
|
// it means, the entry in router cache is stale, so invalidate it.
|
||||||
|
// also set needExecutor to true so a new service can be requested for function.
|
||||||
|
log.Printf("request to %s errored out. removing function : %s from router's cache "+
|
||||||
|
"and requesting a new service for function",
|
||||||
|
req.URL.Host, roundTripper.funcHandler.function.Name)
|
||||||
|
roundTripper.funcHandler.fmap.remove(roundTripper.funcHandler.function)
|
||||||
|
needExecutor = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally, one more retry with the default timeout
|
// finally, one more retry with the default timeout
|
||||||
@@ -90,8 +184,6 @@ func (fh *functionHandler) tapService(serviceUrl *url.URL) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *http.Request) {
|
func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
reqStartTime := time.Now()
|
|
||||||
|
|
||||||
// retrieve url params and add them to request header
|
// retrieve url params and add them to request header
|
||||||
vars := mux.Vars(request)
|
vars := mux.Vars(request)
|
||||||
for k, v := range vars {
|
for k, v := range vars {
|
||||||
@@ -101,71 +193,23 @@ func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *
|
|||||||
// System Params
|
// System Params
|
||||||
MetadataToHeaders(HEADERS_FISSION_FUNCTION_PREFIX, fh.function, request)
|
MetadataToHeaders(HEADERS_FISSION_FUNCTION_PREFIX, fh.function, request)
|
||||||
|
|
||||||
// cache lookup
|
|
||||||
serviceUrl, err := fh.fmap.lookup(fh.function)
|
|
||||||
if err != nil {
|
|
||||||
// Cache miss: request the Pool Manager to make a new service.
|
|
||||||
log.Printf("Not cached, getting new service for %v", fh.function)
|
|
||||||
|
|
||||||
var poolErr error
|
|
||||||
serviceUrl, poolErr = fh.getServiceForFunction()
|
|
||||||
if poolErr != nil {
|
|
||||||
log.Printf("Failed to get service for function %v: %v", fh.function.Name, poolErr)
|
|
||||||
// We might want a specific error code or header for fission
|
|
||||||
// failures as opposed to user function bugs.
|
|
||||||
http.Error(responseWriter, "Internal server error (fission)", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// add it to the map
|
|
||||||
fh.fmap.assign(fh.function, serviceUrl)
|
|
||||||
} else {
|
|
||||||
// if we're using our cache, asynchronously tell
|
|
||||||
// executor we're using this service
|
|
||||||
go fh.tapService(serviceUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proxy off our request to the serviceUrl, and send the response back.
|
|
||||||
// TODO: As an optimization we may want to cache proxies too -- this might get us
|
// TODO: As an optimization we may want to cache proxies too -- this might get us
|
||||||
// connection reuse and possibly better performance
|
// connection reuse and possibly better performance
|
||||||
director := func(req *http.Request) {
|
director := func(req *http.Request) {
|
||||||
log.Printf("Proxying request for %v to %v", req.URL, serviceUrl.Host)
|
|
||||||
|
|
||||||
// send this request to serviceurl
|
|
||||||
req.URL.Scheme = serviceUrl.Scheme
|
|
||||||
req.URL.Host = serviceUrl.Host
|
|
||||||
|
|
||||||
// To keep the function run container simple, it
|
|
||||||
// doesn't do any routing. In the future if we have
|
|
||||||
// multiple functions per container, we could use the
|
|
||||||
// function metadata here.
|
|
||||||
req.URL.Path = "/"
|
|
||||||
|
|
||||||
// Overwrite request host with internal host,
|
|
||||||
// or request will be blocked in some situations
|
|
||||||
// (e.g. istio-proxy)
|
|
||||||
req.Host = serviceUrl.Host
|
|
||||||
|
|
||||||
// leave the query string intact (req.URL.RawQuery)
|
|
||||||
|
|
||||||
if _, ok := req.Header["User-Agent"]; !ok {
|
if _, ok := req.Header["User-Agent"]; !ok {
|
||||||
// explicitly disable User-Agent so it's not set to default value
|
// explicitly disable User-Agent so it's not set to default value
|
||||||
req.Header.Set("User-Agent", "")
|
req.Header.Set("User-Agent", "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial requests to new k8s services sometimes seem to
|
|
||||||
// fail, but retries work. So use a transport that does retries.
|
|
||||||
proxy := &httputil.ReverseProxy{
|
proxy := &httputil.ReverseProxy{
|
||||||
Director: director,
|
Director: director,
|
||||||
Transport: RetryingRoundTripper{
|
Transport: &RetryingRoundTripper{
|
||||||
maxRetries: 10,
|
initialTimeout: 50 * time.Millisecond,
|
||||||
initalTimeout: 50 * time.Millisecond,
|
maxRetries: 10,
|
||||||
|
funcHandler: fh,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
delay := time.Since(reqStartTime)
|
|
||||||
if delay > 100*time.Millisecond {
|
|
||||||
log.Printf("Request delay for %v: %v", serviceUrl, delay)
|
|
||||||
}
|
|
||||||
proxy.ServeHTTP(responseWriter, request)
|
proxy.ServeHTTP(responseWriter, request)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ func TestFunctionProxying(t *testing.T) {
|
|||||||
fmap := makeFunctionServiceMap(0)
|
fmap := makeFunctionServiceMap(0)
|
||||||
fmap.assign(fn, backendURL)
|
fmap.assign(fn, backendURL)
|
||||||
|
|
||||||
fh := &functionHandler{fmap: fmap, function: fn}
|
fh := &functionHandler{fmap: fmap,
|
||||||
|
function: fn,
|
||||||
|
}
|
||||||
functionHandlerServer := httptest.NewServer(http.HandlerFunc(fh.handler))
|
functionHandlerServer := httptest.NewServer(http.HandlerFunc(fh.handler))
|
||||||
fhURL := functionHandlerServer.URL
|
fhURL := functionHandlerServer.URL
|
||||||
|
|
||||||
|
|||||||
@@ -75,3 +75,8 @@ func (fmap *functionServiceMap) assign(f *metav1.ObjectMeta, serviceUrl *url.URL
|
|||||||
// ignore error
|
// ignore error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fmap *functionServiceMap) remove(f *metav1.ObjectMeta) error {
|
||||||
|
mk := keyFromMetadata(f)
|
||||||
|
return fmap.cache.Delete(*mk)
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ func Start(port int, executorUrl string) {
|
|||||||
log.Fatalf("Error connecting to kubernetes API: %v", err)
|
log.Fatalf("Error connecting to kubernetes API: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = fissionClient.WaitForCRDs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error waiting for CRDs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
restClient := fissionClient.GetCrdClient()
|
restClient := fissionClient.GetCrdClient()
|
||||||
|
|
||||||
executor := executorClient.MakeClient(executorUrl)
|
executor := executorClient.MakeClient(executorUrl)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
FROM golang:1.10.1 AS go-builder
|
||||||
|
WORKDIR /go
|
||||||
|
RUN go get github.com/wcharczuk/go-chart
|
||||||
|
COPY picasso.go /go
|
||||||
|
RUN CGO_ENABLE=0 GOOS=linux GOARCH=amd64 go build -o picasso .
|
||||||
|
|
||||||
|
FROM loadimpact/k6
|
||||||
|
WORKDIR /fission-bench
|
||||||
|
COPY --from=go-builder /go/picasso /usr/local/bin/picasso
|
||||||
|
RUN apk --update add --no-cache bash curl
|
||||||
|
RUN curl -Lo fission https://github.com/fission/fission/releases/download/$(curl --silent "https://api.github.com/repos/fission/fission/releases/latest" | grep "tag_name" |sed -E 's/.*"([^"]+)".*/\1/')/fission-cli-linux && chmod +x fission && mv fission /usr/local/bin/
|
||||||
|
|
||||||
|
ENTRYPOINT ["sh"]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
def main():
|
||||||
|
return "Hello, world!\n"
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/wcharczuk/go-chart"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
title *string
|
||||||
|
file *string
|
||||||
|
outputFile *string
|
||||||
|
outputFormat *string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PNG = "png"
|
||||||
|
SVG = "svg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Tags struct {
|
||||||
|
Group string `json:"group"`
|
||||||
|
Iter string `json:"iter"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Proto string `json:"proto"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Vu string `json:"vu"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Data struct {
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
Tags Tags `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
MetricPoint struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Data Data `json:"data"`
|
||||||
|
Metric string `json:"metric"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
title = flag.String("title", "Fission Benchmark", "Chart title")
|
||||||
|
file = flag.String("file", "", "Metric json file")
|
||||||
|
outputFile = flag.String("o", "chart.png", "Output file name")
|
||||||
|
outputFormat = flag.String("format", PNG, "Format of output file (png or svg)")
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateContinuousSeries(file string) chart.Series {
|
||||||
|
f, err := os.OpenFile(file, os.O_RDONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to open file metric point: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(f)
|
||||||
|
|
||||||
|
var points []*MetricPoint
|
||||||
|
var xVals []float64
|
||||||
|
var yVals []float64
|
||||||
|
|
||||||
|
var initTime *time.Time
|
||||||
|
|
||||||
|
for {
|
||||||
|
l, _, err := reader.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
point := &MetricPoint{}
|
||||||
|
err = json.Unmarshal(l, point)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to parse metric point: %v -> %v", err, string(l))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if point.Type != "Point" || point.Metric != "http_req_duration" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
points = append(points, point)
|
||||||
|
|
||||||
|
if initTime == nil {
|
||||||
|
initTime = &point.Data.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
timeSinceStart := point.Data.Time.Sub(*initTime).Seconds()
|
||||||
|
xVals = append(xVals, timeSinceStart)
|
||||||
|
yVals = append(yVals, point.Data.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chart.ContinuousSeries{
|
||||||
|
Style: chart.Style{
|
||||||
|
Show: true,
|
||||||
|
StrokeColor: chart.GetDefaultColor(0).WithAlpha(64),
|
||||||
|
FillColor: chart.GetDefaultColor(0).WithAlpha(64),
|
||||||
|
},
|
||||||
|
XValues: xVals,
|
||||||
|
YValues: yVals,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateChart(title string, file string, format chart.RendererProvider, series []chart.Series) error {
|
||||||
|
if series == nil {
|
||||||
|
return errors.New("Series cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := chart.ConcatSeries(series)
|
||||||
|
|
||||||
|
graph := chart.Chart{
|
||||||
|
Title: title,
|
||||||
|
TitleStyle: chart.StyleShow(),
|
||||||
|
Background: chart.Style{
|
||||||
|
Padding: chart.Box{
|
||||||
|
Top: 50,
|
||||||
|
Left: 25,
|
||||||
|
Right: 25,
|
||||||
|
Bottom: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
XAxis: chart.XAxis{
|
||||||
|
Name: "Time (s)",
|
||||||
|
NameStyle: chart.StyleShow(),
|
||||||
|
Style: chart.StyleShow(),
|
||||||
|
Range: &chart.ContinuousRange{
|
||||||
|
Min: 0,
|
||||||
|
},
|
||||||
|
ValueFormatter: func(v interface{}) string {
|
||||||
|
return fmt.Sprintf("%.2f s", v.(float64))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
YAxis: chart.YAxis{
|
||||||
|
Name: "Response Time (ms)",
|
||||||
|
NameStyle: chart.StyleShow(),
|
||||||
|
Style: chart.StyleShow(),
|
||||||
|
Range: &chart.ContinuousRange{
|
||||||
|
Min: 0,
|
||||||
|
},
|
||||||
|
ValueFormatter: func(v interface{}) string {
|
||||||
|
return fmt.Sprintf("%d ms", int(v.(float64)))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Series: cs,
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := bytes.NewBuffer([]byte{})
|
||||||
|
err := graph.Render(format, buffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(file, buffer.Bytes(), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listJsonFiles(path string) ([]string, error) {
|
||||||
|
fi, err := os.Stat(*file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.Mode().IsRegular() {
|
||||||
|
return []string{path}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var files []string
|
||||||
|
|
||||||
|
err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(path, ".json") && !info.IsDir() {
|
||||||
|
files = append(files, path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
if file == nil || len(*file) == 0 {
|
||||||
|
fmt.Println("Please provide metric json file name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var format chart.RendererProvider
|
||||||
|
|
||||||
|
if outputFormat == nil {
|
||||||
|
format = chart.PNG
|
||||||
|
} else {
|
||||||
|
switch strings.ToLower(*outputFormat) {
|
||||||
|
case PNG:
|
||||||
|
format = chart.PNG
|
||||||
|
case SVG:
|
||||||
|
format = chart.SVG
|
||||||
|
default:
|
||||||
|
fmt.Println("Unknown format, use png as output format")
|
||||||
|
*outputFormat = PNG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(*outputFile) == 0 {
|
||||||
|
fmt.Println("Please output chart png file name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var series []chart.Series
|
||||||
|
|
||||||
|
files, err := listJsonFiles(*file)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to get file information: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
series = append(series, generateContinuousSeries(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = generateChart(*title, *outputFile, format, series)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to generate chart: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+88
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT=$(dirname $0)/../../../..
|
||||||
|
|
||||||
|
for executorType in poolmgr newdeploy
|
||||||
|
do
|
||||||
|
dirName="burst-load-executor-${executorType}"
|
||||||
|
|
||||||
|
# remove old data
|
||||||
|
rm -rf ${dirName}
|
||||||
|
mkdir ${dirName}
|
||||||
|
pushd ${dirName}
|
||||||
|
|
||||||
|
# run multiple iterations to reduce impact of imbalance of pod distribution.
|
||||||
|
for iteration in {1..10}
|
||||||
|
do
|
||||||
|
|
||||||
|
# Create a hello world function in nodejs, test it with an http trigger
|
||||||
|
echo "Pre-test cleanup"
|
||||||
|
fission env delete --name python || true
|
||||||
|
|
||||||
|
echo "Creating python env"
|
||||||
|
# Use short grace period time to speed up resource recycle time
|
||||||
|
# Use high min/max CPU so that K8S will distribute pod in different nodes
|
||||||
|
fission env create --name python --version 2 --image fission/python-env --period 5 --mincpu 300 --maxcpu 300 --minmemory 256 --maxmemory 256
|
||||||
|
trap "fission env delete --name python" EXIT
|
||||||
|
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
fn=python-hello-$(date +%s)
|
||||||
|
|
||||||
|
echo "Creating package"
|
||||||
|
rm -rf pkg.zip pkg/ || true
|
||||||
|
mkdir pkg
|
||||||
|
cp ../../../assets/hello.py pkg/hello.py
|
||||||
|
|
||||||
|
zip -jr pkg.zip pkg/
|
||||||
|
pkgName=$(fission pkg create --env python --deploy pkg.zip | cut -d' ' -f 2 | cut -d"'" -f 2)
|
||||||
|
|
||||||
|
echo "Creating function"
|
||||||
|
fission fn create --name $fn --env python --pkg ${pkgName} --entrypoint "hello.main" --executortype ${executorType} --minscale 3 --maxscale 3
|
||||||
|
|
||||||
|
echo "Creating route"
|
||||||
|
fission route create --function $fn --url /$fn --method GET
|
||||||
|
|
||||||
|
echo "Waiting for router to catch up"
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
fnEndpoint="http://$FISSION_ROUTER/$fn"
|
||||||
|
js="sample.js"
|
||||||
|
rawFile="raw-${iteration}.json"
|
||||||
|
rawUsageReport="raw-usage.txt"
|
||||||
|
|
||||||
|
# Stage 1: 15s, 10 vus
|
||||||
|
# Stage 2: 45s, 500 vus
|
||||||
|
# Please check sample.js for more detail.
|
||||||
|
k6 run \
|
||||||
|
-e FN_ENDPOINT="${fnEndpoint}" \
|
||||||
|
--no-connection-reuse \
|
||||||
|
--out json="${rawFile}" \
|
||||||
|
--summary-trend-stats="avg,min,med,max,p(5),p(10),p(15),p(20),p(25),p(30),p(35),p(40),p(45),p(50),p(55),p(60),p(65),p(70),p(75),p(80),p(85),p(90),p(95),p(100)" \
|
||||||
|
../${js} >> ${rawUsageReport}
|
||||||
|
|
||||||
|
echo "Clean up"
|
||||||
|
fission fn delete --name ${fn}
|
||||||
|
fission env delete --name python
|
||||||
|
fission route list| grep ${fn}| awk '{print $1}'| xargs fission route delete --name
|
||||||
|
fission pkg delete --name ${pkgName}
|
||||||
|
rm -rf pkg.zip pkg
|
||||||
|
|
||||||
|
kubectl -n fission-function get deploy -o name|xargs -I@ bash -c "kubectl -n fission-function delete @" || true
|
||||||
|
kubectl -n fission-function get pod -o name|xargs -I@ bash -c "kubectl -n fission-function delete @" || true
|
||||||
|
|
||||||
|
echo "All done."
|
||||||
|
done
|
||||||
|
|
||||||
|
usageReport="usage.txt"
|
||||||
|
cat ${rawUsageReport}| grep "http_req_duration"| cut -f2 -d':' > ${usageReport}
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
|
# generate report after iterations are over
|
||||||
|
outImage="${dirName}.png"
|
||||||
|
picasso -file ${dirName} -format png -o ${outImage}
|
||||||
|
|
||||||
|
done
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import http from "k6/http";
|
||||||
|
import { check } from "k6";
|
||||||
|
|
||||||
|
export let options = {
|
||||||
|
stages: [
|
||||||
|
{ duration: "15s", target: 10 },
|
||||||
|
{ duration: "45s", target: 500 },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
let params = { timeout: 30 }
|
||||||
|
let res = http.get(`${__ENV.FN_ENDPOINT}`)
|
||||||
|
check(res, {
|
||||||
|
"status is 200": (r) => r.status === 200
|
||||||
|
});
|
||||||
|
};
|
||||||
+93
@@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT=$(dirname $0)/../../../..
|
||||||
|
|
||||||
|
for executorType in poolmgr newdeploy
|
||||||
|
do
|
||||||
|
for concurrency in 100 250 500 750 1000
|
||||||
|
do
|
||||||
|
|
||||||
|
testDuration="60"
|
||||||
|
dirName="concurrency-${concurrency}-executor-${executorType}"
|
||||||
|
|
||||||
|
# remove old data
|
||||||
|
rm -rf ${dirName}
|
||||||
|
mkdir ${dirName}
|
||||||
|
pushd ${dirName}
|
||||||
|
|
||||||
|
# run multiple iterations to reduce impact of imbalance of pod distribution.
|
||||||
|
for iteration in {1..10}
|
||||||
|
do
|
||||||
|
|
||||||
|
# Create a hello world function in nodejs, test it with an http trigger
|
||||||
|
echo "Pre-test cleanup"
|
||||||
|
fission env delete --name python || true
|
||||||
|
|
||||||
|
echo "Creating python env"
|
||||||
|
# Use short grace period time to speed up resource recycle time
|
||||||
|
# Use high min/max CPU so that K8S will distribute pod in different nodes
|
||||||
|
fission env create --name python --version 2 --image fission/python-env --period 5 --mincpu 300 --maxcpu 300 --minmemory 256 --maxmemory 256
|
||||||
|
trap "fission env delete --name python" EXIT
|
||||||
|
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
fn=python-hello-$(date +%s)
|
||||||
|
|
||||||
|
echo "Creating package"
|
||||||
|
rm -rf pkg.zip pkg/ || true
|
||||||
|
mkdir pkg
|
||||||
|
cp ../../../assets/hello.py pkg/hello.py
|
||||||
|
|
||||||
|
zip -jr pkg.zip pkg/
|
||||||
|
pkgName=$(fission pkg create --env python --deploy pkg.zip | cut -d' ' -f 2 | cut -d"'" -f 2)
|
||||||
|
|
||||||
|
echo "Creating function"
|
||||||
|
fission fn create --name $fn --env python --pkg ${pkgName} --entrypoint "hello.main" --executortype ${executorType} --minscale 3 --maxscale 3
|
||||||
|
|
||||||
|
echo "Creating route"
|
||||||
|
fission route create --function $fn --url /$fn --method GET
|
||||||
|
|
||||||
|
echo "Waiting for router to catch up"
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
fnEndpoint="http://$FISSION_ROUTER/$fn"
|
||||||
|
js="sample.js"
|
||||||
|
rawFile="raw-${iteration}.json"
|
||||||
|
rawUsageReport="raw-usage.txt"
|
||||||
|
|
||||||
|
k6 run \
|
||||||
|
-e FN_ENDPOINT="${fnEndpoint}" \
|
||||||
|
--duration "${testDuration}s" \
|
||||||
|
--rps ${concurrency} \
|
||||||
|
--vus ${concurrency} \
|
||||||
|
--no-connection-reuse \
|
||||||
|
--out json="${rawFile}" \
|
||||||
|
--summary-trend-stats="avg,min,med,max,p(5),p(10),p(15),p(20),p(25),p(30),p(35),p(40),p(45),p(50),p(55),p(60),p(65),p(70),p(75),p(80),p(85),p(90),p(95),p(100)" \
|
||||||
|
../${js} >> ${rawUsageReport}
|
||||||
|
|
||||||
|
echo "Clean up"
|
||||||
|
fission fn delete --name ${fn}
|
||||||
|
fission env delete --name python
|
||||||
|
fission route list| grep ${fn}| awk '{print $1}'| xargs fission route delete --name
|
||||||
|
fission pkg delete --name ${pkgName}
|
||||||
|
rm -rf pkg.zip pkg
|
||||||
|
|
||||||
|
kubectl -n fission-function get deploy -o name|xargs -I@ bash -c "kubectl -n fission-function delete @" || true
|
||||||
|
kubectl -n fission-function get pod -o name|xargs -I@ bash -c "kubectl -n fission-function delete @" || true
|
||||||
|
|
||||||
|
echo "All done."
|
||||||
|
done
|
||||||
|
|
||||||
|
usageReport="usage.txt"
|
||||||
|
cat ${rawUsageReport}| grep "http_req_duration"| cut -f2 -d':' > ${usageReport}
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
|
# generate report after iterations are over
|
||||||
|
outImage="${dirName}.png"
|
||||||
|
picasso -file ${dirName} -format png -o ${outImage}
|
||||||
|
|
||||||
|
done
|
||||||
|
done
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import http from "k6/http";
|
||||||
|
import { check } from "k6";
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
let params = { timeout: 30 }
|
||||||
|
let res = http.get(`${__ENV.FN_ENDPOINT}`)
|
||||||
|
check(res, {
|
||||||
|
"status is 200": (r) => r.status === 200
|
||||||
|
});
|
||||||
|
};
|
||||||
Executable
+117
@@ -0,0 +1,117 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT=$(dirname $0)/../../../..
|
||||||
|
|
||||||
|
for executorType in poolmgr newdeploy
|
||||||
|
do
|
||||||
|
for packagesize in 0 1 5 10 15 20
|
||||||
|
do
|
||||||
|
|
||||||
|
testDuration="5"
|
||||||
|
dirName="package-size-${packagesize}-executor-${executorType}"
|
||||||
|
|
||||||
|
# remove old data
|
||||||
|
rm -rf ${dirName}
|
||||||
|
mkdir ${dirName}
|
||||||
|
pushd ${dirName}
|
||||||
|
|
||||||
|
# run multiple iterations to reduce impact of imbalance of pod distribution.
|
||||||
|
for iteration in {1..10}
|
||||||
|
do
|
||||||
|
|
||||||
|
# Create a hello world function in nodejs, test it with an http trigger
|
||||||
|
echo "Pre-test cleanup"
|
||||||
|
fission env delete --name python || true
|
||||||
|
|
||||||
|
echo "Creating python env"
|
||||||
|
# Use short grace period time to speed up resource recycle time
|
||||||
|
# Use high min/max CPU so that K8S will distribute pod in different nodes
|
||||||
|
|
||||||
|
version=2
|
||||||
|
|
||||||
|
if [[ "${packagesize}" == "0" ]]
|
||||||
|
then
|
||||||
|
version=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
fission env create --name python --version ${version} --image fission/python-env --period 5 --mincpu 300 --maxcpu 300 --minmemory 256 --maxmemory 256
|
||||||
|
|
||||||
|
trap "fission env delete --name python" EXIT
|
||||||
|
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
fn=python-hello-$(date +%s)
|
||||||
|
|
||||||
|
pkgName=""
|
||||||
|
|
||||||
|
if [[ "${packagesize}" == "0" ]]
|
||||||
|
then
|
||||||
|
echo "Creating function"
|
||||||
|
fission fn create --name $fn --env python --code ../../../assets/hello.py --executortype ${executorType} --minscale 3 --maxscale 3
|
||||||
|
else
|
||||||
|
echo "Creating package"
|
||||||
|
rm -rf pkg.zip pkg/ || true
|
||||||
|
mkdir pkg
|
||||||
|
cp ../../../assets/hello.py pkg/hello.py
|
||||||
|
|
||||||
|
# Create empty file with give size to simulate different size of package
|
||||||
|
truncate -s ${packagesize}MiB pkg/foo
|
||||||
|
|
||||||
|
zip -jr pkg.zip pkg/
|
||||||
|
pkgName=$(fission pkg create --env python --deploy pkg.zip | cut -d' ' -f 2 | cut -d"'" -f 2)
|
||||||
|
|
||||||
|
echo "Creating function"
|
||||||
|
fission fn create --name $fn --env python --pkg ${pkgName} --entrypoint "hello.main" --executortype ${executorType} --minscale 3 --maxscale 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Creating route"
|
||||||
|
fission route create --function $fn --url /$fn --method GET
|
||||||
|
|
||||||
|
echo "Waiting for router to catch up"
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
fnEndpoint="http://$FISSION_ROUTER/$fn"
|
||||||
|
js="sample.js"
|
||||||
|
rawFile="raw-${iteration}.json"
|
||||||
|
rawUsageReport="raw-usage.txt"
|
||||||
|
|
||||||
|
k6 run \
|
||||||
|
-e FN_ENDPOINT="${fnEndpoint}" \
|
||||||
|
--duration "${testDuration}s" \
|
||||||
|
--rps 1 \
|
||||||
|
--vus 1 \
|
||||||
|
--no-connection-reuse \
|
||||||
|
--out json="${rawFile}" \
|
||||||
|
--summary-trend-stats="avg,min,med,max,p(5),p(10),p(15),p(20),p(25),p(30),p(35),p(40),p(45),p(50),p(55),p(60),p(65),p(70),p(75),p(80),p(85),p(90),p(95),p(100)" \
|
||||||
|
../${js} >> ${rawUsageReport}
|
||||||
|
|
||||||
|
echo "Clean up"
|
||||||
|
fission fn delete --name ${fn}
|
||||||
|
fission env delete --name python
|
||||||
|
fission route list| grep ${fn}| awk '{print $1}'| xargs fission route delete --name
|
||||||
|
|
||||||
|
if [[ ! -z "${pkgName}" ]]
|
||||||
|
then
|
||||||
|
fission pkg delete --name ${pkgName} || true
|
||||||
|
rm -rf pkg.zip pkg
|
||||||
|
fi
|
||||||
|
|
||||||
|
kubectl -n fission-function get deploy -o name|xargs -I@ bash -c "kubectl -n fission-function delete @" || true
|
||||||
|
kubectl -n fission-function get pod -o name|xargs -I@ bash -c "kubectl -n fission-function delete @" || true
|
||||||
|
|
||||||
|
echo "All done."
|
||||||
|
done
|
||||||
|
|
||||||
|
usageReport="usage.txt"
|
||||||
|
cat ${rawUsageReport}| grep "http_req_duration"| cut -f2 -d':' > ${usageReport}
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
|
# generate report after iterations are over
|
||||||
|
outImage="${dirName}.png"
|
||||||
|
picasso -file ${dirName} -format png -o ${outImage}
|
||||||
|
|
||||||
|
done
|
||||||
|
done
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import http from "k6/http";
|
||||||
|
import { check } from "k6";
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
let params = { timeout: 30 }
|
||||||
|
let res = http.get(`${__ENV.FN_ENDPOINT}`)
|
||||||
|
check(res, {
|
||||||
|
"status is 200": (r) => r.status === 200
|
||||||
|
});
|
||||||
|
};
|
||||||
+10
-2
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Unbound variables cause failure, so this readable if block instead of Parameter Expansion
|
||||||
|
if [[ ${TRAVIS_EVENT_TYPE+NOVALUE} == "cronNOVALUE" ]]
|
||||||
|
then
|
||||||
|
echo "Skipping build & test, this is cron job for fission upgrade tests"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f ${HOME}/.kube/config ]
|
if [ ! -f ${HOME}/.kube/config ]
|
||||||
then
|
then
|
||||||
echo "Skipping end to end tests, no cluster credentials"
|
echo "Skipping end to end tests, no cluster credentials"
|
||||||
@@ -17,7 +24,8 @@ FLUENTD_IMAGE=gcr.io/fission-ci/fluentd
|
|||||||
BUILDER_IMAGE=$REPO/builder
|
BUILDER_IMAGE=$REPO/builder
|
||||||
TAG=test
|
TAG=test
|
||||||
PRUNE_INTERVAL=1 # this variable controls the interval to run archivePruner. The unit is in minutes.
|
PRUNE_INTERVAL=1 # this variable controls the interval to run archivePruner. The unit is in minutes.
|
||||||
ROUTER_SERVICE_TYPE=ClusterIP
|
ROUTER_SERVICE_TYPE=LoadBalancer
|
||||||
|
SERVICE_TYPE=LoadBalancer
|
||||||
|
|
||||||
dump_system_info
|
dump_system_info
|
||||||
|
|
||||||
@@ -37,4 +45,4 @@ build_and_push_fluentd $FLUENTD_IMAGE:$TAG
|
|||||||
|
|
||||||
build_fission_cli
|
build_fission_cli
|
||||||
|
|
||||||
install_and_test $IMAGE $TAG $FETCHER_IMAGE $TAG $FLUENTD_IMAGE $TAG $PRUNE_INTERVAL $ROUTER_SERVICE_TYPE
|
install_and_test $IMAGE $TAG $FETCHER_IMAGE $TAG $FLUENTD_IMAGE $TAG $PRUNE_INTERVAL $ROUTER_SERVICE_TYPE $SERVICE_TYPE
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
clusterID="fissionMQTrigger"
|
|
||||||
topic="foo.bar"
|
|
||||||
resptopic="foo.foo"
|
|
||||||
expectedRespOutput="[foo.foo]: 'Hello, World!'"
|
|
||||||
FISSIONDIR=$GOPATH"/src/github.com/fission/fission"
|
|
||||||
|
|
||||||
if [[ -z $NATS_STREAMING_URL ]]; then
|
|
||||||
echo "'NATS_STREAMING_URL' must not be empty. For example: export NATS_STREAMING_URL=nats://192.168.0.1:4222"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z $FISSION_URL ]]; then
|
|
||||||
echo "'FISSION_URL' must not be empty. For example: export FISSION_URL=http://10.10.10.10"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd $FISSIONDIR"/fission/"
|
|
||||||
go build
|
|
||||||
mv fission $FISSIONDIR"/test/mqtrigger"
|
|
||||||
cd $FISSIONDIR"/test/mqtrigger"
|
|
||||||
|
|
||||||
./fission env create --name nodejs --image fission/node-env
|
|
||||||
./fission fn create --name hello1 --env nodejs --code main.js --method GET
|
|
||||||
./fission route create --method GET --url /h1 --function hello1
|
|
||||||
./fission mqtrigger create --name h1 --function hello1 --mqtype "nats-streaming" --topic "foo.bar" --resptopic "foo.foo"
|
|
||||||
|
|
||||||
# wait until nats trigger is created
|
|
||||||
sleep 5
|
|
||||||
|
|
||||||
go run ./stan-pub.go -s $NATS_STREAMING_URL -c $clusterID -id clientPub $topic "" || exit 1
|
|
||||||
|
|
||||||
response=$(go run ./stan-sub.go --last -s $NATS_STREAMING_URL -c $clusterID -id clientSub $resptopic 2>&1)
|
|
||||||
|
|
||||||
if [[ "$response" != "$expectedRespOutput" ]]; then
|
|
||||||
echo "$response is not equal to $expectedRespOutput"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Subscriber received expected response: $response"
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
+67
-10
@@ -148,6 +148,18 @@ clean_tpr_crd_resources() {
|
|||||||
kubectl --namespace default get crd| grep -v NAME| grep "fission.io"| awk '{print $1}'|xargs -I@ bash -c "kubectl --namespace default delete crd @" || true
|
kubectl --namespace default get crd| grep -v NAME| grep "fission.io"| awk '{print $1}'|xargs -I@ bash -c "kubectl --namespace default delete crd @" || true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_environment() {
|
||||||
|
id=$1
|
||||||
|
ns=f-$id
|
||||||
|
|
||||||
|
export FISSION_URL=http://$(kubectl -n $ns get svc controller -o jsonpath='{...ip}')
|
||||||
|
export FISSION_ROUTER=$(kubectl -n $ns get svc router -o jsonpath='{...ip}')
|
||||||
|
export FISSION_NATS_STREAMING_URL="http://defaultFissionAuthToken@$(kubectl -n $ns get svc nats-streaming -o jsonpath='{...ip}:{.spec.ports[0].port}')"
|
||||||
|
|
||||||
|
# set path to include cli
|
||||||
|
export PATH=$ROOT/fission:$PATH
|
||||||
|
}
|
||||||
|
|
||||||
generate_test_id() {
|
generate_test_id() {
|
||||||
echo $(date|md5sum|cut -c1-6)
|
echo $(date|md5sum|cut -c1-6)
|
||||||
}
|
}
|
||||||
@@ -161,14 +173,15 @@ helm_install_fission() {
|
|||||||
controllerNodeport=$6
|
controllerNodeport=$6
|
||||||
routerNodeport=$7
|
routerNodeport=$7
|
||||||
fluentdImage=$8
|
fluentdImage=$8
|
||||||
fluentdImageTag=$9
|
fluentdImageTag=${9}
|
||||||
pruneInterval="${10}"
|
pruneInterval="${10}"
|
||||||
routerServiceType=${11}
|
routerServiceType=${11}
|
||||||
|
serviceType=${12}
|
||||||
|
|
||||||
ns=f-$id
|
ns=f-$id
|
||||||
fns=f-func-$id
|
fns=f-func-$id
|
||||||
|
|
||||||
helmVars=image=$image,imageTag=$imageTag,fetcherImage=$fetcherImage,fetcherImageTag=$fetcherImageTag,functionNamespace=$fns,controllerPort=$controllerNodeport,routerPort=$routerNodeport,pullPolicy=Always,analytics=false,logger.fluentdImage=$fluentdImage,logger.fluentdImageTag=$fluentdImageTag,pruneInterval=$pruneInterval,routerServiceType=$routerServiceType
|
helmVars=image=$image,imageTag=$imageTag,fetcherImage=$fetcherImage,fetcherImageTag=$fetcherImageTag,functionNamespace=$fns,controllerPort=$controllerNodeport,routerPort=$routerNodeport,pullPolicy=Always,analytics=false,logger.fluentdImage=$fluentdImage,logger.fluentdImageTag=$fluentdImageTag,pruneInterval=$pruneInterval,routerServiceType=$routerServiceType,serviceType=$serviceType
|
||||||
|
|
||||||
timeout 30 bash -c "helm_setup"
|
timeout 30 bash -c "helm_setup"
|
||||||
|
|
||||||
@@ -215,6 +228,32 @@ dump_tiller_logs() {
|
|||||||
}
|
}
|
||||||
export -f dump_tiller_logs
|
export -f dump_tiller_logs
|
||||||
|
|
||||||
|
wait_for_service() {
|
||||||
|
id=$1
|
||||||
|
svc=$2
|
||||||
|
|
||||||
|
ns=f-$id
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
ip=$(kubectl -n $ns get svc $svc -o jsonpath='{...ip}')
|
||||||
|
if [ ! -z $ip ]
|
||||||
|
then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo Waiting for service $svc...
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_services() {
|
||||||
|
id=$1
|
||||||
|
|
||||||
|
wait_for_service $id controller
|
||||||
|
wait_for_service $id router
|
||||||
|
|
||||||
|
echo Waiting for service is routable...
|
||||||
|
sleep 10
|
||||||
|
}
|
||||||
|
|
||||||
helm_uninstall_fission() {(set +e
|
helm_uninstall_fission() {(set +e
|
||||||
id=$1
|
id=$1
|
||||||
@@ -234,17 +273,31 @@ export -f helm_uninstall_fission
|
|||||||
port_forward_services() {
|
port_forward_services() {
|
||||||
id=$1
|
id=$1
|
||||||
ns=f-$id
|
ns=f-$id
|
||||||
port=8888
|
svc=$2
|
||||||
|
port=$3
|
||||||
|
|
||||||
kubectl get pods -l svc="router" -o name --namespace $ns | \
|
kubectl get pods -l svc="$svc" -o name --namespace $ns | \
|
||||||
sed 's/^.*\///' | \
|
sed 's/^.*\///' | \
|
||||||
xargs -I{} kubectl port-forward {} $port:$port -n $ns &
|
xargs -I{} kubectl port-forward {} $port:$port -n $ns &
|
||||||
|
|
||||||
export FISSION_ROUTER="127.0.0.1:"
|
|
||||||
FISSION_ROUTER+="$port"
|
|
||||||
export PATH=$ROOT/fission:$PATH
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_service() {
|
||||||
|
id=$1
|
||||||
|
svc=$2
|
||||||
|
|
||||||
|
ns=f-$id
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
ip=$(kubectl -n $ns get svc $svc -o jsonpath='{...ip}')
|
||||||
|
if [ ! -z $ip ]
|
||||||
|
then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo Waiting for service $svc...
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
dump_builder_pod_logs() {
|
dump_builder_pod_logs() {
|
||||||
bns=$1
|
bns=$1
|
||||||
builderPods=$(kubectl -n $bns get pod -o name)
|
builderPods=$(kubectl -n $bns get pod -o name)
|
||||||
@@ -367,6 +420,8 @@ dump_logs() {
|
|||||||
dump_fission_logs $ns $fns buildermgr
|
dump_fission_logs $ns $fns buildermgr
|
||||||
dump_fission_logs $ns $fns executor
|
dump_fission_logs $ns $fns executor
|
||||||
dump_fission_logs $ns $fns storagesvc
|
dump_fission_logs $ns $fns storagesvc
|
||||||
|
dump_fission_logs $ns $fns mqtrigger
|
||||||
|
dump_fission_logs $ns $fns nats-streaming
|
||||||
dump_function_pod_logs $ns $fns
|
dump_function_pod_logs $ns $fns
|
||||||
dump_builder_pod_logs $bns
|
dump_builder_pod_logs $bns
|
||||||
dump_fission_crds
|
dump_fission_crds
|
||||||
@@ -428,6 +483,7 @@ install_and_test() {
|
|||||||
fluentdImageTag=$6
|
fluentdImageTag=$6
|
||||||
pruneInterval=$7
|
pruneInterval=$7
|
||||||
routerServiceType=$8
|
routerServiceType=$8
|
||||||
|
serviceType=$9
|
||||||
|
|
||||||
controllerPort=31234
|
controllerPort=31234
|
||||||
routerPort=31235
|
routerPort=31235
|
||||||
@@ -436,7 +492,7 @@ install_and_test() {
|
|||||||
|
|
||||||
id=$(generate_test_id)
|
id=$(generate_test_id)
|
||||||
trap "helm_uninstall_fission $id" EXIT
|
trap "helm_uninstall_fission $id" EXIT
|
||||||
helm_install_fission $id $image $imageTag $fetcherImage $fetcherImageTag $controllerPort $routerPort $fluentdImage $fluentdImageTag $pruneInterval $routerServiceType
|
helm_install_fission $id $image $imageTag $fetcherImage $fetcherImageTag $controllerPort $routerPort $fluentdImage $fluentdImageTag $pruneInterval $routerServiceType $serviceType
|
||||||
helm status $id | grep STATUS | grep -i deployed
|
helm status $id | grep STATUS | grep -i deployed
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
describe_all_pods $id
|
describe_all_pods $id
|
||||||
@@ -445,7 +501,8 @@ install_and_test() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
port_forward_services $id $routerPort
|
wait_for_services $id
|
||||||
|
set_environment $id
|
||||||
run_all_tests $id
|
run_all_tests $id
|
||||||
|
|
||||||
dump_logs $id
|
dump_logs $id
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user