Compare commits

...
62 Commits
Author SHA1 Message Date
Ta-Ching ChenandGitHub 6a3e326f36 Add benchmark script (#666) 2018-05-05 12:42:16 +08:00
smruthi2187andTa-Ching Chen f0dc2554a1 Meaningful error message when fetch request is received for a package when build is not successful. (#661) 2018-05-03 22:51:48 +08:00
VishalandTa-Ching Chen 2ece0db13f Fixed the issue with update wiping original values & added resources in listing, fixes #654 (#663) 2018-05-02 22:29:08 +08:00
Ta-Ching ChenandGitHub 0e3a270d27 Delete deployment with proper delete propagation policy (#630) 2018-05-01 23:25:50 +08:00
Ta-Ching ChenandGitHub 6a7381c575 Fix newdeploy backend failed to delete deployment due to incorrect resource version (#657) 2018-05-01 16:48:34 +08:00
VishalandTa-Ching Chen 0aff2f214d Fixes the issue with fn test and adds relevant test cases, fixes #650 (#651)
* Fixes the issue with fn test and adds relevant test cases, fixes #650
* Checking log size for invalid function
* Testing the invalid function logs a few time beforr quitting
* Disabling the test for now, linked to issue #653
2018-04-30 14:14:46 +08:00
VishalandTa-Ching Chen dec4a39c15 Function update should be possible without change to code (#652) 2018-04-30 11:35:19 +08:00
Ta-Ching ChenandGitHub 7577339912 Change time precision for fluentd influxdb plugin to nano second (#646) 2018-04-26 20:57:55 +08:00
Ta-Ching ChenandGitHub 6375819195 Fix test cases occasional failure (#647) 2018-04-26 14:39:16 +08:00
VishalandGitHub 0b34abcada Update .travis.yml
Seems a small item is missing, causing notifications not to work. Similar block in fission-workflows does work.
2018-04-25 23:15:32 +05:30
smruthi2187andTa-Ching Chen 772fdfa15c Setting package buildStatus to pending when functions source is updated. (#637) 2018-04-24 15:24:46 +08:00
VishalandGitHub 7198b24028 Fission upgrade tests (#605)
Tests for upgrading Fission from the last version to current branch and testing. Currently test is very simple and more tests can be added in future
2018-04-24 12:51:17 +05:30
VishalandGitHub eb696432d8 Testing proposal: Requirements and frameworks exploration (#581)
Proposal for revamping the testing framework and tests in general based on needs of Fission project
2018-04-24 00:45:09 +05:30
Ta-Ching ChenandGitHub 2d359f879a Fix SEGFAULT issue when buildmgr failed to update package (#635) 2018-04-21 23:17:33 +08:00
smruthi2187andGitHub 94e2f11ea6 Remove port forward in tests for router, controller and nats pods (#611) 2018-04-20 19:00:22 -07:00
VishalandTa-Ching Chen ff01cf2452 Removed the fn pods functionality (#594) 2018-04-19 14:32:16 +08:00
Ta-Ching ChenandGitHub 400e19a48f Fix executor does not reap specialized function pod when env no longer exists (#633) 2018-04-19 07:39:37 +08:00
Ta-Ching ChenandGitHub 0194f53e12 Fix SEGFAULT issue when build manager failed to update package (#626) 2018-04-17 09:36:11 +08:00
John GallagherandSoam Vasani fef1469c61 Update readme to point to the proper link (#628) 2018-04-16 11:30:27 -07:00
Ta-Ching ChenandGitHub cf53e17be5 Remove confusing default value description & add default value to cli flag (#619) 2018-04-16 17:27:10 +08:00
smruthi2187andGitHub 449f49515a Changes needed for release 0.7.1 (#622)
bump up the version to 0.7.1 in charts and adding changelog.
2018-04-10 18:03:08 -07:00
Ta-Ching ChenandGitHub d01b309fc7 Add validate function to crd resource and do validate before creation/update (#580) 2018-04-08 00:56:13 +08:00
smruthi2187andGitHub 12299df811 Prevent releasing idle connections because transport is shared. (#609)
The transport in RoundTripper is the default transport and there's just one object shared between different http requests and releasing the idle Connections ended up releasing the idle connections that were established with the executor service address too.

So every time a http request was made to get a service for function, the transport layer ended up having to dial a new connection (that was timing out due to various reasons).

By removing the CloseIdleConnections, we minimize the need for transport to dial a new connection to executor for every request, thereby minimizing the occurrence of dial timeouts.
2018-04-06 13:23:25 -07:00
Soam VasaniandGitHub 95a50dbbd6 --spec flags for env and trigger creates (#607) 2018-04-06 09:00:01 -07:00
smruthi2187andGitHub 8014c83b02 Invalidate stale router cache entry with podIP's for deleted pods. (#546)
The router's cache entry for a function might become stale if the pod that had the function specialized gets deleted somehow. In such a case, we'd retry getting a new service for the function from executor and retry forwarding the user request to the newly created service.
2018-04-05 13:47:47 -07:00
smruthi2187andGitHub db2f620121 Updating releasing guideliness with a few more details. (#599) 2018-04-05 13:44:17 -07:00
Soam VasaniandGitHub f9ca626940 E2E test for NATS-streaming trigger (#338)
E2E test for NATS-streaming trigger: move ad-hoc shell script into CI tests.
2018-04-05 10:07:00 -07:00
Ta-Ching ChenandGitHub 1dbf8f9243 Fix components crash before crds creation (#602)
* Wait for CRDs creation for 30 sec when component start
* Fix ensureCRD return nil while the error is not empty
2018-04-04 15:40:55 +08:00
Ta-Ching ChenandGitHub 9406720e8a Add deprecated message to subcommand pods (#592) 2018-04-04 01:26:27 +08:00
Ta-Ching ChenandGitHub 2a06705320 Use a separate controller loop to watch functions change and create a service (#544) 2018-04-04 01:17:51 +08:00
smruthi2187andGitHub d39944e04d updates to changelog. (#598) 2018-04-02 18:08:15 -07:00
smruthi2187andGitHub 6f6b52438a changes needed for release 0.7.0 (#597) 2018-04-02 15:32:22 -07:00
Soam VasaniandGitHub 5baf67f143 Spec validator, better errors, apply waits for previous build (#560)
Adds a validator, invoked through `fission spec validate` and before `fission spec apply`. The validator checks for duplicate names, dangling references, and unused resources.

Also attempts to fix a race between CLI and builder.  Avoids replacing a package while it's building, instead waiting for the build status to change from pending before updating it.
2018-04-02 14:33:31 -07:00
xiekeyangandSoam Vasani 6d61f6e5eb bug fix: spec dir flag (#595)
The cli spec dir flag name should be `specdir`, not `specs`, that should match to:

```go
specDirFlag := cli.StringFlag{Name: "specdir", Usage: "Directory to store specs, defaults to ./specs"}
```

in fission/main.go

Currently it cause the specific specs dir not to work.

```
$ fission spec apply --specdir <my spec dir>
Everything up to date.
```
2018-04-02 05:51:46 -07:00
Ta-Ching ChenandGitHub 6ab902e7a3 Add steps for how to set FISSION_ROUTER env variable & update docs (#593) 2018-03-31 01:10:40 +08:00
smruthi2187andTa-Ching Chen ed7d088bbc Check if the requested file already exists in fetcher and skip fetch (#584) 2018-03-30 16:33:06 +08:00
smruthi2187andGitHub c0058153a0 Adding routerUrl parameter for kubewatch, timer, mqueue trigger deployments (#591)
* This is to enable these triggers to be able to talk to fission router if deployed in a namespace other than fission.
2018-03-29 17:38:50 -07:00
VishalandGitHub a8482a8b89 Function logs: Uses proper way to get server URL
After changes related to port forwarding, function logs was getting empty URL, fixed to use the appropriate method instead.
2018-03-29 15:26:47 +05:30
VishalandGitHub a23f8acffc Tests for function update (#550)
Tests for updates to a function of new deployment executor type. Tests check for changes in the environment, scale, secrets etc. Also checks conversion of function executor type from new deployment to pool manager and vice versa
2018-03-28 20:32:04 +05:30
smruthi2187andSoam Vasani 77be3379b2 Prepending a slash to user input url if missing. (#547)
* Prepending a slash to user input url if missing.

* Adding the url slash check for spec.
2018-03-26 12:31:02 -07:00
Ta-Ching ChenandSoam Vasani 03046170dc Add post-upgrade-job to track fission upgrade (#564) 2018-03-26 12:28:35 -07:00
VishalandSoam Vasani c0acaa74c3 Fixes the issue #559 with env versions (#569)
Do not set automatically set env version if user has already specified it.
2018-03-26 12:25:16 -07:00
Soam VasaniandGitHub 8708fdec93 Add verbosity flag and verbose logs for portforwarder (#575)
Also, parse flags before connecting to server.
2018-03-26 12:23:45 -07:00
Chris LeeandSoam Vasani 6b188c053a Add golang example to installation guide (#578)
Use --src instead of --code for Go example.
2018-03-26 10:04:25 -07:00
Soam Vasani 0fd6114218 Update release checklist 2018-03-22 14:05:51 -07:00
Soam Vasani 9ae06f56c8 Update generated changelog 2018-03-22 14:03:02 -07:00
Soam Vasani 704a8094e6 Bump version in charts, forgot one file 2018-03-22 11:33:23 -07:00
Soam Vasani 6f54565195 Bump version in charts 2018-03-22 11:26:58 -07:00
Soam Vasani 90206fbaa0 Release checklist updates 2018-03-22 11:26:41 -07:00
yiqundingandSoam Vasani 8fb4aca231 This change fixes an error in a yaml file in the fission-core chart. (#563)
The Deployment definition for executor, controller, router contains readines/liveness probes. The port definition for those probes should be numbers, instead of string, i.e. port: "8888" should be port 8888.
2018-03-22 11:14:47 -07:00
Erwin van EykandSoam Vasani 70a93a7302 Add Container object to environment build and runtime specs (#413)
Environment Specs so far had only an image URL to specify a container image.

This was fine for public images but fell short in a few of cases:
(a) Using private image registries
(b) Specifying environment variables (this is needed for workflows helm install)
(c) Setting a SecurityContext for the container

This change adds the Container object to both build and runtime Environments. 

Compatibility is preserved -- the existing ImageURL field is still used.  See the comments in types.go for the overriding rules in the case that both Container and ImageURL are specified.
2018-03-22 00:13:02 -07:00
Bo-Yi WuandSoam Vasani c76b6bacc0 [ci skip] update release number (#561) 2018-03-21 16:16:43 -07:00
VishalandGitHub 15bedfb688 Warning when updating function from poolmgr type to newdeploy executor type
When changing the function executor type from pool manager to new deployment - warning should not be given about ignore environment resources
2018-03-20 14:11:30 -07:00
Ta-Ching ChenandGitHub 1447e6949d Show fission deployment version with cli (#538)
Add version information to binary through go ldflags
2018-03-19 14:14:55 +08:00
Ta-Ching ChenandGitHub 048ab6149a Fix executor failed to clean cache & kubeobjs after function deleted (#534) 2018-03-14 02:29:12 +08:00
Soam Vasani 8a0abc59db use specific go env version in example 2018-03-11 11:54:30 -07:00
Ta-Ching ChenandGitHub 039e003af6 Always retry for function pod specialization when istio feature is enabled. (#536) 2018-03-11 23:17:05 +08:00
Soam VasaniandGitHub 1ea665386a Docs update (#542)
* Misc doc updates

* Some theme updates: no all caps headers, remove blue arrows
2018-03-09 11:26:15 -08:00
Soam VasaniandGitHub e7c9a67a68 Release script updates (#541) 2018-03-08 13:48:57 -08:00
Ta-Ching ChenandGitHub afd7f7f436 Show warning when trying to create a route with non-existent function (#539) 2018-03-09 02:45:30 +08:00
Ta-Ching ChenandGitHub b4300feabc Fix executor tries to create a new deployment when a function is updated (#524)
Newdeploy manager now checks the existence of function service cache by function UID before trying to create a new deployment. And return the cached fsvc directly if the cache exists.
2018-03-08 02:52:36 +08:00
smruthi2187andGitHub a3826046a5 Delete healthz log (#525)
Silencing the healthz logs.
2018-03-02 15:50:48 -08:00
151 changed files with 4743 additions and 1171 deletions
+3 -1
View File
@@ -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
View File
@@ -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))
+2 -2
View File
@@ -22,7 +22,7 @@ minikube, you'll need to set the proper environment variables with
``` ```
# Get dependencies # Get dependencies
$ glide install $ glide install -v
# Build fission server and an image # Build fission server and an image
$ pushd fission-bundle $ pushd fission-bundle
@@ -41,7 +41,7 @@ minikube and its built-in docker daemon:
Next, install fission with this image on your kubernetes cluster using the helm chart: Next, install fission with this image on your kubernetes cluster using the helm chart:
``` ```
$ helm install --set "image=minikube/fission-bundle,pullPolicy=IfNotPresent,analytics=false" charts/fission-all $ helm install --set "image=minikube/fission-bundle,imageTag=latest,pullPolicy=IfNotPresent,analytics=false" charts/fission-all
``` ```
And if you're changing the CLI too, you can build it with: And if you're changing the CLI too, you can build it with:
+2 -2
View File
@@ -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"
@@ -11,7 +11,7 @@ defaultContentLanguageInSubdir= true
editURL = "https://github.com/fission/fission/edit/master/Documentation/docs-site/content/" editURL = "https://github.com/fission/fission/edit/master/Documentation/docs-site/content/"
description = "Documentation for Fission" description = "Documentation for Fission"
author = "Fission" author = "Fission"
showVisitedLinks = true showVisitedLinks = false
themeVariant = "fission" themeVariant = "fission"
[outputs] [outputs]
+2 -2
View File
@@ -92,8 +92,8 @@ 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!
``` ```
See the [examples](examples) directory for more. See the [examples](examples) directory for more.
@@ -7,4 +7,5 @@ weight: 30
# Fission Concepts # Fission Concepts
#### Understanding Fission terminology and concepts This is an overview of the few main concepts in Fission: Functions,
Environments, and Triggers.
@@ -1,5 +1,5 @@
--- ---
title: "Environment" title: "Environments"
draft: false draft: false
weight: 32 weight: 32
--- ---
@@ -7,4 +7,4 @@ chapter : true
# Installation # Installation
### Installing and upgrading Fission ### Installing and upgrading Fission
@@ -20,19 +20,19 @@ ready. If you already have helm, [skip ahead to the fission install](#install-fi
Ensure you have the Kubernetes CLI. Ensure you have the Kubernetes CLI.
You can get the Kubernetes CLI for OSX like this: You can get the Kubernetes CLI for OSX like this:
``` ```sh
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin $ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin
``` ```
Or, for Linux: Or, for Linux:
``` ```sh
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin $ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin
``` ```
Ensure you have access to a cluster; use kubectl to check your Ensure you have access to a cluster; use kubectl to check your
Kubernetes version: Kubernetes version:
``` ```sh
$ kubectl version $ kubectl version
``` ```
@@ -47,7 +47,7 @@ the next section](#install-fission).
First, you'll need the helm CLI: First, you'll need the helm CLI:
On __OS X__: On __OS X__:
``` ```sh
$ curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.7.0-darwin-amd64.tar.gz $ curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.7.0-darwin-amd64.tar.gz
$ tar xzf helm-v2.7.0-darwin-amd64.tar.gz $ tar xzf helm-v2.7.0-darwin-amd64.tar.gz
@@ -56,7 +56,7 @@ $ mv darwin-amd64/helm /usr/local/bin
``` ```
On __Linux__: On __Linux__:
``` ```sh
$ curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.7.0-linux-amd64.tar.gz $ curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.7.0-linux-amd64.tar.gz
$ tar xzf helm-v2.7.0-linux-amd64.tar.gz $ tar xzf helm-v2.7.0-linux-amd64.tar.gz
@@ -66,7 +66,7 @@ $ mv linux-amd64/helm /usr/local/bin
Next, install the Helm server on your Kubernetes cluster: Next, install the Helm server on your Kubernetes cluster:
``` ```sh
$ helm init $ helm init
``` ```
@@ -74,7 +74,7 @@ $ helm init
#### Minikube #### Minikube
``` ```sh
$ helm install --namespace fission --set serviceType=NodePort https://github.com/fission/fission/releases/download/0.6.0/fission-all-0.6.0.tgz $ helm install --namespace fission --set serviceType=NodePort https://github.com/fission/fission/releases/download/0.6.0/fission-all-0.6.0.tgz
``` ```
@@ -84,7 +84,7 @@ want to expose anything outside the cluster.
#### Cloud hosted clusters (GKE, AWS, Azure etc.) #### Cloud hosted clusters (GKE, AWS, Azure etc.)
``` ```sh
$ helm install --namespace fission https://github.com/fission/fission/releases/download/0.6.0/fission-all-0.6.0.tgz $ helm install --namespace fission https://github.com/fission/fission/releases/download/0.6.0/fission-all-0.6.0.tgz
``` ```
@@ -94,7 +94,7 @@ The fission-all helm chart installs a full set of services including
the NATS message queue, influxDB for logs, etc. If you want a more the NATS message queue, influxDB for logs, etc. If you want a more
minimal setup, you can install the fission-core chart instead: minimal setup, you can install the fission-core chart instead:
``` ```sh
$ helm install --namespace fission https://github.com/fission/fission/releases/download/0.6.0/fission-core-0.6.0.tgz $ helm install --namespace fission https://github.com/fission/fission/releases/download/0.6.0/fission-core-0.6.0.tgz
``` ```
@@ -104,13 +104,13 @@ $ helm install --namespace fission https://github.com/fission/fission/releases/d
Get the CLI binary for Mac: Get the CLI binary for Mac:
``` ```sh
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.6.0/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.6.0/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/
``` ```
#### Linux #### Linux
``` ```sh
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.6.0/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.6.0/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/
``` ```
@@ -123,7 +123,7 @@ this windows executable: [fission.exe](https://github.com/fission/fission/releas
Finally, you're ready to use Fission! Finally, you're ready to use Fission!
``` ```sh
$ fission env create --name nodejs --image fission/node-env:0.6.0 $ fission env create --name nodejs --image fission/node-env:0.6.0
$ curl -LO https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js $ curl -LO https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js
@@ -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
@@ -1,28 +1,38 @@
--- ---
title: "Accessing Secret/configmap in function" title: "Accessing Secrets in Functions"
draft: false draft: false
weight: 47 weight: 47
--- ---
From fission v0.5.0 and later, functions are able to access [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) and [ConfigMaps](https://kubernetes.io/docs/concepts/storage/volumes/#configmap) specified by users. Functions can access Kubernetes
[Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)
and
[ConfigMaps](https://kubernetes.io/docs/concepts/storage/volumes/#configmap).
### Create Secret and ConfigMap Use secrets for things like API keys, authentication tokens, and so
on.
You can create Secret and ConfigMap with CLI. Use config maps for any other configuration that doesn't need to be a
secret.
### Create A Secret or a ConfigMap
You can create a Secret or ConfigMap with the Kubernetes CLI:
``` bash ``` bash
$ kubectl -n default create secret generic foo --from-literal=TEST_KEY="TESTVALUE" $ kubectl -n default create secret generic my-secret --from-literal=TEST_KEY="TESTVALUE"
$ kubectl -n default create configmap bar --from-literal=TEST_KEY=TESTVALUE
$ kubectl -n default create configmap my-configmap --from-literal=TEST_KEY="TESTVALUE"
``` ```
Or use `kubectl create -f <filename.yaml>` to create these from a YAML file. Or, use `kubectl create -f <filename.yaml>` to create these from a YAML file.
``` yaml ``` yaml
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
namespace: default namespace: default
name: foo name: my-secret
data: data:
TEST_KEY: VEVTVFZBTFVF # value after base64 encode TEST_KEY: VEVTVFZBTFVF # value after base64 encode
type: Opaque type: Opaque
@@ -32,14 +42,16 @@ apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
namespace: default namespace: default
name: bar name: my-configmap
data: data:
TEST_KEY: TESTVALUE TEST_KEY: TESTVALUE
``` ```
### Access Secret and ConfigMap ### Accessing Secrets and ConfigMaps
Since content of Secret and ConfigMap are key-value pairs, functions can access them with following paths: Secrets and configmaps are accessed similarly. Each secret or
configmap is a set of key value pairs. Fission sets these up as files
you can read from your function.
``` bash ``` bash
# Secret path # Secret path
@@ -52,24 +64,25 @@ Since content of Secret and ConfigMap are key-value pairs, functions can access
From the previous example, the paths are: From the previous example, the paths are:
``` bash ``` bash
# secret foo # secret my-secret
/secrets/default/foo/TEST_KEY /secrets/default/my-secret/TEST_KEY
# confimap bar # confimap my-configmap
/configs/default/bar/TEST_KEY /configs/default/my-configmap/TEST_KEY
``` ```
Now, let's create a simple python function (leaker.py) that return value of Secret `foo` and ConfigMap `bar`. Now, let's create a simple python function (leaker.py) that returns
the value of Secret `my-secret` and ConfigMap `my-configmap`.
``` python ``` python
# leaker.py # leaker.py
def main(): def main():
path = "/configs/default/bar/TEST_KEY" path = "/configs/default/my-configmap/TEST_KEY"
f = open(path, "r") f = open(path, "r")
config = f.read() config = f.read()
path = "/secrets/default/foo/TEST_KEY" path = "/secrets/default/my-secret/TEST_KEY"
f = open(path, "r") f = open(path, "r")
secret = f.read() secret = f.read()
@@ -79,27 +92,29 @@ def main():
``` ```
Create environment, function and http trigger. Create an environment and a function:
``` bash ``` bash
# create python env # create python env
$ fission env create --name python --image fission/python-env $ fission env create --name python --image fission/python-env
# create function named "leaker" # create function named "leaker"
$ fission fn create --name leaker --env python --code leaker.py --secret foo --configmap bar $ fission fn create --name leaker --env python --code leaker.py --secret my-secret --configmap my-configmap
# create route(http trigger)
$ fission route create --function leaker --url /leaker --method GET
``` ```
Try to access the function, the output should look like following. Run the function, and the output should look like this:
``` bash ``` bash
$ curl http://$FISSION_ROUTER/leaker $ fission function test --name leaker
ConfigMap: TESTVALUE ConfigMap: TESTVALUE
Secret: TESTVALUE Secret: TESTVALUE
``` ```
Note: If the Secret or ConfigMap value is updated, the function may not get the updated value for some time; it may get a cached older value.
{{% notice note %}}
If the Secret or ConfigMap value is updated, the function may
not get the updated value for some time; it may get a cached older
value.
{{% /notice %}}
@@ -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,10 +19,10 @@ 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
``` ```
### Viweing environment information ### Viewing environment information
You can list the environments or view information of an individual environment: You can list the environments or view information of an individual environment:
@@ -30,8 +30,8 @@ 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
@@ -83,4 +83,4 @@ hello-qoxmothj Deployment/hello-qoxmothj 7% / 50% 1 6 1
hello-qoxmothj Deployment/hello-qoxmothj 6% / 50% 1 6 1 11m hello-qoxmothj Deployment/hello-qoxmothj 6% / 50% 1 6 1 11m
hello-qoxmothj Deployment/hello-qoxmothj 6% / 50% 1 6 1 12m hello-qoxmothj Deployment/hello-qoxmothj 6% / 50% 1 6 1 12m
hello-qoxmothj Deployment/hello-qoxmothj 6% / 50% 1 6 1 12m hello-qoxmothj Deployment/hello-qoxmothj 6% / 50% 1 6 1 12m
``` ```
@@ -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!
@@ -1,5 +1,5 @@
--- ---
title: "Trigger" title: "Triggers"
draft: false draft: false
weight: 44 weight: 44
--- ---
@@ -38,11 +38,16 @@ halfhourly 0 30 * * * hello
minute @every 1m hello minute @every 1m hello
``` ```
### Create a MQ Trigger ### Create a Message Queue Trigger
For creating a MQ based trigger which will invoke the function when a new message arrives in newfile topic, you can use the syntax below. The response of the function execution will be sent to topic newfileresponse. A message queue trigger invokes a function based on messages from an
message queue. Currently, NATS and Azure Storage Queue are supported
queues. (Kafka support is under development.)
``` ```
$ fission mqt create --name hellomsg --function hello --mqtype nats-streaming --topic newfile --resptopic newfileresponse $ fission mqt create --name hellomsg --function hello --mqtype nats-streaming --topic newfile --resptopic newfileresponse
trigger 'hellomsg' created trigger 'hellomsg' created
``` ```
You can list or update message queue triggers with `fission mqt list`,
or `fission mqt update`.
@@ -39,13 +39,7 @@
{{ end }} {{ end }}
{{ end }} {{ end }}
{{with ($.Scratch.Get "prevPage")}}
<a class="nav nav-prev" href="{{.URL}}" title="{{.Title}}"> <i class="fa fa-chevron-left"></i></a>
{{end}}
{{with ($.Scratch.Get "nextPage")}}
<a class="nav nav-next" href="{{.URL}}" title="{{.Title}}" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
{{end}}
</div> </div>
</section> </section>
@@ -98,6 +98,7 @@ textarea:focus, input[type="email"]:focus, input[type="number"]:focus, input[typ
box-shadow: inset 0 1px 3px rgba(0,0,0,.06),0 0 5px rgba(0,169,218,.7) box-shadow: inset 0 1px 3px rgba(0,0,0,.06),0 0 5px rgba(0,169,218,.7)
} }
#header-wrapper { #header-wrapper {
/*background: #4a4a4b;*/
background: #1E022D; background: #1E022D;
color: #fff; color: #fff;
text-align: center; text-align: center;
@@ -445,6 +446,7 @@ textarea:focus, input[type="email"]:focus, input[type="number"]:focus, input[typ
} }
body { body {
font-family: "Work Sans", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif; font-family: "Work Sans", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif;
/*font-family: -apple-system,BlinkMacSystemFont,"avenir next",avenir,"helvetica neue",helvetica,ubuntu,roboto,noto,"segoe ui",arial,sans-serif;*/
font-weight: 300; font-weight: 300;
line-height: 1.6; line-height: 1.6;
font-size: 18px !important; font-size: 18px !important;
@@ -457,11 +459,13 @@ h2, h3, h4, h5, h6 {
letter-spacing: -1px; letter-spacing: -1px;
} }
h1 { h1 {
font-family: "Novacento Sans Wide", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif; font-family: "Work Sans", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif;
text-align: center; /*font-family: "Novacento Sans Wide", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif;*/
text-transform: uppercase; /*text-align: center;*/
color: #222; /*text-transform: uppercase;*/
font-weight: 200; color: #5e5e5e;
font-weight: 400;
letter-spacing: -1px;
} }
blockquote { blockquote {
border-left: 10px solid #F0F2F4; border-left: 10px solid #F0F2F4;
@@ -1116,8 +1120,10 @@ pre .copy-to-clipboard:hover {
} }
#sidebar #shortcuts h3 { #sidebar #shortcuts h3 {
font-family: "Novacento Sans Wide", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif; /*font-family: "Novacento Sans Wide", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif;*/
color: white ; font-family: "Work Sans", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif;
color: #eee;
font-weight: 200;
margin-top:1rem; margin-top:1rem;
padding-left: 1rem; padding-left: 1rem;
} }
@@ -458,8 +458,8 @@ h2, h3, h4, h5, h6 {
} }
h1 { h1 {
font-family: "Novacento Sans Wide", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif; font-family: "Novacento Sans Wide", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif;
text-align: center; /*text-align: center;*/
text-transform: uppercase; /* text-transform: uppercase; */
color: #222; color: #222;
font-weight: 200; font-weight: 200;
} }
+209
View File
@@ -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
+4 -1
View File
@@ -1,2 +1,5 @@
These instructions are replaced by: These instructions are replaced by:
http://fission.io/docs/0.4.0/installation/
http://fission.io/docs/0.6.0/installation/
You can change the release number `0.6.0` from [release page](https://github.com/fission/fission/releases).
+3 -3
View File
@@ -15,7 +15,7 @@
.DEFAULT_GOAL := build .DEFAULT_GOAL := build
IMAGE ?= fission/fission-bundle IMAGE ?= fission/fission-bundle
VERSION ?= 0.3.0 VERSION ?= latest
ARCH ?= amd64 ARCH ?= amd64
OS ?= linux OS ?= linux
@@ -42,5 +42,5 @@ image-push: image
docker push "$(IMAGE):$(VERSION)" docker push "$(IMAGE):$(VERSION)"
clean: clean:
@rm -rf fission-bundle/fission-bundle @rm -f fission-bundle/fission-bundle
@rm -rf fission/fission @rm -f fission/fission
+3 -3
View File
@@ -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
====== ======
+7
View File
@@ -33,6 +33,8 @@ import (
"time" "time"
"github.com/dchest/uniuri" "github.com/dchest/uniuri"
"github.com/fission/fission"
) )
const ( const (
@@ -68,6 +70,11 @@ func MakeBuilder(sharedVolumePath string) *Builder {
} }
} }
func (builder *Builder) VersionHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprintf(w, fission.VersionInfo().String())
}
func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) { func (builder *Builder) Handler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" { if r.Method != "POST" {
e := fmt.Sprintf("Method not allowed: %v", r.Method) e := fmt.Sprintf("Method not allowed: %v", r.Method)
+16 -1
View File
@@ -1,3 +1,18 @@
#!/bin/bash #!/bin/bash
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH -o builder . version=$1
if [ -z $version ]; then
version=$(git rev-parse HEAD)
fi
date=$2
if [ -z $date ]; then
date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
fi
gitcommit=$3
if [ -z $gitcommit ]; then
gitcommit=$(git rev-parse HEAD)
fi
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH -ldflags "-X github.com/fission/fission.GitCommit=$gitcommit -X github.com/fission/fission.BuildDate=$date -X github.com/fission/fission.Version=$version" -o builder .
+1
View File
@@ -38,6 +38,7 @@ func main() {
builder := builder.MakeBuilder(dir) builder := builder.MakeBuilder(dir)
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/", builder.Handler) mux.HandleFunc("/", builder.Handler)
mux.HandleFunc("/version", builder.VersionHandler)
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
+5
View File
@@ -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()
+9 -18
View File
@@ -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)
} }
@@ -497,7 +488,7 @@ func (envw *environmentWatcher) createBuilderDeployment(env *crd.Environment) (*
}, },
}, },
Containers: []apiv1.Container{ Containers: []apiv1.Container{
{ fission.MergeContainerSpecs(&apiv1.Container{
Name: "builder", Name: "builder",
Image: env.Spec.Builder.Image, Image: env.Spec.Builder.Image,
ImagePullPolicy: apiv1.PullAlways, ImagePullPolicy: apiv1.PullAlways,
@@ -530,7 +521,7 @@ func (envw *environmentWatcher) createBuilderDeployment(env *crd.Environment) (*
}, },
}, },
}, },
}, }, env.Spec.Builder.Container),
{ {
Name: "fetcher", Name: "fetcher",
Image: envw.fetcherImage, Image: envw.fetcherImage,
+7 -7
View File
@@ -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
} }
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
name: fission-all name: fission-all
version: 0.6.0 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.0 appVersion: 0.7.1
+3 -3
View File
@@ -1,13 +1,13 @@
1. Install the client CLI. 1. Install the client CLI.
Mac: Mac:
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.6.0/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.0/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.0/fission-cli-windows.exe For Windows, you can use the linux binary on WSL. Or you can download this windows executable: https://github.com/fission/fission/releases/download/0.7.1/fission-cli-windows.exe
2. You're ready to use Fission! 2. You're ready to use Fission!
+3 -5
View File
@@ -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 }}
+3 -3
View File
@@ -16,13 +16,13 @@ image: fission/fission-bundle
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
## Fission image version ## Fission image version
imageTag: 0.6.0 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.0 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.0 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
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
name: fission-core name: fission-core
version: 0.6.0 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.0 appVersion: 0.7.1
+3 -3
View File
@@ -1,13 +1,13 @@
1. Install the client CLI. 1. Install the client CLI.
Mac: Mac:
$ curl -Lo fission https://github.com/fission/fission/releases/download/0.6.0/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.0/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.0/fission-cli-windows.exe For Windows, you can use the linux binary on WSL. Or you can download this windows executable: https://github.com/fission/fission/releases/download/0.7.1/fission-cli-windows.exe
2. You're ready to use Fission! 2. You're ready to use Fission!
+8 -10
View File
@@ -139,19 +139,17 @@ 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"
port: "8888" port: 8888
initialDelaySeconds: 1 initialDelaySeconds: 1
periodSeconds: 1 periodSeconds: 1
failureThreshold: 30 failureThreshold: 30
livenessProbe: livenessProbe:
httpGet: httpGet:
path: "/healthz" path: "/healthz"
port: "8888" port: 8888
initialDelaySeconds: 35 initialDelaySeconds: 35
periodSeconds: 5 periodSeconds: 5
serviceAccount: fission-svc serviceAccount: fission-svc
@@ -180,14 +178,14 @@ spec:
readinessProbe: readinessProbe:
httpGet: httpGet:
path: "/router-healthz" path: "/router-healthz"
port: "8888" port: 8888
initialDelaySeconds: 1 initialDelaySeconds: 1
periodSeconds: 1 periodSeconds: 1
failureThreshold: 30 failureThreshold: 30
livenessProbe: livenessProbe:
httpGet: httpGet:
path: "/router-healthz" path: "/router-healthz"
port: "8888" port: 8888
initialDelaySeconds: 35 initialDelaySeconds: 35
periodSeconds: 5 periodSeconds: 5
serviceAccount: fission-svc serviceAccount: fission-svc
@@ -238,14 +236,14 @@ spec:
readinessProbe: readinessProbe:
httpGet: httpGet:
path: "/healthz" path: "/healthz"
port: "8888" port: 8888
initialDelaySeconds: 1 initialDelaySeconds: 1
periodSeconds: 1 periodSeconds: 1
failureThreshold: 30 failureThreshold: 30
livenessProbe: livenessProbe:
httpGet: httpGet:
path: "/healthz" path: "/healthz"
port: "8888" port: 8888
initialDelaySeconds: 35 initialDelaySeconds: 35
periodSeconds: 5 periodSeconds: 5
serviceAccount: fission-svc serviceAccount: fission-svc
@@ -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 }}
+2 -2
View File
@@ -13,7 +13,7 @@ routerServiceType: LoadBalancer
image: fission/fission-bundle image: fission/fission-bundle
## Fission image version ## Fission image version
imageTag: 0.6.0 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.0 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
+67
View File
@@ -19,10 +19,16 @@ package fission
import ( import (
"fmt" "fmt"
"net" "net"
"net/http"
"os" "os"
"os/signal" "os/signal"
"runtime/debug" "runtime/debug"
"strings"
"syscall" "syscall"
"github.com/gorilla/handlers"
"github.com/imdario/mergo"
apiv1 "k8s.io/client-go/pkg/api/v1"
) )
func UrlForFunction(name string) string { func UrlForFunction(name string) string {
@@ -52,3 +58,64 @@ func IsNetworkError(err error) bool {
func GetFunctionIstioServiceName(fnName, fnNamespace string) string { func GetFunctionIstioServiceName(fnName, fnNamespace string) string {
return fmt.Sprintf("istio-%v-%v", fnName, fnNamespace) return fmt.Sprintf("istio-%v-%v", fnName, fnNamespace)
} }
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestURI := r.RequestURI
if !strings.Contains(requestURI, "healthz") {
// Call the next handler, which can be another middleware in the chain, or the final handler.
handlers.LoggingHandler(os.Stdout, next).ServeHTTP(w, r)
}
})
}
// MergeContainerSpecs merges container specs using a predefined order.
//
// The order of the arguments indicates which spec has precedence (lower index takes precedence over higher indexes).
// Slices and maps are merged; other fields are set only if they are a zero value.
func MergeContainerSpecs(specs ...*apiv1.Container) apiv1.Container {
result := &apiv1.Container{}
for _, spec := range specs {
if spec == nil {
continue
}
err := mergo.Merge(result, spec)
if err != nil {
panic(err)
}
}
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
}
+3 -12
View File
@@ -21,10 +21,8 @@ import (
"net/http" "net/http"
"os" "os"
"runtime/debug" "runtime/debug"
"strconv"
"strings" "strings"
"github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
kerrors "k8s.io/apimachinery/pkg/api/errors" kerrors "k8s.io/apimachinery/pkg/api/errors"
@@ -84,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
} }
@@ -138,7 +128,7 @@ func (api *API) getLogDBConfig(dbType string) logDBConfig {
func (api *API) HomeHandler(w http.ResponseWriter, r *http.Request) { func (api *API) HomeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprintf(w, "{\"message\": \"Fission API\", \"version\": \"0.6.0\"}\n") fmt.Fprintf(w, fission.VersionInfo().String())
} }
func (api *API) ApiVersionMismatchHandler(w http.ResponseWriter, r *http.Request) { func (api *API) ApiVersionMismatchHandler(w http.ResponseWriter, r *http.Request) {
@@ -209,5 +199,6 @@ func (api *API) Serve(port int) {
address := fmt.Sprintf(":%v", port) address := fmt.Sprintf(":%v", port)
log.WithFields(log.Fields{"port": port}).Info("Server started") log.WithFields(log.Fields{"port": port}).Info("Server started")
log.Fatal(http.ListenAndServe(address, handlers.LoggingHandler(os.Stdout, r))) r.Use(fission.LoggingMiddleware)
log.Fatal(http.ListenAndServe(address, r))
} }
+15 -11
View File
@@ -23,6 +23,8 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"reflect"
"strings"
"testing" "testing"
"time" "time"
@@ -68,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) {
@@ -81,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",
},
}, },
}, },
} }
@@ -141,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,
@@ -214,11 +222,7 @@ func TestEnvironmentApi(t *testing.T) {
e, err := g.client.EnvironmentGet(m) e, err := g.client.EnvironmentGet(m)
panicIf(err) panicIf(err)
assert(testEnv.Spec.AllowedFunctionsPerContainer == e.Spec.AllowedFunctionsPerContainer, "env AllowedFunctionsPerContainer should match after reading") assert(reflect.DeepEqual(testEnv.Spec, e.Spec), "env should match after reading")
assert(testEnv.Spec.Poolsize == e.Spec.Poolsize, "env Poolsize should match after reading")
assert(testEnv.Spec.Builder == e.Spec.Builder, "env Builder should match after reading")
assert(testEnv.Spec.Runtime == e.Spec.Runtime, "env Runtime should match after reading")
assert(testEnv.Spec.Version == e.Spec.Version, "env Version should match after reading")
testEnv.Metadata.ResourceVersion = m.ResourceVersion testEnv.Metadata.ResourceVersion = m.ResourceVersion
testEnv.Spec.Runtime.Image = "another-img" testEnv.Spec.Runtime.Image = "another-img"
@@ -266,9 +270,9 @@ func TestWatchApi(t *testing.T) {
w, err := g.client.WatchGet(m) w, err := g.client.WatchGet(m)
panicIf(err) panicIf(err)
assert((testWatch.Spec.Namespace == w.Spec.Namespace && assert(testWatch.Spec.Namespace == w.Spec.Namespace &&
testWatch.Spec.Type == w.Spec.Type && testWatch.Spec.Type == w.Spec.Type &&
testWatch.Spec.FunctionReference == w.Spec.FunctionReference), "watch should match after reading") testWatch.Spec.FunctionReference == w.Spec.FunctionReference, "watch should match after reading")
testWatch.Metadata.Name = "yyy" testWatch.Metadata.Name = "yyy"
m2, err := g.client.WatchCreate(testWatch) m2, err := g.client.WatchCreate(testWatch)
+11
View File
@@ -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
+10
View File
@@ -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
+11
View File
@@ -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
+11
View File
@@ -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
+10
View File
@@ -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
+11
View File
@@ -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
+4 -1
View File
@@ -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 {
-11
View File
@@ -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
}
-6
View File
@@ -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)
-78
View File
@@ -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(""))
} }
-6
View File
@@ -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 {
-6
View File
@@ -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)
-6
View File
@@ -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")
-6
View File
@@ -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 {
-6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
} }
+16 -1
View File
@@ -1,2 +1,17 @@
#!/bin/sh #!/bin/sh
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH -o fetcher . version=$1
if [ -z $version ]; then
version=$(git rev-parse HEAD)
fi
date=$2
if [ -z $date ]; then
date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
fi
gitcommit=$3
if [ -z $gitcommit ]; then
gitcommit=$(git rev-parse HEAD)
fi
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH -ldflags "-X github.com/fission/fission.GitCommit=$gitcommit -X github.com/fission/fission.BuildDate=$date -X github.com/fission/fission.Version=$version" -o fetcher .
+1
View File
@@ -71,6 +71,7 @@ func main() {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/", fetcher.FetchHandler) mux.HandleFunc("/", fetcher.FetchHandler)
mux.HandleFunc("/upload", fetcher.UploadHandler) mux.HandleFunc("/upload", fetcher.UploadHandler)
mux.HandleFunc("/version", fetcher.VersionHandler)
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
+27
View File
@@ -163,6 +163,11 @@ func writeSecretOrConfigMap(dataMap map[string][]byte, dirPath string) error {
return nil return nil
} }
func (fetcher *Fetcher) VersionHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprintf(w, fission.VersionInfo().String())
}
func (fetcher *Fetcher) FetchHandler(w http.ResponseWriter, r *http.Request) { func (fetcher *Fetcher) FetchHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" { if r.Method != "POST" {
http.Error(w, "only POST is supported on this endpoint", http.StatusMethodNotAllowed) http.Error(w, "only POST is supported on this endpoint", http.StatusMethodNotAllowed)
@@ -212,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)
@@ -236,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
+2 -2
View File
@@ -7,7 +7,7 @@ spec:
version: 2 version: 2
builder: builder:
command: build command: build
image: fission/go-build-env:latest image: fission/go-builder:0.6.0
runtime: runtime:
image: fission/go-env:latest image: fission/go-env:0.6.0
+19 -5
View File
@@ -23,10 +23,8 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"os"
"strings" "strings"
"github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -59,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)
@@ -113,5 +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)
log.Fatal(http.ListenAndServe(address, handlers.LoggingHandler(os.Stdout, r))) executor.gpm.Run(ctx)
r.Use(fission.LoggingMiddleware)
log.Fatal(http.ListenAndServe(address, r))
} }
+30 -71
View File
@@ -23,7 +23,7 @@ import (
"time" "time"
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"
@@ -32,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,
@@ -64,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
@@ -103,31 +100,32 @@ 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 {
log.Printf("Error getting function: %v", fsvc.Function.Name)
continue
}
// Ignore functions of NewDeploy ExecutorType with MinScale > 0
if fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale > 0 && fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fission.ExecutorTypeNewdeploy {
continue
}
deleted, err := fsCache.DeleteOld(fsvc, idlePodReapTime) deleted, err := fsCache.DeleteOld(fsvc, idlePodReapTime)
if err != nil { if err != nil {
log.Printf("Error deleting Kubernetes objects for fsvc '%v': %v", fsvc, err) log.Printf("Error deleting Kubernetes objects for fsvc '%v': %v", fsvc, err)
log.Printf("Object Name| Object Kind | Object Namespace") log.Printf("Object Name| Object Kind | Object Namespace")
@@ -139,6 +137,7 @@ func idleObjectReaper(kubeClient *kubernetes.Clientset,
if !deleted { if !deleted {
continue continue
} }
for _, kubeobj := range fsvc.KubernetesObjects { for _, kubeobj := range fsvc.KubernetesObjects {
deleteKubeobject(kubeClient, &kubeobj) deleteKubeobject(kubeClient, &kubeobj)
} }
@@ -158,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)
@@ -173,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 {
@@ -198,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
} }
@@ -206,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
} }
@@ -214,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
View File
@@ -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(
+5 -1
View File
@@ -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{
+65 -13
View File
@@ -21,6 +21,7 @@ import (
"time" "time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/pkg/api" "k8s.io/client-go/pkg/api"
"github.com/fission/fission" "github.com/fission/fission"
@@ -56,8 +57,9 @@ type (
} }
FunctionServiceCache struct { FunctionServiceCache struct {
byFunction *cache.Cache // function-key -> funcSvc : map[string]*funcSvc byFunction *cache.Cache // function-key -> funcSvc : map[string]*funcSvc
byAddress *cache.Cache // address -> function : map[string]metav1.ObjectMeta byAddress *cache.Cache // address -> function : map[string]metav1.ObjectMeta
byFunctionUID *cache.Cache // function uid -> function : map[string]metav1.ObjectMeta
requestChannel chan *fscRequest requestChannel chan *fscRequest
} }
@@ -66,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 {
@@ -76,10 +77,25 @@ type (
} }
) )
func IsNotFoundError(err error) bool {
if fe, ok := err.(fission.Error); ok {
return fe.Code == fission.ErrorNotFound
}
return false
}
func IsNameExistError(err error) bool {
if fe, ok := err.(fission.Error); ok {
return fe.Code == fission.ErrorNameExists
}
return false
}
func MakeFunctionServiceCache() *FunctionServiceCache { func MakeFunctionServiceCache() *FunctionServiceCache {
fsc := &FunctionServiceCache{ fsc := &FunctionServiceCache{
byFunction: cache.MakeCache(0, 0), byFunction: cache.MakeCache(0, 0),
byAddress: cache.MakeCache(0, 0), byAddress: cache.MakeCache(0, 0),
byFunctionUID: cache.MakeCache(0, 0),
requestChannel: make(chan *fscRequest), requestChannel: make(chan *fscRequest),
} }
go fsc.service() go fsc.service()
@@ -100,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)
} }
} }
@@ -136,6 +151,27 @@ func (fsc *FunctionServiceCache) GetByFunction(m *metav1.ObjectMeta) (*FuncSvc,
return &fsvcCopy, nil return &fsvcCopy, nil
} }
func (fsc *FunctionServiceCache) GetByFunctionUID(uid types.UID) (*FuncSvc, error) {
mI, err := fsc.byFunctionUID.Get(uid)
if err != nil {
return nil, err
}
m := mI.(metav1.ObjectMeta)
fsvcI, err := fsc.byFunction.Get(crd.CacheKey(&m))
if err != nil {
return nil, err
}
// update atime
fsvc := fsvcI.(*FuncSvc)
fsvc.Atime = time.Now()
fsvcCopy := *fsvc
return &fsvcCopy, nil
}
func (fsc *FunctionServiceCache) Add(fsvc FuncSvc) (*FuncSvc, error) { func (fsc *FunctionServiceCache) Add(fsvc FuncSvc) (*FuncSvc, error) {
err, existing := fsc.byFunction.Set(crd.CacheKey(fsvc.Function), &fsvc) err, existing := fsc.byFunction.Set(crd.CacheKey(fsvc.Function), &fsvc)
if err != nil { if err != nil {
@@ -158,16 +194,28 @@ func (fsc *FunctionServiceCache) Add(fsvc FuncSvc) (*FuncSvc, error) {
// because of multiple-specialization. See issue #331. // because of multiple-specialization. See issue #331.
err, _ = fsc.byAddress.Set(fsvc.Address, *fsvc.Function) err, _ = fsc.byAddress.Set(fsvc.Address, *fsvc.Function)
if err != nil { if err != nil {
if fe, ok := err.(fission.Error); ok { if IsNameExistError(err) {
if fe.Code == fission.ErrorNameExists { err = nil
err = nil
}
} }
if err != nil { if err != nil {
log.Printf("error caching fsvc: %v", err) log.Printf("error caching fsvc: %v", err)
} }
return nil, err return nil, err
} }
// Add to byFunctionUID cache. Ignore NameExists errors
// because of multiple-specialization. See issue #331.
err, _ = fsc.byFunctionUID.Set(fsvc.Function.UID, *fsvc.Function)
if err != nil {
if IsNameExistError(err) {
err = nil
}
if err != nil {
log.Printf("error caching fsvc by function uid: %v", err)
}
return nil, err
}
return nil, nil return nil, nil
} }
@@ -197,23 +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)
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
@@ -70,6 +70,11 @@ func TestFunctionServiceCache(t *testing.T) {
fsc.Log() fsc.Log()
log.Panicf("Failed to get fsvc: %v", err) log.Panicf("Failed to get fsvc: %v", err)
} }
f, err = fsc.GetByFunctionUID(fsvc.Function.UID)
if err != nil {
fsc.Log()
log.Panicf("Failed to get fsvc by function uid: %v", err)
}
fsvc.Atime = f.Atime fsvc.Atime = f.Atime
fsvc.Ctime = f.Ctime fsvc.Ctime = f.Ctime
if f.Address != fsvc.Address { if f.Address != fsvc.Address {
@@ -98,4 +103,10 @@ func TestFunctionServiceCache(t *testing.T) {
fsc.Log() fsc.Log()
log.Panicf("found fsvc while expecting empty cache: %v", err) log.Panicf("found fsvc while expecting empty cache: %v", err)
} }
_, err = fsc.GetByFunctionUID(fsvc.Function.UID)
if err == nil {
fsc.Log()
log.Panicf("found fsvc by function uid while expecting empty cache: %v", err)
}
} }
+26 -16
View File
@@ -58,7 +58,10 @@ func (deploy *NewDeploy) createOrGetDeployment(fn *crd.Function, env *crd.Enviro
} }
existingDepl, err := deploy.kubernetesClient.ExtensionsV1beta1().Deployments(deploy.namespace).Get(deployName, metav1.GetOptions{}) existingDepl, err := deploy.kubernetesClient.ExtensionsV1beta1().Deployments(deploy.namespace).Get(deployName, metav1.GetOptions{})
if err == nil && existingDepl.Status.ReadyReplicas >= replicas { if err == nil {
if existingDepl.Status.ReadyReplicas < replicas {
existingDepl, err = deploy.waitForDeploy(existingDepl, replicas)
}
return existingDepl, err return existingDepl, err
} }
@@ -75,18 +78,7 @@ func (deploy *NewDeploy) createOrGetDeployment(fn *crd.Function, env *crd.Enviro
return nil, err return nil, err
} }
for i := 0; i < 120; i++ { return deploy.waitForDeploy(depl, replicas)
latestDepl, err := deploy.kubernetesClient.ExtensionsV1beta1().Deployments(deploy.namespace).Get(depl.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
//TODO check for imagePullerror
if latestDepl.Status.ReadyReplicas == replicas {
return latestDepl, err
}
time.Sleep(time.Second)
}
return nil, errors.New("failed to create deployment within timeout window")
} }
return nil, err return nil, err
@@ -124,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,
@@ -201,7 +196,7 @@ func (deploy *NewDeploy) getDeploymentSpec(fn *crd.Function, env *crd.Environmen
}, },
}, },
Containers: []apiv1.Container{ Containers: []apiv1.Container{
{ fission.MergeContainerSpecs(&apiv1.Container{
Name: fn.Metadata.Name, Name: fn.Metadata.Name,
Image: env.Spec.Runtime.Image, Image: env.Spec.Runtime.Image,
ImagePullPolicy: apiv1.PullIfNotPresent, ImagePullPolicy: apiv1.PullIfNotPresent,
@@ -231,7 +226,7 @@ func (deploy *NewDeploy) getDeploymentSpec(fn *crd.Function, env *crd.Environmen
}, },
}, },
Resources: resources, Resources: resources,
}, }, env.Spec.Runtime.Container),
{ {
Name: "fetcher", Name: "fetcher",
Image: deploy.fetcherImg, Image: deploy.fetcherImg,
@@ -459,3 +454,18 @@ func (deploy *NewDeploy) deleteSvc(ns string, name string) error {
} }
return nil return nil
} }
func (deploy *NewDeploy) waitForDeploy(depl *v1beta1.Deployment, replicas int32) (*v1beta1.Deployment, error) {
for i := 0; i < 120; i++ {
latestDepl, err := deploy.kubernetesClient.ExtensionsV1beta1().Deployments(deploy.namespace).Get(depl.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
//TODO check for imagePullerror
if latestDepl.Status.ReadyReplicas >= replicas {
return latestDepl, err
}
time.Sleep(time.Second)
}
return nil, errors.New("failed to create deployment within timeout window")
}
+88 -20
View File
@@ -22,6 +22,7 @@ import (
"log" "log"
"os" "os"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -77,6 +78,7 @@ type (
const ( const (
FnCreate requestType = iota FnCreate requestType = iota
FnUpdate
FnDelete FnDelete
) )
@@ -191,11 +193,27 @@ func (deploy *NewDeploy) GetFuncSvc(metadata *metav1.ObjectMeta) (*fscache.FuncS
if err != nil { if err != nil {
return nil, err return nil, err
} }
fsvc, err := deploy.fsCache.GetByFunctionUID(metadata.UID)
// If the function service cache exists, means
// the kubeObjects of function are created before.
// In this case, return cached fsvc.
if err == nil {
return fsvc, nil
}
if !fscache.IsNotFoundError(err) {
log.Printf("error getting function service by uid: %v", err)
return nil, err
}
deploy.requestChannel <- &fnRequest{ deploy.requestChannel <- &fnRequest{
fn: fn, fn: fn,
reqType: FnCreate, reqType: FnCreate,
responseChannel: c, responseChannel: c,
} }
resp := <-c resp := <-c
if resp.error != nil { if resp.error != nil {
return nil, resp.error return nil, resp.error
@@ -224,6 +242,19 @@ func (deploy *NewDeploy) createFunction(fn *crd.Function) {
} }
} }
func (deploy *NewDeploy) updateFunction(fn *crd.Function) {
c := make(chan *fnResponse)
deploy.requestChannel <- &fnRequest{
fn: fn,
reqType: FnUpdate,
responseChannel: c,
}
resp := <-c
if resp.error != nil {
log.Printf("Error eager updating function: %v", resp.error)
}
}
func (deploy *NewDeploy) deleteFunction(fn *crd.Function) { func (deploy *NewDeploy) deleteFunction(fn *crd.Function) {
if fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fission.ExecutorTypeNewdeploy { if fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fission.ExecutorTypeNewdeploy {
c := make(chan *fnResponse) c := make(chan *fnResponse)
@@ -336,7 +367,7 @@ func (deploy *NewDeploy) fnUpdate(oldFn *crd.Function, newFn *crd.Function) {
return return
} }
changed := false deployChanged := false
if oldFn.Spec.InvokeStrategy != newFn.Spec.InvokeStrategy { if oldFn.Spec.InvokeStrategy != newFn.Spec.InvokeStrategy {
@@ -366,59 +397,63 @@ func (deploy *NewDeploy) fnUpdate(oldFn *crd.Function, newFn *crd.Function) {
return return
} }
hpaChanged := false
if newFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale != oldFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale { if newFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale != oldFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale {
replicas := int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale) replicas := int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale)
hpa.Spec.MinReplicas = &replicas hpa.Spec.MinReplicas = &replicas
changed = true // Will start deployment update deployChanged = true
hpaChanged = true
} }
if newFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale != oldFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale { if newFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale != oldFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale {
hpa.Spec.MaxReplicas = int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale) hpa.Spec.MaxReplicas = int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale)
hpaChanged = true
} }
if newFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent != oldFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent { if newFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent != oldFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent {
targetCpupercent := int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent) targetCpupercent := int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent)
hpa.Spec.TargetCPUUtilizationPercentage = &targetCpupercent hpa.Spec.TargetCPUUtilizationPercentage = &targetCpupercent
hpaChanged = true
} }
err = deploy.updateHpa(hpa) if hpaChanged {
if err != nil { err := deploy.updateHpa(hpa)
updateStatus(oldFn, err, "error updating HPA while updating function") if err != nil {
return updateStatus(oldFn, err, "error updating HPA while updating function")
return
}
} }
} }
if oldFn.Spec.Environment != newFn.Spec.Environment { if oldFn.Spec.Environment != newFn.Spec.Environment ||
changed = true oldFn.Spec.Package.PackageRef != newFn.Spec.Package.PackageRef {
} deployChanged = true
if oldFn.Spec.Package.PackageRef != newFn.Spec.Package.PackageRef {
changed = true
} }
// If length of slice has changed then no need to check individual elements // If length of slice has changed then no need to check individual elements
if len(oldFn.Spec.Secrets) != len(newFn.Spec.Secrets) { if len(oldFn.Spec.Secrets) != len(newFn.Spec.Secrets) {
changed = true deployChanged = true
} else { } else {
for i, newSecret := range newFn.Spec.Secrets { for i, newSecret := range newFn.Spec.Secrets {
if newSecret != oldFn.Spec.Secrets[i] { if newSecret != oldFn.Spec.Secrets[i] {
changed = true deployChanged = true
break break
} }
} }
} }
if len(oldFn.Spec.ConfigMaps) != len(newFn.Spec.ConfigMaps) { if len(oldFn.Spec.ConfigMaps) != len(newFn.Spec.ConfigMaps) {
changed = true deployChanged = true
} else { } else {
for i, newConfig := range newFn.Spec.ConfigMaps { for i, newConfig := range newFn.Spec.ConfigMaps {
if newConfig != oldFn.Spec.ConfigMaps[i] { if newConfig != oldFn.Spec.ConfigMaps[i] {
changed = true deployChanged = true
break break
} }
} }
} }
if changed == true { if deployChanged == true {
env, err := deploy.fissionClient.Environments(newFn.Spec.Environment.Namespace). env, err := deploy.fissionClient.Environments(newFn.Spec.Environment.Namespace).
Get(newFn.Spec.Environment.Name) Get(newFn.Spec.Environment.Name)
if err != nil { if err != nil {
@@ -438,7 +473,6 @@ func (deploy *NewDeploy) fnUpdate(oldFn *crd.Function, newFn *crd.Function) {
updateStatus(oldFn, err, "failed to update deployment while updating function") updateStatus(oldFn, err, "failed to update deployment while updating function")
return return
} }
return
} }
} }
@@ -446,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
} }
@@ -501,8 +540,37 @@ func (deploy *NewDeploy) getDeployLabels(fn *crd.Function, env *crd.Environment)
} }
} }
// updateKubeObjRefRV update the resource version of kubeObjectRef with
// given kind and return error if failed to find the reference.
func (deploy *NewDeploy) updateKubeObjRefRV(fsvc *fscache.FuncSvc, objKind string, rv string) error {
kubeObjs := fsvc.KubernetesObjects
for i, obj := range kubeObjs {
if obj.Kind == objKind {
kubeObjs[i].ResourceVersion = rv
return nil
}
}
fsvc.KubernetesObjects = kubeObjs
return errors.New(fmt.Sprintf("error finding kubernetes object reference with kind: %v", objKind))
}
// updateStatus is a function which updates status of update. // updateStatus is a function which updates status of update.
// Current implementation only logs messages, in future it will update function status // Current implementation only logs messages, in future it will update function status
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
}
+125
View File
@@ -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
}
+22 -53
View File
@@ -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{
@@ -445,14 +435,15 @@ func (gp *GenericPool) specializePod(pod *apiv1.Pod, metadata *metav1.ObjectMeta
// Receive response with non-200 http code // Receive response with non-200 http code
if err == nil { if err == nil {
err = fission.MakeErrorFromHTTP(resp2) err = fission.MakeErrorFromHTTP(resp2)
// The istio-proxy block all http requests until it's ready
// to serve traffic. Retry if istio feature is enabled.
if gp.useIstio {
retry = true
}
} }
if retry { // The istio-proxy block all http requests until it's ready
// to serve traffic. Retry if istio feature is enabled.
if gp.useIstio {
retry = true
}
if retry && i < maxRetries-1 {
time.Sleep(500 * time.Duration(2*i) * time.Millisecond) time.Sleep(500 * time.Duration(2*i) * time.Millisecond)
log.Printf("Error connecting to pod (%v), retrying", err) log.Printf("Error connecting to pod (%v), retrying", err)
continue continue
@@ -478,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)
@@ -523,7 +517,7 @@ func (gp *GenericPool) createPool() error {
}, },
}, },
Containers: []apiv1.Container{ Containers: []apiv1.Container{
{ fission.MergeContainerSpecs(&apiv1.Container{
Name: gp.env.Metadata.Name, Name: gp.env.Metadata.Name,
Image: gp.env.Spec.Runtime.Image, Image: gp.env.Spec.Runtime.Image,
ImagePullPolicy: gp.runtimeImagePullPolicy, ImagePullPolicy: gp.runtimeImagePullPolicy,
@@ -559,7 +553,7 @@ func (gp *GenericPool) createPool() error {
}, },
}, },
}, },
}, }, gp.env.Spec.Runtime.Container),
{ {
Name: "fetcher", Name: "fetcher",
Image: gp.fetcherImage, Image: gp.fetcherImage,
@@ -749,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)
@@ -808,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
View File
@@ -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
}
+16 -1
View File
@@ -1,2 +1,17 @@
#!/bin/sh #!/bin/sh
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH version=$1
if [ -z $version ]; then
version=$(git rev-parse HEAD)
fi
date=$2
if [ -z $date ]; then
date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
fi
gitcommit=$3
if [ -z $gitcommit ]; then
gitcommit=$(git rev-parse HEAD)
fi
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH -ldflags "-X github.com/fission/fission.GitCommit=$gitcommit -X github.com/fission/fission.BuildDate=$date -X github.com/fission/fission.Version=$version"
+6 -1
View File
@@ -1,12 +1,14 @@
package main package main
import ( import (
"fmt"
"log" "log"
"os" "os"
"strconv" "strconv"
"github.com/docopt/docopt-go" "github.com/docopt/docopt-go"
"github.com/fission/fission"
"github.com/fission/fission/buildermgr" "github.com/fission/fission/buildermgr"
"github.com/fission/fission/controller" "github.com/fission/fission/controller"
"github.com/fission/fission/executor" "github.com/fission/fission/executor"
@@ -120,6 +122,7 @@ Usage:
fission-bundle --builderMgr [--storageSvcUrl=<url>] [--envbuilder-namespace=<namespace>] fission-bundle --builderMgr [--storageSvcUrl=<url>] [--envbuilder-namespace=<namespace>]
fission-bundle --timer [--routerUrl=<url>] fission-bundle --timer [--routerUrl=<url>]
fission-bundle --mqt [--routerUrl=<url>] fission-bundle --mqt [--routerUrl=<url>]
fission-bundle --version
Options: Options:
--controllerPort=<port> Port that the controller should listen on. --controllerPort=<port> Port that the controller should listen on.
--routerPort=<port> Port that the router should listen on. --routerPort=<port> Port that the router should listen on.
@@ -135,8 +138,10 @@ Options:
--timer Start Timer. --timer Start Timer.
--mqt Start message queue trigger. --mqt Start message queue trigger.
--builderMgr Start builder manager. --builderMgr Start builder manager.
--version Print version information
` `
arguments, err := docopt.Parse(usage, nil, true, "fission-bundle", false) version := fmt.Sprintf("Fission Bundle Version: %v", fission.VersionInfo().String())
arguments, err := docopt.Parse(usage, nil, true, version, false)
if err != nil { if err != nil {
log.Fatalf("Error: %v", err) log.Fatalf("Error: %v", err)
} }
+13 -2
View File
@@ -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
View File
@@ -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
} }
+24 -59
View File
@@ -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 "":
@@ -510,7 +511,7 @@ func fnUpdate(c *cli.Context) error {
default: default:
fatal("Executor type must be one of 'poolmgr' or 'newdeploy', defaults to 'poolmgr'") fatal("Executor type must be one of 'poolmgr' or 'newdeploy', defaults to 'poolmgr'")
} }
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")) &&
fnExecutor == fission.ExecutorTypePoolmgr { fnExecutor == 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")
} }
@@ -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 {
+29
View File
@@ -28,6 +28,7 @@ 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"
"github.com/fission/fission/controller/client"
"github.com/fission/fission/crd" "github.com/fission/fission/crd"
) )
@@ -57,6 +58,18 @@ func getMethod(method string) string {
return "" return ""
} }
func checkFunctionExistence(fissionClient *client.Client, fnName string) {
meta := &metav1.ObjectMeta{
Name: fnName,
Namespace: metav1.NamespaceDefault,
}
_, err := fissionClient.FunctionGet(meta)
if err != nil {
fmt.Printf("function '%v' does not exist, use 'fission function create --name %v ...' to create the function\n", fnName, fnName)
}
}
func htCreate(c *cli.Context) error { func htCreate(c *cli.Context) error {
client := getClient(c.GlobalString("server")) client := getClient(c.GlobalString("server"))
@@ -68,11 +81,17 @@ 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"
} }
checkFunctionExistence(client, fnName)
// just name triggers by uuid. // just name triggers by uuid.
triggerName := uuid.NewV4().String() triggerName := uuid.NewV4().String()
@@ -91,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")
@@ -115,6 +142,8 @@ func htUpdate(c *cli.Context) error {
fatal("Nothing to update. Use --function to specify a new function.") fatal("Nothing to update. Use --function to specify a new function.")
} }
checkFunctionExistence(client, newFn)
ht, err := client.HTTPTriggerGet(&metav1.ObjectMeta{ ht, err := client.HTTPTriggerGet(&metav1.ObjectMeta{
Name: htName, Name: htName,
Namespace: metav1.NamespaceDefault, Namespace: metav1.NamespaceDefault,
+1 -23
View File
@@ -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 {
-1
View File
@@ -27,7 +27,6 @@ const (
) )
type LogDatabase interface { type LogDatabase interface {
GetPods(LogFilter) ([]string, error)
GetLogs(LogFilter) ([]LogEntry, error) GetLogs(LogFilter) ([]LogEntry, error)
} }
+71 -30
View File
@@ -17,9 +17,14 @@ limitations under the License.
package main package main
import ( import (
"fmt"
"io/ioutil"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings"
version "github.com/fission/fission"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@@ -41,31 +46,69 @@ func getKubeConfigPath() string {
return kubeConfig return kubeConfig
} }
func main() { func getFissionAPIVersion(apiUrl string) (string, error) {
app := cli.NewApp() resp, err := http.Get(apiUrl)
app.Name = "fission" if err != nil {
app.Usage = "Serverless functions for Kubernetes" return "", err
app.Version = "0.6.0" }
defer resp.Body.Close()
// fetch the FISSION_URL env variable. If not set, port-forward to controller. body, err := ioutil.ReadAll(resp.Body)
var value string if err != nil {
return "", err
}
return strings.TrimRight(string(body), "\n"), nil
}
func getServerUrl() string {
var serverUrl string
// Use FISSION_URL env variable if set; otherwise, port-forward to controller.
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) {
clientVer := version.VersionInfo().String()
fmt.Printf("Client Version: %v\n", clientVer)
serverVer, err := getFissionAPIVersion(getServerUrl())
if err != nil {
fmt.Printf("Error getting Fission API version: %v", err)
} else {
fmt.Printf("Server Version: %v", serverVer)
}
} }
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)
@@ -75,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)"}
@@ -99,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},
@@ -130,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},
@@ -140,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},
@@ -154,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},
} }
@@ -176,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},
@@ -243,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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
} }
+8
View File
@@ -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
View File
@@ -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,
}, },
+8
View File
@@ -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
+17 -12
View File
@@ -1,5 +1,5 @@
hash: 03ac7555eb1f5745e82abe42a07828ffcaac3785a77e7f37a596f30db8189e7f hash: d77d204547318863d07beea96b4e29a90f0d6c106a957dd0ba40eb0345a77e42
updated: 2018-02-08T22:40:25.586979477-08: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
@@ -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:
@@ -104,7 +108,7 @@ imports:
- name: github.com/howeyc/gopass - name: github.com/howeyc/gopass
version: bf9dde6d0d2c004a008c27aaee91170c786f6db8 version: bf9dde6d0d2c004a008c27aaee91170c786f6db8
- name: github.com/imdario/mergo - name: github.com/imdario/mergo
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc version: 163f41321a19dd09362d4c63cc2489db2015f1f4
- name: github.com/influxdata/influxdb - name: github.com/influxdata/influxdb
version: b7bb7e8359642b6e071735b50ae41f5eb343fd42 version: b7bb7e8359642b6e071735b50ae41f5eb343fd42
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: 6fdcdfbb2589e68692a68fd5b64b1b7e7c54bf05 version: 7889f37a10062ff8eb9ac11855ed51cddeaf4469
subpackages: subpackages:
- spb - spb
- util - util
@@ -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: 0965a40c0530e110459750a7fcb9cfa4910fb944 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: 80184f5f100c67bfcda6b8a848e0243fe4bc2772 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
@@ -389,4 +394,4 @@ testImports:
subpackages: subpackages:
- difflib - difflib
- name: github.com/stretchr/objx - name: github.com/stretchr/objx
version: 8a3f7159479fbc75b30357fbc48f380b7320f08e version: 8a3f7159479fbc75b30357fbc48f380b7320f08e
+4
View File
@@ -58,3 +58,7 @@ import:
version: ~1.2.1 version: ~1.2.1
- package: github.com/davecgh/go-spew - package: github.com/davecgh/go-spew
version: ~1.1.0 version: ~1.1.0
- package: github.com/imdario/mergo
version: ~0.3.2
- package: github.com/hashicorp/go-multierror
- package: github.com/hashicorp/errwrap
+58 -26
View File
@@ -14,73 +14,105 @@ Prerequisites
The ability to build docker images. The ability to build docker images.
`docker login` `docker login` with your own username; get this username added to
the fission team on dockerhub.
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
--------------- ---------------
Update the version in the source code, helm charts and docs. Update the version in the helm charts:
In the sources, the version is present in both CLI and controller:
fission/main.go
controller/api.go
In each charts, the version is in Chart.yaml, values.yaml and In each charts, the version is in Chart.yaml, values.yaml and
templates/NOTES.yaml. templates/NOTES.yaml.
Commit your changes, push them, open a PR, merge it. Commit your changes, push them, open a PR, merge it.
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
---------------- ----------------
1. cd to the top of the repo and run
1. cd to the top of the repo, create a branch:
git checkout -b vX.Y.Z
Commit & push the version changes.
2. Run
./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.
2. Generate docs, copy to the right place, push to github 3. Push docs update to github
3. Copy over charts, 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
4. Manually test an installation, just for a sanity check 5. Manually test an installation, just for a sanity check
5. 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
+87 -45
View File
@@ -8,7 +8,7 @@ BUILDDIR=$(realpath $DIR)/build
# Ensure we're on the master branch # Ensure we're on the master branch
check_branch() { check_branch() {
version=$1 local version=$1
curr_branch=$(git rev-parse --abbrev-ref HEAD) curr_branch=$(git rev-parse --abbrev-ref HEAD)
if [ $curr_branch != "v${version}" ] if [ $curr_branch != "v${version}" ]
then then
@@ -28,15 +28,22 @@ check_clean() {
# Build CLI binaries for mac/linux/windows # Build CLI binaries for mac/linux/windows
build_all_cli() { build_all_cli() {
build_cli "linux" "linux" local version=$1
build_cli "darwin" "osx" local date=$2
build_cli "windows" "windows" local gitcommit=$3
build_cli "linux" "linux" $version $date $gitcommit
build_cli "darwin" "osx" $version $date $gitcommit
build_cli "windows" "windows" $version $date $gitcommit
} }
# Build cli binary for one OS, and put it in $BUILDDIR/cli/<os>/ # Build cli binary for one OS, and put it in $BUILDDIR/cli/<os>/
build_cli() { build_cli() {
os=$1 os=$1
osName=$2 osName=$2
local version=$3
local date=$4
local gitcommit=$5
arch="amd64" # parameterize if/when we need to arch="amd64" # parameterize if/when we need to
pushd $DIR/fission pushd $DIR/fission
@@ -48,7 +55,7 @@ build_cli() {
binary=fission-cli-${osName} binary=fission-cli-${osName}
fi fi
GOOS=$os GOARCH=$arch go build -gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH -o $binary . GOOS=$os GOARCH=$arch go build -gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH -ldflags "-X github.com/fission/fission.GitCommit=$gitcommit -X github.com/fission/fission.BuildDate=$date -X github.com/fission/fission.Version=$version" -o $binary .
outdir=$BUILDDIR/cli/$osName/ outdir=$BUILDDIR/cli/$osName/
mkdir -p $outdir mkdir -p $outdir
@@ -59,12 +66,15 @@ build_cli() {
# Build fission-bundle image # Build fission-bundle image
build_fission_bundle_image() { build_fission_bundle_image() {
version=$1 local version=$1
tag=fission/fission-bundle:$version local date=$2
local gitcommit=$3
local tag=fission/fission-bundle:$version
pushd $DIR/fission-bundle pushd $DIR/fission-bundle
./build.sh ./build.sh $version $date $gitcommit
docker build -t $tag . docker build -t $tag .
docker tag $tag fission/fission-bundle:latest docker tag $tag fission/fission-bundle:latest
@@ -73,18 +83,20 @@ build_fission_bundle_image() {
# Push fission-bundle image # Push fission-bundle image
push_fission_bundle_image() { push_fission_bundle_image() {
version=$1 local version=$1
tag=fission/fission-bundle:$version local tag=fission/fission-bundle:$version
docker push $tag docker push $tag
} }
build_fetcher_image() { build_fetcher_image() {
version=$1 local version=$1
tag=fission/fetcher:$version local date=$2
local gitcommit=$3
local tag=fission/fetcher:$version
pushd $DIR/environments/fetcher/cmd pushd $DIR/environments/fetcher/cmd
./build.sh ./build.sh $version $date $gitcommit
docker build -t $tag . docker build -t $tag .
docker tag $tag fission/fetcher:latest docker tag $tag fission/fetcher:latest
@@ -92,43 +104,47 @@ build_fetcher_image() {
} }
push_fetcher_image() { push_fetcher_image() {
version=$1 local version=$1
tag=fission/fetcher:$version local tag=fission/fetcher:$version
docker push $tag docker push $tag
} }
build_builder_image() { build_builder_image() {
version=$1 local version=$1
tag=fission/builder:$version local date=$2
local gitcommit=$3
local tag=fission/builder:$version
pushd $DIR/builder/cmd pushd $DIR/builder/cmd
./build.sh ./build.sh $version $date $gitcommit
docker build -t $tag . docker build -t $tag .
docker tag $tag fission/builder:latest
popd popd
} }
push_builder_image() { push_builder_image() {
version=$1 local version=$1
tag=fission/builder:$version local tag=fission/builder:$version
docker push $tag docker push $tag
} }
build_logger_image() { build_logger_image() {
version=$1 local version=$1
tag=fission/fluentd:$version local tag=fission/fluentd:$version
pushd $DIR/logger/fluentd pushd $DIR/logger/fluentd
docker build -t $tag . docker build -t $tag .
docker tag $tag fission/fluentd:latest
popd popd
} }
push_logger_image() { push_logger_image() {
version=$1 local version=$1
tag=fission/fluentd:$version local tag=fission/fluentd:$version
docker push $tag docker push $tag
} }
@@ -138,7 +154,7 @@ build_and_push_logger_image() {
} }
build_and_push_env_image() { build_and_push_env_image() {
version=$1 local version=$1
envdir=$2 envdir=$2
imgnamebase=$3 imgnamebase=$3
imgvariant=$4 imgvariant=$4
@@ -169,7 +185,7 @@ build_and_push_env_image() {
} }
build_and_push_all_envs() { build_and_push_all_envs() {
version=$1 local version=$1
# call with version, env dir, image name base, image name variant # call with version, env dir, image name base, image name variant
build_and_push_env_image "$version" "nodejs" "node-env" "" build_and_push_env_image "$version" "nodejs" "node-env" ""
@@ -186,7 +202,7 @@ build_and_push_all_envs() {
} }
build_and_push_env_builder_image() { build_and_push_env_builder_image() {
version=$1 local version=$1
envdir=$2 envdir=$2
imgnamebase=$3 imgnamebase=$3
imgvariant=$4 imgvariant=$4
@@ -213,7 +229,7 @@ build_and_push_env_builder_image() {
} }
build_and_push_all_env_builders() { build_and_push_all_env_builders() {
version=$1 local version=$1
# call with version, env dir, image name base, image name variant # call with version, env dir, image name base, image name variant
build_and_push_env_builder_image "$version" "python" "python-builder" "" build_and_push_env_builder_image "$version" "python" "python-builder" ""
@@ -222,7 +238,7 @@ build_and_push_all_env_builders() {
} }
build_charts() { build_charts() {
version=$1 local version=$1
mkdir -p $BUILDDIR/charts mkdir -p $BUILDDIR/charts
pushd $DIR/charts pushd $DIR/charts
find . -iname *.~?~ | xargs rm find . -iname *.~?~ | xargs rm
@@ -235,12 +251,29 @@ build_charts() {
} }
build_all() { build_all() {
version=$1 local version=$1
if [ -z "$version" ] if [ -z "$version" ]
then then
echo "Version unspecified" echo "Version unspecified"
exit 1 exit 1
fi fi
local date=$2
if [ -z "$date" ]
then
echo "Build date unspecified"
exit 1
fi
local gitcommit=$3
if [ -z "gitcommit" ]
then
echo "Git commit unspecified"
exit 1
fi
if [ -e $BUILDDIR ] if [ -e $BUILDDIR ]
then then
@@ -250,26 +283,32 @@ build_all() {
mkdir -p $BUILDDIR mkdir -p $BUILDDIR
build_fission_bundle_image $version build_fission_bundle_image $version $date $gitcommit
build_fetcher_image $version build_fetcher_image $version $date $gitcommit
build_builder_image $version build_builder_image $version $date $gitcommit
build_logger_image $version build_logger_image $version
build_all_cli build_all_cli $version $date $gitcommit
build_charts $version build_charts $version
} }
push_all() { push_all() {
local version=$1
push_fission_bundle_image $version push_fission_bundle_image $version
push_fission_bundle_image latest push_fission_bundle_image latest
push_fetcher_image $version push_fetcher_image $version
push_fetcher_image latest push_fetcher_image latest
push_builder_image $version push_builder_image $version
push_builder_image latest
push_logger_image $version push_logger_image $version
push_logger_image latest
} }
tag_and_release() { tag_and_release() {
version=$1 local version=$1
gittag=$version local gittag=$version
# tag the release # tag the release
git tag $gittag git tag $gittag
@@ -288,8 +327,8 @@ tag_and_release() {
} }
attach_github_release_cli() { attach_github_release_cli() {
version=$1 local version=$1
gittag=$version local gittag=$version
# cli # cli
echo "Uploading osx cli" echo "Uploading osx cli"
gothub upload \ gothub upload \
@@ -320,8 +359,8 @@ attach_github_release_cli() {
} }
attach_github_release_charts() { attach_github_release_charts() {
version=$1 local version=$1
gittag=$version local gittag=$version
# helm charts # helm charts
gothub upload \ gothub upload \
@@ -343,7 +382,7 @@ attach_github_release_charts() {
} }
generate_changelog() { generate_changelog() {
version=$1 local version=$1
echo "# ${version}" > new_CHANGELOG.md echo "# ${version}" > new_CHANGELOG.md
echo echo
@@ -364,13 +403,13 @@ generate_changelog() {
} }
create_downloads_table () { create_downloads_table () {
release_tag=$1 local release_tag=$1
url_prefix="https://github.com/fission/fission/releases/download" local url_prefix="https://github.com/fission/fission/releases/download"
echo "## Downloads for ${version}" echo "## Downloads for ${version}"
echo echo
files=$(find build -name '*' -type f) local files=$(find build -name '*' -type f)
echo echo
echo "filename | sha256 hash" echo "filename | sha256 hash"
@@ -386,10 +425,13 @@ export GITHUB_TOKEN=$(cat ~/.gh-access-token)
version=$1 version=$1
date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
gitcommit=$(git rev-parse HEAD)
check_branch $version check_branch $version
check_clean check_clean
build_all $version build_all $version $date $gitcommit
push_all $version push_all $version
build_and_push_all_envs $version build_and_push_all_envs $version
build_and_push_all_env_builders $version build_and_push_all_env_builders $version
+2 -2
View File
@@ -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')
+8
View File
@@ -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)

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