Compare commits
17
Commits
1.7.0-rc.2
...
1.7.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9401c1ee85 | ||
|
|
5c704f053b | ||
|
|
eb1f971d52 | ||
|
|
83bc5508ee | ||
|
|
d6a8734aae | ||
|
|
3ee98cd13a | ||
|
|
275da18cf6 | ||
|
|
3f3b11ffbf | ||
|
|
7f8cb69326 | ||
|
|
003c304105 | ||
|
|
763ab475f2 | ||
|
|
4b3f48b537 | ||
|
|
6301a78814 | ||
|
|
506b427124 | ||
|
|
19ae7d5ac6 | ||
|
|
47aaa85108 | ||
|
|
3067ecdee3 |
+78
-23
@@ -1,22 +1,77 @@
|
||||
# 1.7.0-rc.1
|
||||
# 1.7.0
|
||||
[Documentation](https://docs.fission.io/)
|
||||
## Downloads for 1.7.0-rc.1
|
||||
## Downloads for 1.7.0
|
||||
|
||||
|
||||
filename | sha256 hash
|
||||
-------- | -----------
|
||||
[fission-all-1.7.0-rc.1-minikube.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-all-1.7.0-rc.1-minikube.yaml) | `360645acc1a13dfce5e739ac9cf1abd3cc0aebed612a3a010eb4db668ba9a34b`
|
||||
[fission-core-1.7.0-rc.1-minikube.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-core-1.7.0-rc.1-minikube.yaml) | `8564fe7d7acdcb17baad49f4f3c20b23bb101bd6e550a84a408eeeecceb7d00e`
|
||||
[fission-core-1.7.0-rc.1.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-core-1.7.0-rc.1.yaml) | `1e49e381b08e71454ae330eca958842957d9401a6d26bdfb028ec09338ad68fe`
|
||||
[fission-all-1.7.0-rc.1.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-all-1.7.0-rc.1.yaml) | `6d12dc90d0a4395eed2397362789843e894534ee446a08f411aa63de92f400fb`
|
||||
[fission-all-1.7.0-rc.1.tgz](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-all-1.7.0-rc.1.tgz) | `be78fd00b1a8df028a41b000838c00e7e16eea25b8a6d0014ee841b8ed73e9c7`
|
||||
[fission-core-1.7.0-rc.1.tgz](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-core-1.7.0-rc.1.tgz) | `07cb5287d0624af5ab025821a1222e764f6e9634b82003b23a46910f9de2f80a`
|
||||
[fission-cli-osx](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-cli-osx) | `d68939d54a5e9c9c4bc6bb5477e9e11591514d4c3dcf371bb4272b8f41915d00`
|
||||
[fission-cli-windows.exe](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-cli-windows.exe) | `adc30a600a150b78c8defdaae97f8d7aa4d3bc27b0dd71ff1b793e36bfc25437`
|
||||
[fission-cli-linux](https://github.com/fission/fission/releases/download/1.7.0-rc.1/fission-cli-linux) | `5b02b0271568460de8ae7d3090e89cef16031fa8ac9e3e3b179445596bbbeca5`
|
||||
[fission-core-1.7.0.yaml](https://github.com/fission/fission/releases/download/1.7.0/fission-core-1.7.0.yaml) | `e087b95a3f2adf8c97aed5ddc47a57941b0439ab9fb2253de5118b1eaa01c5e1`
|
||||
[fission-core-1.7.0-minikube.yaml](https://github.com/fission/fission/releases/download/1.7.0/fission-core-1.7.0-minikube.yaml) | `ff0b623b21c7e087e90c6030a720e83c88572c2d9e87d82faa2a1f762cba8742`
|
||||
[fission-all-1.7.0.yaml](https://github.com/fission/fission/releases/download/1.7.0/fission-all-1.7.0.yaml) | `5fc5eb9174ca74a102f172410dbc310cc29f56deb34f4490799315fe6c5913b0`
|
||||
[fission-all-1.7.0-openshift.yaml](https://github.com/fission/fission/releases/download/1.7.0/fission-all-1.7.0-openshift.yaml) | `0a93a1b2e7b3bafc7e6db826170471120af90bc17362d84daf93216da503a33e`
|
||||
[fission-all-1.7.0-minikube.yaml](https://github.com/fission/fission/releases/download/1.7.0/fission-all-1.7.0-minikube.yaml) | `21be5a7a7b95b11ca32b78f744d2c05897d2e0a3c6dfb9136936c90d3cbee0d8`
|
||||
[fission-core-1.7.0-openshift.yaml](https://github.com/fission/fission/releases/download/1.7.0/fission-core-1.7.0-openshift.yaml) | `679c9dd7482f2a799e61079149c3ede138dad8ee635faf8b325e45f58fac955d`
|
||||
[fission-core-1.7.0.tgz](https://github.com/fission/fission/releases/download/1.7.0/fission-core-1.7.0.tgz) | `c8678298c1315502833e856b61c9ef587a751007ad757ccc9106bb0f3dc78fb8`
|
||||
[fission-all-1.7.0.tgz](https://github.com/fission/fission/releases/download/1.7.0/fission-all-1.7.0.tgz) | `9849562fd8f8f3f83a3896c6a77dafe24f53d8f4f4701903d48bbfc3d365ec83`
|
||||
[fission-cli-osx](https://github.com/fission/fission/releases/download/1.7.0/fission-cli-osx) | `0ded704fae3ff10e08a7b535c4df5cbd11c2cb65a6d344c442718f6a807082fa`
|
||||
[fission-cli-windows.exe](https://github.com/fission/fission/releases/download/1.7.0/fission-cli-windows.exe) | `2ff4b77e77446836170bb5433d765cc68f48b4eef13ef7ed580a9aa0c07be64e`
|
||||
[fission-cli-linux](https://github.com/fission/fission/releases/download/1.7.0/fission-cli-linux) | `1b8a3961a5de14c414fcaaea472020707b20cb9712020e55cc70f3f0b6a220b2`
|
||||
|
||||
# Change Log
|
||||
|
||||
## [1.7.0](https://github.com/fission/fission/tree/1.7.0) (2019-12-02)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/v1.7.0...1.7.0)
|
||||
|
||||
## [v1.7.0](https://github.com/fission/fission/tree/v1.7.0) (2019-12-02)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/1.7.0-rc.2...v1.7.0)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix release script not uploads OpenShift deploy YAML file [\#1456](https://github.com/fission/fission/pull/1456) ([life1347](https://github.com/life1347))
|
||||
- Let executor type manages how to do cleanup for old kubeobjects [\#1455](https://github.com/fission/fission/pull/1455) ([life1347](https://github.com/life1347))
|
||||
- Prevent deployment from rolling update due to different instance-id [\#1454](https://github.com/fission/fission/pull/1454) ([life1347](https://github.com/life1347))
|
||||
- Make AdoptExistingResources optional [\#1453](https://github.com/fission/fission/pull/1453) ([life1347](https://github.com/life1347))
|
||||
- Prevent newdeploy updates deployment if no resources changed [\#1452](https://github.com/fission/fission/pull/1452) ([life1347](https://github.com/life1347))
|
||||
- Fix CLI unable to get pod logs from controller [\#1451](https://github.com/fission/fission/pull/1451) ([life1347](https://github.com/life1347))
|
||||
- Ignore hidden file when creating archive file [\#1450](https://github.com/fission/fission/pull/1450) ([life1347](https://github.com/life1347))
|
||||
- Fix spec init overrides existing deploymentconfig [\#1449](https://github.com/fission/fission/pull/1449) ([life1347](https://github.com/life1347))
|
||||
- Fix spec shows source archive is not used [\#1448](https://github.com/fission/fission/pull/1448) ([life1347](https://github.com/life1347))
|
||||
- Fix adopted deployment uses old fetcher image [\#1447](https://github.com/fission/fission/pull/1447) ([life1347](https://github.com/life1347))
|
||||
- Improve executor bootstrap speed [\#1446](https://github.com/fission/fission/pull/1446) ([life1347](https://github.com/life1347))
|
||||
- Fission 1.7.0-rc.2 [\#1445](https://github.com/fission/fission/pull/1445) ([life1347](https://github.com/life1347))
|
||||
|
||||
## [1.7.0-rc.2](https://github.com/fission/fission/tree/1.7.0-rc.2) (2019-11-27)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/v1.7.0-rc.2...1.7.0-rc.2)
|
||||
|
||||
## [v1.7.0-rc.2](https://github.com/fission/fission/tree/v1.7.0-rc.2) (2019-11-27)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/1.7.0-rc.1...v1.7.0-rc.2)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Push extra tag to fit go module semver tag format [\#1444](https://github.com/fission/fission/pull/1444) ([life1347](https://github.com/life1347))
|
||||
- Adopt existing orphan kubernetes resources when executor starts up [\#1443](https://github.com/fission/fission/pull/1443) ([life1347](https://github.com/life1347))
|
||||
- Revert "Try to fix flaky canary test" [\#1442](https://github.com/fission/fission/pull/1442) ([life1347](https://github.com/life1347))
|
||||
- Try to fix flaky canary test [\#1441](https://github.com/fission/fission/pull/1441) ([life1347](https://github.com/life1347))
|
||||
- Fix router tries to update ingress when createIngress is false [\#1440](https://github.com/fission/fission/pull/1440) ([life1347](https://github.com/life1347))
|
||||
- Fix poolmanager sets 0 timeout for function specialization [\#1439](https://github.com/fission/fission/pull/1439) ([life1347](https://github.com/life1347))
|
||||
- Add huge response body test [\#1437](https://github.com/fission/fission/pull/1437) ([life1347](https://github.com/life1347))
|
||||
- Return error when specialization failed [\#1436](https://github.com/fission/fission/pull/1436) ([life1347](https://github.com/life1347))
|
||||
- Fix poolmanager terminates running function pod periodically [\#1435](https://github.com/fission/fission/pull/1435) ([life1347](https://github.com/life1347))
|
||||
- Allow to tap multiple function services at one time [\#1434](https://github.com/fission/fission/pull/1434) ([life1347](https://github.com/life1347))
|
||||
- Collect function metrics after finishing request [\#1433](https://github.com/fission/fission/pull/1433) ([life1347](https://github.com/life1347))
|
||||
- Fix poolmanager crashes when failed to list environment [\#1432](https://github.com/fission/fission/pull/1432) ([life1347](https://github.com/life1347))
|
||||
- Ability to pull builder image from private registry [\#1431](https://github.com/fission/fission/pull/1431) ([life1347](https://github.com/life1347))
|
||||
- Add checksum and insecure flag for user to skip checksum generation [\#1430](https://github.com/fission/fission/pull/1430) ([life1347](https://github.com/life1347))
|
||||
- Support to set imagePullSecret when creating environment [\#1429](https://github.com/fission/fission/pull/1429) ([life1347](https://github.com/life1347))
|
||||
- Fix no kubeobjs get created if fn created before env creation [\#1428](https://github.com/fission/fission/pull/1428) ([life1347](https://github.com/life1347))
|
||||
- Fix verbosity flag not found in subcommand [\#1425](https://github.com/fission/fission/pull/1425) ([life1347](https://github.com/life1347))
|
||||
- Improve compatibility with Openshift [\#1424](https://github.com/fission/fission/pull/1424) ([life1347](https://github.com/life1347))
|
||||
- Fix truncated body returned from router [\#1420](https://github.com/fission/fission/pull/1420) ([life1347](https://github.com/life1347))
|
||||
- Fission 1.7.0-rc.1 [\#1419](https://github.com/fission/fission/pull/1419) ([life1347](https://github.com/life1347))
|
||||
|
||||
## [1.7.0-rc.1](https://github.com/fission/fission/tree/1.7.0-rc.1) (2019-11-18)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/v1.7.0-rc.1...1.7.0-rc.1)
|
||||
|
||||
## [v1.7.0-rc.1](https://github.com/fission/fission/tree/v1.7.0-rc.1) (2019-11-18)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/1.6.0...v1.7.0-rc.1)
|
||||
|
||||
@@ -155,8 +210,10 @@ filename | sha256 hash
|
||||
- Update tensorflow serving image name in env README [\#1256](https://github.com/fission/fission/pull/1256) ([life1347](https://github.com/life1347))
|
||||
- V1.4.1 [\#1255](https://github.com/fission/fission/pull/1255) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Fix environment version validation [\#1253](https://github.com/fission/fission/pull/1253) ([davidsmf](https://github.com/davidsmf))
|
||||
- Allow to set deployment config uid during initialization [\#1249](https://github.com/fission/fission/pull/1249) ([life1347](https://github.com/life1347))
|
||||
- Add swagger \(OpenAPI 2.0\) support [\#1245](https://github.com/fission/fission/pull/1245) ([life1347](https://github.com/life1347))
|
||||
- Update go dependencies [\#1240](https://github.com/fission/fission/pull/1240) ([life1347](https://github.com/life1347))
|
||||
- Allow to set log level through environment variable [\#1217](https://github.com/fission/fission/pull/1217) ([life1347](https://github.com/life1347))
|
||||
|
||||
## [1.4.1](https://github.com/fission/fission/tree/1.4.1) (2019-07-29)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/1.4.0...1.4.1)
|
||||
@@ -165,7 +222,6 @@ filename | sha256 hash
|
||||
|
||||
- Fix wrongly replace spec api version [\#1254](https://github.com/fission/fission/pull/1254) ([life1347](https://github.com/life1347))
|
||||
- Revert change of product name in README [\#1250](https://github.com/fission/fission/pull/1250) ([davidsmf](https://github.com/davidsmf))
|
||||
- Allow to set deployment config uid during initialization [\#1249](https://github.com/fission/fission/pull/1249) ([life1347](https://github.com/life1347))
|
||||
- Analytics env fix in chart [\#1247](https://github.com/fission/fission/pull/1247) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Fix CI unable to start test due to the same travis build ID [\#1246](https://github.com/fission/fission/pull/1246) ([life1347](https://github.com/life1347))
|
||||
- V1.4.0 [\#1244](https://github.com/fission/fission/pull/1244) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
@@ -187,7 +243,6 @@ filename | sha256 hash
|
||||
- Function Update if config/secret changes [\#1224](https://github.com/fission/fission/pull/1224) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Fix nil pointer when CLI unable to get server version [\#1223](https://github.com/fission/fission/pull/1223) ([life1347](https://github.com/life1347))
|
||||
- Reuse go docker build cache [\#1218](https://github.com/fission/fission/pull/1218) ([life1347](https://github.com/life1347))
|
||||
- Allow to set log level through environment variable [\#1217](https://github.com/fission/fission/pull/1217) ([life1347](https://github.com/life1347))
|
||||
- Fix roundtripper doesn't increase request timeout setting after each retry [\#1216](https://github.com/fission/fission/pull/1216) ([life1347](https://github.com/life1347))
|
||||
- Configmaps/secrets in function exist check [\#1214](https://github.com/fission/fission/pull/1214) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Add experimental environment: tensorflow-serving [\#1212](https://github.com/fission/fission/pull/1212) ([life1347](https://github.com/life1347))
|
||||
@@ -200,7 +255,6 @@ filename | sha256 hash
|
||||
- Fallback to get user home directory from env [\#1203](https://github.com/fission/fission/pull/1203) ([life1347](https://github.com/life1347))
|
||||
- Fission v1.3.0 [\#1202](https://github.com/fission/fission/pull/1202) ([life1347](https://github.com/life1347))
|
||||
- \[bugfix\] Fix CLI drops controller URL path when querying logs [\#1201](https://github.com/fission/fission/pull/1201) ([moluzhang](https://github.com/moluzhang))
|
||||
- Enable go module support for go environment [\#1152](https://github.com/fission/fission/pull/1152) ([life1347](https://github.com/life1347))
|
||||
|
||||
## [1.3.0](https://github.com/fission/fission/tree/1.3.0) (2019-06-03)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/1.2.1...1.3.0)
|
||||
@@ -211,7 +265,9 @@ filename | sha256 hash
|
||||
- Add readiness/liveness probes to nat-streaming [\#1199](https://github.com/fission/fission/pull/1199) ([life1347](https://github.com/life1347))
|
||||
- Update bug issue templates [\#1198](https://github.com/fission/fission/pull/1198) ([life1347](https://github.com/life1347))
|
||||
- Add static code analysis to CI test [\#1197](https://github.com/fission/fission/pull/1197) ([life1347](https://github.com/life1347))
|
||||
- Analytics bugfix [\#1195](https://github.com/fission/fission/pull/1195) ([soamvasani](https://github.com/soamvasani))
|
||||
- Add Terraform configuration and upgrade helm version [\#1194](https://github.com/fission/fission/pull/1194) ([darkgerm](https://github.com/darkgerm))
|
||||
- Show warning message if spec alters poolsize while env version \< 3 [\#1193](https://github.com/fission/fission/pull/1193) ([life1347](https://github.com/life1347))
|
||||
- Move packages to proejct/pkg to follow go project folder structure convention [\#1190](https://github.com/fission/fission/pull/1190) ([life1347](https://github.com/life1347))
|
||||
- router analytics -- close http response body [\#1180](https://github.com/fission/fission/pull/1180) ([soamvasani](https://github.com/soamvasani))
|
||||
- Remove prometheus server connectivity test during controller initialization [\#1179](https://github.com/fission/fission/pull/1179) ([life1347](https://github.com/life1347))
|
||||
@@ -221,6 +277,7 @@ filename | sha256 hash
|
||||
- Refactor test framework [\#1128](https://github.com/fission/fission/pull/1128) ([darkgerm](https://github.com/darkgerm))
|
||||
- Pod specs [\#1106](https://github.com/fission/fission/pull/1106) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Allow non-toplevel modules in python environment [\#1042](https://github.com/fission/fission/pull/1042) ([soamvasani](https://github.com/soamvasani))
|
||||
- Created dotnet2.0 Builder Image and Added /v2/specialized Endpoint to dotnet2.0 Envrionment [\#1001](https://github.com/fission/fission/pull/1001) ([paraspatidar](https://github.com/paraspatidar))
|
||||
|
||||
## [1.2.1](https://github.com/fission/fission/tree/1.2.1) (2019-05-09)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/1.2.0...1.2.1)
|
||||
@@ -236,13 +293,12 @@ filename | sha256 hash
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Analytics bugfix [\#1195](https://github.com/fission/fission/pull/1195) ([soamvasani](https://github.com/soamvasani))
|
||||
- Show warning message if spec alters poolsize while env version \< 3 [\#1193](https://github.com/fission/fission/pull/1193) ([life1347](https://github.com/life1347))
|
||||
- DRY up fetcher configuration [\#1168](https://github.com/fission/fission/pull/1168) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Add simple anonymous usage metrics [\#1167](https://github.com/fission/fission/pull/1167) ([soamvasani](https://github.com/soamvasani))
|
||||
- Fix the logger not working [\#1166](https://github.com/fission/fission/pull/1166) ([darkgerm](https://github.com/darkgerm))
|
||||
- Change log level in executor for better log reading/troubleshooting [\#1163](https://github.com/fission/fission/pull/1163) ([life1347](https://github.com/life1347))
|
||||
- Fix TravisCI go environment version to avoid go bugs [\#1154](https://github.com/fission/fission/pull/1154) ([life1347](https://github.com/life1347))
|
||||
- Enable go module support for go environment [\#1152](https://github.com/fission/fission/pull/1152) ([life1347](https://github.com/life1347))
|
||||
- \#1132 nodejs environment, increase body size [\#1149](https://github.com/fission/fission/pull/1149) ([JannikZed](https://github.com/JannikZed))
|
||||
- Added php builder to release script fixes \#1140 [\#1145](https://github.com/fission/fission/pull/1145) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Using templated imagePullPolicy for containers in deployment.yaml [\#1137](https://github.com/fission/fission/pull/1137) ([msshroff](https://github.com/msshroff))
|
||||
@@ -274,7 +330,6 @@ filename | sha256 hash
|
||||
- Added draft proposal for CI/CD [\#1084](https://github.com/fission/fission/pull/1084) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- \[Kafka MQT\] Add warning about Kafka version [\#1083](https://github.com/fission/fission/pull/1083) ([bhavin192](https://github.com/bhavin192))
|
||||
- Bump base image version of Go environment to 1.11.4 [\#1026](https://github.com/fission/fission/pull/1026) ([life1347](https://github.com/life1347))
|
||||
- Created dotnet2.0 Builder Image and Added /v2/specialized Endpoint to dotnet2.0 Envrionment [\#1001](https://github.com/fission/fission/pull/1001) ([paraspatidar](https://github.com/paraspatidar))
|
||||
|
||||
## [1.0.0](https://github.com/fission/fission/tree/1.0.0) (2019-02-13)
|
||||
[Full Changelog](https://github.com/fission/fission/compare/1.0...1.0.0)
|
||||
@@ -391,6 +446,7 @@ filename | sha256 hash
|
||||
**Merged pull requests:**
|
||||
|
||||
- Print status with the get option. [\#907](https://github.com/fission/fission/pull/907) ([smruthi2187](https://github.com/smruthi2187))
|
||||
- Fixed the spec validation UX issue [\#898](https://github.com/fission/fission/pull/898) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Check CRD creation error instead of doing return directly [\#897](https://github.com/fission/fission/pull/897) ([life1347](https://github.com/life1347))
|
||||
- Fix failed to find release-builder dockerfile & push specific tag [\#870](https://github.com/fission/fission/pull/870) ([life1347](https://github.com/life1347))
|
||||
- V0.10.0 [\#868](https://github.com/fission/fission/pull/868) ([life1347](https://github.com/life1347))
|
||||
@@ -411,7 +467,6 @@ filename | sha256 hash
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fixed the spec validation UX issue [\#898](https://github.com/fission/fission/pull/898) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Fix CLI failed to setup port-forward caused by \#712 [\#867](https://github.com/fission/fission/pull/867) ([life1347](https://github.com/life1347))
|
||||
- Replay recorded requests by ReqUID [\#864](https://github.com/fission/fission/pull/864) ([Amusement](https://github.com/Amusement))
|
||||
- Add cleanup function to test scripts [\#863](https://github.com/fission/fission/pull/863) ([life1347](https://github.com/life1347))
|
||||
@@ -647,8 +702,10 @@ filename | sha256 hash
|
||||
- Add readiness probe to go env [\#461](https://github.com/fission/fission/pull/461) ([life1347](https://github.com/life1347))
|
||||
- Fix fission bundle build failure [\#456](https://github.com/fission/fission/pull/456) ([life1347](https://github.com/life1347))
|
||||
- Convert build.sh to a multi-stage Dockerfile. [\#452](https://github.com/fission/fission/pull/452) ([justinbarrick](https://github.com/justinbarrick))
|
||||
- Fix python environment failed to launch [\#451](https://github.com/fission/fission/pull/451) ([life1347](https://github.com/life1347))
|
||||
- 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 fission/builder image [\#397](https://github.com/fission/fission/pull/397) ([erwinvaneyk](https://github.com/erwinvaneyk))
|
||||
- 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))
|
||||
@@ -666,7 +723,6 @@ filename | sha256 hash
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix python environment failed to launch [\#451](https://github.com/fission/fission/pull/451) ([life1347](https://github.com/life1347))
|
||||
- use time.Since instead of time.Now\(\).Sub [\#449](https://github.com/fission/fission/pull/449) ([wgliang](https://github.com/wgliang))
|
||||
- Fix fission function logs [\#448](https://github.com/fission/fission/pull/448) ([prithviramesh](https://github.com/prithviramesh))
|
||||
- Integration test improvements [\#447](https://github.com/fission/fission/pull/447) ([soamvasani](https://github.com/soamvasani))
|
||||
@@ -685,9 +741,7 @@ filename | sha256 hash
|
||||
- Executor API panics if there is err in getting function from backends [\#407](https://github.com/fission/fission/pull/407) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- fission function logs returns logs in correct order now [\#405](https://github.com/fission/fission/pull/405) ([prithviramesh](https://github.com/prithviramesh))
|
||||
- Fetcher retry [\#403](https://github.com/fission/fission/pull/403) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Add fission/builder image [\#397](https://github.com/fission/fission/pull/397) ([erwinvaneyk](https://github.com/erwinvaneyk))
|
||||
- Changed podName to a generic objectReference in cache implementation [\#391](https://github.com/fission/fission/pull/391) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
- Add package command [\#385](https://github.com/fission/fission/pull/385) ([life1347](https://github.com/life1347))
|
||||
- Executor abstraction [\#384](https://github.com/fission/fission/pull/384) ([vishal-biyani](https://github.com/vishal-biyani))
|
||||
|
||||
## [0.4.0](https://github.com/fission/fission/tree/0.4.0) (2017-11-15)
|
||||
@@ -698,8 +752,8 @@ filename | sha256 hash
|
||||
- Added python example to demonstrate status codes. [\#395](https://github.com/fission/fission/pull/395) ([c0dyhi11](https://github.com/c0dyhi11))
|
||||
- created weather.js in node.js examples, modified README.md [\#394](https://github.com/fission/fission/pull/394) ([svicenteruiz](https://github.com/svicenteruiz))
|
||||
- Delete failed helm releases to prevent test case failure [\#393](https://github.com/fission/fission/pull/393) ([life1347](https://github.com/life1347))
|
||||
- Added AWS to install cloud setup [\#392](https://github.com/fission/fission/pull/392) ([joshkelly](https://github.com/joshkelly))
|
||||
- Fix functionReferenceResolver return out-of-date function metadata [\#390](https://github.com/fission/fission/pull/390) ([life1347](https://github.com/life1347))
|
||||
- Add package command [\#385](https://github.com/fission/fission/pull/385) ([life1347](https://github.com/life1347))
|
||||
- changes made to FluentD configuration to circumvent Logger daemonset [\#380](https://github.com/fission/fission/pull/380) ([prithviramesh](https://github.com/prithviramesh))
|
||||
|
||||
## [0.4.0rc](https://github.com/fission/fission/tree/0.4.0rc) (2017-11-08)
|
||||
@@ -707,6 +761,7 @@ filename | sha256 hash
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Added AWS to install cloud setup [\#392](https://github.com/fission/fission/pull/392) ([joshkelly](https://github.com/joshkelly))
|
||||
- Use store to sync functions/triggers for fast synchronization [\#382](https://github.com/fission/fission/pull/382) ([life1347](https://github.com/life1347))
|
||||
- Switch from ThirdPartyResources to CustomResourceDefinitions [\#381](https://github.com/fission/fission/pull/381) ([life1347](https://github.com/life1347))
|
||||
- changed helm install pullPolicy from Always to IfNotPresent when building local docker image [\#378](https://github.com/fission/fission/pull/378) ([prithviramesh](https://github.com/prithviramesh))
|
||||
@@ -929,7 +984,6 @@ filename | sha256 hash
|
||||
- Readme updates [\#29](https://github.com/fission/fission/pull/29) ([soamvasani](https://github.com/soamvasani))
|
||||
- Add "fission function edit \<function\>" command [\#28](https://github.com/fission/fission/pull/28) ([soamvasani](https://github.com/soamvasani))
|
||||
- Move client-go dependency to 1.5 [\#27](https://github.com/fission/fission/pull/27) ([soamvasani](https://github.com/soamvasani))
|
||||
- fission-bundle: executable package for router, controller, poolmgr [\#17](https://github.com/fission/fission/pull/17) ([soamvasani](https://github.com/soamvasani))
|
||||
|
||||
## [kubecon](https://github.com/fission/fission/tree/kubecon) (2016-11-11)
|
||||
**Merged pull requests:**
|
||||
@@ -937,6 +991,7 @@ filename | sha256 hash
|
||||
- Reap idle pods [\#20](https://github.com/fission/fission/pull/20) ([soamvasani](https://github.com/soamvasani))
|
||||
- Fission CLI [\#19](https://github.com/fission/fission/pull/19) ([soamvasani](https://github.com/soamvasani))
|
||||
- Fix resource store errors on empty db [\#18](https://github.com/fission/fission/pull/18) ([soamvasani](https://github.com/soamvasani))
|
||||
- fission-bundle: executable package for router, controller, poolmgr [\#17](https://github.com/fission/fission/pull/17) ([soamvasani](https://github.com/soamvasani))
|
||||
- Router integration with poolmgr and controller [\#16](https://github.com/fission/fission/pull/16) ([soamvasani](https://github.com/soamvasani))
|
||||
- Poolmgr -- manage generic containers and their specialization [\#15](https://github.com/fission/fission/pull/15) ([soamvasani](https://github.com/soamvasani))
|
||||
- Fetcher is a helper for function run containers [\#14](https://github.com/fission/fission/pull/14) ([soamvasani](https://github.com/soamvasani))
|
||||
|
||||
+3
-2
@@ -43,10 +43,10 @@ Parameter | Description | Default
|
||||
`routerServiceType` | Type of Fission Router service to use. For minikube, set this to NodePort, elsewhere use LoadBalancer or ClusterIP. | `LoadBalancer`
|
||||
`repository` | Image base repository | `index.docker.io`
|
||||
`image` | Fission image repository | `fission/fission-bundle`
|
||||
`imageTag` | Fission image tag | `1.7.0-rc.2`
|
||||
`imageTag` | Fission image tag | `1.7.1`
|
||||
`pullPolicy` | Image pull policy | `IfNotPresent`
|
||||
`fetcherImage` | Fission fetcher repository | `fission/fetcher`
|
||||
`fetcherImageTag` | Fission fetcher image tag | `1.7.0-rc.2`
|
||||
`fetcherImageTag` | Fission fetcher image tag | `1.7.1`
|
||||
`controllerPort` | Fission Controller service port | `31313`
|
||||
`routerPort` | Fission Router service port | ` 31314`
|
||||
`functionNamespace` | Namespace in which to run fission functions (this is different from the release namespace) | `fission-function`
|
||||
@@ -66,6 +66,7 @@ Parameter | Description | Default
|
||||
`prometheus.serviceEndpoint` | If prometheus.enabled is false, please assign the prometheus service URL that is accessible by components. | `nil`
|
||||
`canaryDeployment.enabled` | Set to true if you need canary deployment feature | `true` in `fission-all`, `false` in `fission-core`
|
||||
`extraCoreComponentPodConfig` | Extend the container specs for the core fission pods. Can be used to add things like affinty/tolerations/nodeSelectors/etc. | None
|
||||
`executor.adoptExistingResources` | If true, executor will try to adopt existing resources created by the old executor instance. | `false`
|
||||
`router.deployAsDaemonSet` | Deploy router as DaemonSet instead of Deployment | `false`
|
||||
`router.svcAddressMaxRetries` | Max retries times for router to retry on a certain service URL returns from cache/executor | `5`
|
||||
`router.svcAddressUpdateTimeout` | The length of update lock expiry time for router to get a service URL returns from executor | `30`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: v1
|
||||
name: fission-all
|
||||
version: 1.7.0-rc.2
|
||||
version: 1.7.1
|
||||
description: Fission is a fast serverless framework for Kubernetes.
|
||||
keywords:
|
||||
- fission
|
||||
@@ -12,4 +12,4 @@ maintainers:
|
||||
- name: Ta Ching Chen
|
||||
email: hello@tachingchen.com
|
||||
engine: gotpl
|
||||
appVersion: 1.7.0-rc.2
|
||||
appVersion: 1.7.1
|
||||
|
||||
@@ -215,6 +215,8 @@ spec:
|
||||
value: "{{ .Values.pullPolicy }}"
|
||||
- name: RUNTIME_IMAGE_PULL_POLICY
|
||||
value: "{{ .Values.pullPolicy }}"
|
||||
- name: ADOPT_EXISTING_RESOURCES
|
||||
value: {{ .Values.executor.adoptExistingResources | default false | quote }}
|
||||
- name: ENABLE_ISTIO
|
||||
value: "{{ .Values.enableIstio }}"
|
||||
- name: TRACE_JAEGER_COLLECTOR_ENDPOINT
|
||||
|
||||
@@ -20,13 +20,13 @@ image: fission/fission-bundle
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## Fission image version
|
||||
imageTag: 1.7.0-rc.2
|
||||
imageTag: 1.7.1
|
||||
|
||||
## Fission fetcher repository
|
||||
fetcherImage: fission/fetcher
|
||||
|
||||
## Fission fetcher image version
|
||||
fetcherImageTag: 1.7.0-rc.2
|
||||
fetcherImageTag: 1.7.1
|
||||
|
||||
## Port at which Fission controller service should be exposed
|
||||
controllerPort: 31313
|
||||
@@ -65,6 +65,9 @@ logger:
|
||||
## security context and set privileged to true.
|
||||
enableSecurityContext: false
|
||||
|
||||
executor:
|
||||
adoptExistingResources: false
|
||||
|
||||
## Router config
|
||||
router:
|
||||
deployAsDaemonSet: false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: v1
|
||||
name: fission-core
|
||||
version: 1.7.0-rc.2
|
||||
version: 1.7.1
|
||||
description: Fission is a fast serverless framework for Kubernetes.
|
||||
keywords:
|
||||
- fission
|
||||
@@ -12,4 +12,4 @@ maintainers:
|
||||
- name: Ta Ching Chen
|
||||
email: hello@tachingchen.com
|
||||
engine: gotpl
|
||||
appVersion: 1.7.0-rc.2
|
||||
appVersion: 1.7.1
|
||||
|
||||
@@ -217,6 +217,8 @@ spec:
|
||||
value: "{{ .Values.traceCollectorEndpoint }}"
|
||||
- name: TRACING_SAMPLING_RATE
|
||||
value: {{ .Values.traceSamplingRate | default "0.5" | quote }}
|
||||
- name: ADOPT_EXISTING_RESOURCES
|
||||
value: {{ .Values.executor.adoptExistingResources | default false | quote }}
|
||||
- name: ENABLE_ISTIO
|
||||
value: "{{ .Values.enableIstio }}"
|
||||
- name: FETCHER_MINCPU
|
||||
|
||||
@@ -17,7 +17,7 @@ repository: index.docker.io
|
||||
image: fission/fission-bundle
|
||||
|
||||
## Fission image version
|
||||
imageTag: 1.7.0-rc.2
|
||||
imageTag: 1.7.1
|
||||
|
||||
## Image pull policy
|
||||
pullPolicy: IfNotPresent
|
||||
@@ -26,7 +26,7 @@ pullPolicy: IfNotPresent
|
||||
fetcherImage: fission/fetcher
|
||||
|
||||
## Fission fetcher image version
|
||||
fetcherImageTag: 1.7.0-rc.2
|
||||
fetcherImageTag: 1.7.1
|
||||
|
||||
## Port at which Fission controller service should be exposed
|
||||
controllerPort: 31313
|
||||
@@ -45,6 +45,9 @@ builderNamespace: fission-builder
|
||||
## Enable istio integration
|
||||
enableIstio: false
|
||||
|
||||
executor:
|
||||
adoptExistingResources: false
|
||||
|
||||
## Router config
|
||||
router:
|
||||
deployAsDaemonSet: false
|
||||
|
||||
+11
-3
@@ -204,7 +204,7 @@ attach_github_release_cli() {
|
||||
--replace \
|
||||
--user fission \
|
||||
--repo fission \
|
||||
--tag $gittag \
|
||||
--tag $gittag \
|
||||
--name fission-cli-windows.exe \
|
||||
--file $BUILDDIR/cli/windows/fission-cli-windows.exe
|
||||
}
|
||||
@@ -218,7 +218,7 @@ attach_github_release_charts() {
|
||||
--replace \
|
||||
--user fission \
|
||||
--repo fission \
|
||||
--tag $gittag \
|
||||
--tag $gittag \
|
||||
--name fission-all-$version.tgz \
|
||||
--file $BUILDDIR/charts/fission-all-$version.tgz
|
||||
|
||||
@@ -226,7 +226,7 @@ attach_github_release_charts() {
|
||||
--replace \
|
||||
--user fission \
|
||||
--repo fission \
|
||||
--tag $gittag \
|
||||
--tag $gittag \
|
||||
--name fission-core-$version.tgz \
|
||||
--file $BUILDDIR/charts/fission-core-$version.tgz
|
||||
|
||||
@@ -254,6 +254,14 @@ attach_github_release_yamls() {
|
||||
--tag $gittag \
|
||||
--name ${c}-${version}.yaml \
|
||||
--file $BUILDDIR/yamls/${c}-${version}.yaml
|
||||
|
||||
gothub upload \
|
||||
--replace \
|
||||
--user fission \
|
||||
--repo fission \
|
||||
--tag $gittag \
|
||||
--name ${c}-${version}-openshift.yaml \
|
||||
--file $BUILDDIR/yamls/${c}-${version}-openshift.yaml
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,12 @@ package v1
|
||||
|
||||
const (
|
||||
EXECUTOR_INSTANCEID_LABEL string = "executorInstanceId"
|
||||
POOLMGR_INSTANCEID_LABEL string = "poolmgrInstanceId"
|
||||
DEFAULT_FUNCTION_TIMEOUT int = 60
|
||||
)
|
||||
|
||||
const (
|
||||
//LastUpdateTimestamp env variable is used for updating configmaps and secrets in pods
|
||||
LastUpdateTimestamp string = "LASTUPDATE_TIMESTAMP"
|
||||
// ResourceVersionCount env variable is used for updating configmaps and secrets in pods
|
||||
ResourceVersionCount string = "RESOURCE_VERSION_COUNT"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -489,15 +489,18 @@ func (envw *environmentWatcher) createBuilderDeployment(env *fv1.Environment, ns
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podSpec := apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{*container},
|
||||
ServiceAccountName: "fission-builder",
|
||||
pod := apiv1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: sel,
|
||||
Annotations: podAnnotations,
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{*container},
|
||||
ServiceAccountName: "fission-builder",
|
||||
},
|
||||
}
|
||||
|
||||
finalPodSpec, err := util.MergePodSpec(&podSpec, env.Spec.Builder.PodSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pod.Spec = *(util.ApplyImagePullSecret(env.Spec.ImagePullSecret, pod.Spec))
|
||||
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -510,13 +513,7 @@ func (envw *environmentWatcher) createBuilderDeployment(env *fv1.Environment, ns
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: sel,
|
||||
},
|
||||
Template: apiv1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: sel,
|
||||
Annotations: podAnnotations,
|
||||
},
|
||||
Spec: *(util.ApplyImagePullSecret(env.Spec.ImagePullSecret, *finalPodSpec)),
|
||||
},
|
||||
Template: pod,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -525,11 +522,20 @@ func (envw *environmentWatcher) createBuilderDeployment(env *fv1.Environment, ns
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envw.logger.Info("creating builder deployment", zap.String("deployment", name))
|
||||
if env.Spec.Builder.PodSpec != nil {
|
||||
newPodSpec, err := util.MergePodSpec(&deployment.Spec.Template.Spec, env.Spec.Builder.PodSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deployment.Spec.Template.Spec = *newPodSpec
|
||||
}
|
||||
|
||||
_, err = envw.kubernetesClient.AppsV1().Deployments(ns).Create(deployment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envw.logger.Info("creating builder deployment", zap.String("deployment", name))
|
||||
|
||||
return deployment, nil
|
||||
}
|
||||
|
||||
@@ -19,10 +19,15 @@ package client
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||
"github.com/fission/fission/pkg/fission-cli/console"
|
||||
)
|
||||
|
||||
func (c *Client) FunctionCreate(f *fv1.Function) (*metav1.ObjectMeta, error) {
|
||||
@@ -152,3 +157,29 @@ func (c *Client) FunctionList(functionNamespace string) ([]fv1.Function, error)
|
||||
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
func (c *Client) FunctionPodLogs(m *metav1.ObjectMeta) (io.ReadCloser, int, error) {
|
||||
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
|
||||
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
|
||||
|
||||
queryURL, err := url.Parse(c.Url)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrapf(err, "error parsing the base URL '%v'", c.Url)
|
||||
}
|
||||
queryURL.Path = fmt.Sprintf("/proxy/logs/%s", m.Name)
|
||||
|
||||
console.Verbose(2, fmt.Sprintf("Try to get pod logs from controller '%v'", queryURL.String()))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, "error creating logs request")
|
||||
}
|
||||
|
||||
httpClient := http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, "error executing get logs request")
|
||||
}
|
||||
|
||||
return resp.Body, resp.StatusCode, nil
|
||||
}
|
||||
|
||||
@@ -18,12 +18,15 @@ package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
restfulspec "github.com/emicklei/go-restful-openapi"
|
||||
@@ -33,10 +36,12 @@ import (
|
||||
"go.uber.org/zap"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||
ferror "github.com/fission/fission/pkg/error"
|
||||
"github.com/fission/fission/pkg/types"
|
||||
)
|
||||
|
||||
func RegisterFunctionRoute(ws *restful.WebService) {
|
||||
@@ -280,27 +285,35 @@ func (a *API) FunctionLogsApiPost(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *API) FunctionPodLogs(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
fnName := vars["function"]
|
||||
ns := vars["namespace"]
|
||||
|
||||
ns := a.extractQueryParamFromRequest(r, "namespace")
|
||||
podNs := "fission-function"
|
||||
|
||||
if len(ns) == 0 {
|
||||
ns = "fission-function"
|
||||
ns = metav1.NamespaceDefault
|
||||
} else if ns != metav1.NamespaceDefault {
|
||||
// If the function namespace is "default", executor
|
||||
// will create function pods under "fission-function".
|
||||
// Otherwise, the function pod will be created under
|
||||
// the same namespace of function.
|
||||
podNs = ns
|
||||
}
|
||||
|
||||
f, err := a.fissionClient.Functions(ns).Get(fnName)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
envName := f.Spec.Environment.Name
|
||||
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get function Pods first
|
||||
selector := "functionName=" + fnName
|
||||
podList, err := a.kubernetesClient.CoreV1().Pods(ns).List(metav1.ListOptions{LabelSelector: selector})
|
||||
selector := map[string]string{
|
||||
types.FUNCTION_UID: string(f.Metadata.UID),
|
||||
types.ENVIRONMENT_NAME: f.Spec.Environment.Name,
|
||||
types.ENVIRONMENT_NAMESPACE: f.Spec.Environment.Namespace,
|
||||
}
|
||||
podList, err := a.kubernetesClient.CoreV1().Pods(podNs).List(metav1.ListOptions{
|
||||
LabelSelector: labels.Set(selector).AsSelector().String(),
|
||||
})
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
@@ -309,30 +322,47 @@ func (a *API) FunctionPodLogs(w http.ResponseWriter, r *http.Request) {
|
||||
// Get the logs for last Pod executed
|
||||
pods := podList.Items
|
||||
sort.Slice(pods, func(i, j int) bool {
|
||||
itime := pods[i].ObjectMeta.CreationTimestamp.Time
|
||||
jtime := pods[j].ObjectMeta.CreationTimestamp.Time
|
||||
return itime.After(jtime)
|
||||
rv1, _ := strconv.ParseInt(pods[i].ObjectMeta.ResourceVersion, 10, 32)
|
||||
rv2, _ := strconv.ParseInt(pods[j].ObjectMeta.ResourceVersion, 10, 32)
|
||||
return rv1 > rv2
|
||||
})
|
||||
|
||||
podLogOpts := apiv1.PodLogOptions{Container: envName} // Only the env container, not fetcher
|
||||
var podLogsReq *restclient.Request
|
||||
if len(pods) > 0 {
|
||||
podLogsReq = a.kubernetesClient.CoreV1().Pods(ns).GetLogs(pods[0].ObjectMeta.Name, &podLogOpts)
|
||||
} else {
|
||||
if len(pods) <= 0 {
|
||||
a.respondWithError(w, errors.New("no active pods found"))
|
||||
return
|
||||
}
|
||||
|
||||
podLogs, err := podLogsReq.Stream()
|
||||
// get the pod with highest resource version
|
||||
err = getContainerLog(a.kubernetesClient, w, f, &pods[0])
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
return
|
||||
}
|
||||
defer podLogs.Close()
|
||||
|
||||
_, err = io.Copy(w, podLogs)
|
||||
if err != nil {
|
||||
a.respondWithError(w, err)
|
||||
a.respondWithError(w, errors.Wrapf(err, "error getting container logs"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getContainerLog(kubernetesClient *kubernetes.Clientset, w http.ResponseWriter, fn *fv1.Function, pod *apiv1.Pod) error {
|
||||
seq := strings.Repeat("=", 35)
|
||||
|
||||
for _, container := range pod.Spec.Containers {
|
||||
podLogOpts := apiv1.PodLogOptions{Container: container.Name} // Only the env container, not fetcher
|
||||
podLogsReq := kubernetesClient.CoreV1().Pods(pod.Namespace).GetLogs(pod.ObjectMeta.Name, &podLogOpts)
|
||||
|
||||
podLogs, err := podLogsReq.Stream()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error streaming pod log")
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("\n%v\nFunction: %v\nEnvironment: %v\nNamespace: %v\nPod: %v\nContainer: %v\nNode: %v\n%v\n", seq,
|
||||
fn.Metadata.Name, fn.Spec.Environment.Name, pod.Namespace, pod.Name, container.Name, pod.Spec.NodeName, seq)
|
||||
w.Write([]byte(msg))
|
||||
|
||||
_, err = io.Copy(w, podLogs)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error copying pod log")
|
||||
}
|
||||
|
||||
podLogs.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func initConfigmapController(logger *zap.Logger, fissionClient *crd.FissionClien
|
||||
if err != nil {
|
||||
logger.Error("Failed to get functions related to configmap", zap.String("configmap_name", newCm.ObjectMeta.Name), zap.String("configmap_namespace", newCm.ObjectMeta.Namespace))
|
||||
}
|
||||
recyclePods(logger, funcs, types)
|
||||
refreshPods(logger, funcs, types)
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -131,7 +131,7 @@ func initSecretController(logger *zap.Logger, fissionClient *crd.FissionClient,
|
||||
if err != nil {
|
||||
logger.Error("Failed to get functions related to secret", zap.String("secret_name", newS.ObjectMeta.Name), zap.String("secret_namespace", newS.ObjectMeta.Namespace))
|
||||
}
|
||||
recyclePods(logger, funcs, types)
|
||||
refreshPods(logger, funcs, types)
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -157,7 +157,7 @@ func getSecretRelatedFuncs(logger *zap.Logger, m *metav1.ObjectMeta, fissionClie
|
||||
return relatedFunctions, nil
|
||||
}
|
||||
|
||||
func recyclePods(logger *zap.Logger, funcs []fv1.Function, types map[fv1.ExecutorType]executortype.ExecutorType) {
|
||||
func refreshPods(logger *zap.Logger, funcs []fv1.Function, types map[fv1.ExecutorType]executortype.ExecutorType) {
|
||||
for _, f := range funcs {
|
||||
var err error
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -37,6 +39,7 @@ import (
|
||||
"github.com/fission/fission/pkg/executor/executortype/poolmgr"
|
||||
"github.com/fission/fission/pkg/executor/fscache"
|
||||
"github.com/fission/fission/pkg/executor/reaper"
|
||||
"github.com/fission/fission/pkg/executor/util"
|
||||
fetcherConfig "github.com/fission/fission/pkg/fetcher/config"
|
||||
)
|
||||
|
||||
@@ -74,6 +77,12 @@ func MakeExecutor(logger *zap.Logger, cms *cms.ConfigSecretController,
|
||||
requestChan: make(chan *createFuncServiceRequest),
|
||||
fsCreateWg: make(map[string]*sync.WaitGroup),
|
||||
}
|
||||
for _, et := range types {
|
||||
go func(et executortype.ExecutorType) {
|
||||
et.Run(context.Background())
|
||||
}(et)
|
||||
}
|
||||
go cms.Run(context.Background())
|
||||
go executor.serveCreateFuncServices()
|
||||
|
||||
return executor, nil
|
||||
@@ -235,19 +244,24 @@ func StartExecutor(logger *zap.Logger, functionNamespace string, envBuilderNames
|
||||
executorTypes[gpm.GetTypeName()] = gpm
|
||||
executorTypes[ndm.GetTypeName()] = ndm
|
||||
|
||||
adoptExistingResources, _ := strconv.ParseBool(os.Getenv("ADOPT_EXISTING_RESOURCES"))
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, et := range executorTypes {
|
||||
wg.Add(1)
|
||||
go func(et executortype.ExecutorType) {
|
||||
defer wg.Done()
|
||||
et.AdoptOrphanResources()
|
||||
et.Run(context.Background())
|
||||
if adoptExistingResources {
|
||||
et.AdoptExistingResources()
|
||||
}
|
||||
et.CleanupOldExecutorObjects()
|
||||
}(et)
|
||||
}
|
||||
wg.Wait()
|
||||
// set hard timeout for resource adoption
|
||||
// TODO: use context to control the waiting time once kubernetes client supports it.
|
||||
util.WaitTimeout(wg, 30*time.Second)
|
||||
|
||||
cms := cms.MakeConfigSecretController(logger, fissionClient, kubernetesClient, executorTypes)
|
||||
cms.Run(context.Background())
|
||||
|
||||
api, err := MakeExecutor(logger, cms, fissionClient, executorTypes)
|
||||
if err != nil {
|
||||
@@ -255,7 +269,6 @@ func StartExecutor(logger *zap.Logger, functionNamespace string, envBuilderNames
|
||||
}
|
||||
|
||||
go reaper.CleanupRoleBindings(logger, kubernetesClient, fissionClient, functionNamespace, envBuilderNamespace, time.Minute*30)
|
||||
go reaper.CleanupOldExecutorObjects(logger, kubernetesClient, executorInstanceID)
|
||||
go api.Serve(port)
|
||||
go serveMetric(logger)
|
||||
|
||||
|
||||
@@ -52,5 +52,8 @@ type ExecutorType interface {
|
||||
RefreshFuncPods(*zap.Logger, fv1.Function) error
|
||||
|
||||
// AdoptOrphanResources adopts existing resources created by the deleted executor.
|
||||
AdoptOrphanResources()
|
||||
AdoptExistingResources()
|
||||
|
||||
// CleanupOldExecutorObjects cleans up resources created by old executor instances
|
||||
CleanupOldExecutorObjects()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package newdeploy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
@@ -29,8 +30,8 @@ import (
|
||||
k8s_err "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8sTypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||
"github.com/fission/fission/pkg/executor/util"
|
||||
@@ -44,42 +45,53 @@ const (
|
||||
)
|
||||
|
||||
func (deploy *NewDeploy) createOrGetDeployment(fn *fv1.Function, env *fv1.Environment,
|
||||
deployName string, deployLabels map[string]string, deployAnnotations map[string]string, deployNamespace string, firstcreate bool) (*appsv1.Deployment, error) {
|
||||
deployName string, deployLabels map[string]string, deployAnnotations map[string]string, deployNamespace string) (*appsv1.Deployment, error) {
|
||||
|
||||
minScale := int32(fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale)
|
||||
specializationTimeout := int(fn.Spec.InvokeStrategy.ExecutionStrategy.SpecializationTimeout)
|
||||
minScale := int32(fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale)
|
||||
|
||||
// If it's not the first time creation and minscale is 0 means that all pods for function were recycled,
|
||||
// in such cases we need set minscale to 1 for router to serve requests.
|
||||
if !firstcreate && minScale <= 0 {
|
||||
// Always scale to at least one pod when createOrGetDeployment
|
||||
// is called. The idleObjectReaper will scale-in the deployment
|
||||
// later if no requests to the function.
|
||||
if minScale <= 0 {
|
||||
minScale = 1
|
||||
}
|
||||
|
||||
waitForDeploy := minScale > 0
|
||||
deployment, err := deploy.getDeploymentSpec(fn, env, &minScale, deployName, deployNamespace, deployLabels, deployAnnotations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
existingDepl, err := deploy.kubernetesClient.AppsV1().Deployments(deployNamespace).Get(deployName, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
if existingDepl.Labels[fv1.EXECUTOR_INSTANCEID_LABEL] != deploy.instanceID {
|
||||
patch := fmt.Sprintf(`{"metadata":{"annotations":{"%v":"%v"}}}`, types.EXECUTOR_INSTANCEID_LABEL, deploy.instanceID)
|
||||
existingDepl, err = deploy.kubernetesClient.AppsV1().Deployments(deployNamespace).Patch(deployName, k8sTypes.StrategicMergePatchType, []byte(patch))
|
||||
// Try to adopt orphan deployment created by the old executor.
|
||||
if existingDepl.Annotations[fv1.EXECUTOR_INSTANCEID_LABEL] != deploy.instanceID {
|
||||
existingDepl.Annotations = deployment.Annotations
|
||||
existingDepl.Labels = deployment.Labels
|
||||
existingDepl.Spec.Template.Spec.Containers = deployment.Spec.Template.Spec.Containers
|
||||
existingDepl.Spec.Template.Spec.ServiceAccountName = deployment.Spec.Template.Spec.ServiceAccountName
|
||||
existingDepl.Spec.Template.Spec.TerminationGracePeriodSeconds = deployment.Spec.Template.Spec.TerminationGracePeriodSeconds
|
||||
existingDepl, err = deploy.kubernetesClient.AppsV1().Deployments(deployNamespace).Update(existingDepl)
|
||||
if err != nil {
|
||||
deploy.logger.Warn("error patching executor instance ID of deploy", zap.Error(err),
|
||||
deploy.logger.Warn("error adopting deploy", zap.Error(err),
|
||||
zap.String("deploy", deployName), zap.String("ns", deployNamespace))
|
||||
return nil, err
|
||||
}
|
||||
// In this case, we just return without waiting for it for fast bootstraping.
|
||||
return existingDepl, nil
|
||||
}
|
||||
if waitForDeploy {
|
||||
if *existingDepl.Spec.Replicas < minScale {
|
||||
err = deploy.scaleDeployment(existingDepl.Namespace, existingDepl.Name, minScale)
|
||||
if err != nil {
|
||||
deploy.logger.Error("error scaling up function deployment", zap.Error(err), zap.String("function", fn.Metadata.Name))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if existingDepl.Status.AvailableReplicas < minScale {
|
||||
existingDepl, err = deploy.waitForDeploy(existingDepl, minScale, specializationTimeout)
|
||||
|
||||
if *existingDepl.Spec.Replicas < minScale {
|
||||
err = deploy.scaleDeployment(existingDepl.Namespace, existingDepl.Name, minScale)
|
||||
if err != nil {
|
||||
deploy.logger.Error("error scaling up function deployment", zap.Error(err), zap.String("function", fn.Metadata.Name))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if existingDepl.Status.AvailableReplicas < minScale {
|
||||
existingDepl, err = deploy.waitForDeploy(existingDepl, minScale, specializationTimeout)
|
||||
}
|
||||
|
||||
return existingDepl, err
|
||||
} else if k8s_err.IsNotFound(err) {
|
||||
err := deploy.setupRBACObjs(deployNamespace, fn)
|
||||
@@ -87,30 +99,26 @@ func (deploy *NewDeploy) createOrGetDeployment(fn *fv1.Function, env *fv1.Enviro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deployment, err := deploy.getDeploymentSpec(fn, env, deployName, deployNamespace, deployLabels, deployAnnotations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
depl, err := deploy.kubernetesClient.AppsV1().Deployments(deployNamespace).Create(deployment)
|
||||
if err != nil {
|
||||
deploy.logger.Error("error while creating function deployment",
|
||||
zap.Error(err),
|
||||
zap.String("function", fn.Metadata.Name),
|
||||
zap.String("deployment_name", deployName),
|
||||
zap.String("deployment_namespace", deployNamespace))
|
||||
return nil, err
|
||||
if k8s_err.IsAlreadyExists(err) {
|
||||
depl, err = deploy.kubernetesClient.AppsV1().Deployments(deployNamespace).Get(deployName, metav1.GetOptions{})
|
||||
}
|
||||
if err != nil {
|
||||
deploy.logger.Error("error while creating function deployment",
|
||||
zap.Error(err),
|
||||
zap.String("function", fn.Metadata.Name),
|
||||
zap.String("deployment_name", deployName),
|
||||
zap.String("deployment_namespace", deployNamespace))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if waitForDeploy {
|
||||
if minScale > 0 {
|
||||
depl, err = deploy.waitForDeploy(depl, minScale, specializationTimeout)
|
||||
}
|
||||
|
||||
return depl, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) setupRBACObjs(deployNamespace string, fn *fv1.Function) error {
|
||||
@@ -168,10 +176,13 @@ func (deploy *NewDeploy) deleteDeployment(ns string, name string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) getDeploymentSpec(fn *fv1.Function, env *fv1.Environment,
|
||||
func (deploy *NewDeploy) getDeploymentSpec(fn *fv1.Function, env *fv1.Environment, targetReplicas *int32,
|
||||
deployName string, deployNamespace string, deployLabels map[string]string, deployAnnotations map[string]string) (*appsv1.Deployment, error) {
|
||||
|
||||
replicas := int32(fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale)
|
||||
if targetReplicas != nil {
|
||||
replicas = *targetReplicas
|
||||
}
|
||||
|
||||
gracePeriodSeconds := int64(6 * 60)
|
||||
if env.Spec.TerminationGracePeriod > 0 {
|
||||
@@ -182,12 +193,13 @@ func (deploy *NewDeploy) getDeploymentSpec(fn *fv1.Function, env *fv1.Environmen
|
||||
if podAnnotations == nil {
|
||||
podAnnotations = make(map[string]string)
|
||||
}
|
||||
|
||||
// Here, we don't append deployAnnotations to podAnnotations
|
||||
// since newdeploy doesn't manager pod lifecycle directly.
|
||||
|
||||
if deploy.useIstio && env.Spec.AllowAccessToExternalNetwork {
|
||||
podAnnotations["sidecar.istio.io/inject"] = "false"
|
||||
}
|
||||
for k, v := range deployAnnotations {
|
||||
podAnnotations[k] = v
|
||||
}
|
||||
resources := deploy.getResources(env, fn)
|
||||
|
||||
// Set maxUnavailable and maxSurge to 20% is because we want
|
||||
@@ -207,6 +219,11 @@ func (deploy *NewDeploy) getDeploymentSpec(fn *fv1.Function, env *fv1.Environmen
|
||||
// rollback, set RevisionHistoryLimit to 0 to disable this feature.
|
||||
revisionHistoryLimit := int32(0)
|
||||
|
||||
rvCount, err := referencedResourcesRVSum(deploy.kubernetesClient, fn.Metadata.Namespace, fn.Spec.Secrets, fn.Spec.ConfigMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
container, err := util.MergeContainer(&apiv1.Container{
|
||||
Name: fn.Metadata.Name,
|
||||
Image: env.Spec.Runtime.Image,
|
||||
@@ -224,8 +241,8 @@ func (deploy *NewDeploy) getDeploymentSpec(fn *fv1.Function, env *fv1.Environmen
|
||||
},
|
||||
Env: []apiv1.EnvVar{
|
||||
{
|
||||
Name: fv1.LastUpdateTimestamp,
|
||||
Value: time.Now().String(),
|
||||
Name: fv1.ResourceVersionCount,
|
||||
Value: fmt.Sprintf("%v", rvCount),
|
||||
},
|
||||
},
|
||||
// https://istio.io/docs/setup/kubernetes/additional-setup/requirements/
|
||||
@@ -334,7 +351,9 @@ func (deploy *NewDeploy) getResources(env *fv1.Environment, fn *fv1.Function) ap
|
||||
return resources
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) createOrGetHpa(hpaName string, execStrategy *fv1.ExecutionStrategy, depl *appsv1.Deployment, deployLabels map[string]string, deployAnnotations map[string]string) (*asv1.HorizontalPodAutoscaler, error) {
|
||||
func (deploy *NewDeploy) createOrGetHpa(hpaName string, execStrategy *fv1.ExecutionStrategy,
|
||||
depl *appsv1.Deployment, deployLabels map[string]string, deployAnnotations map[string]string) (*asv1.HorizontalPodAutoscaler, error) {
|
||||
|
||||
if depl == nil {
|
||||
return nil, errors.New("failed to create HPA, found empty deployment")
|
||||
}
|
||||
@@ -349,46 +368,52 @@ func (deploy *NewDeploy) createOrGetHpa(hpaName string, execStrategy *fv1.Execut
|
||||
}
|
||||
targetCPU := int32(execStrategy.TargetCPUPercent)
|
||||
|
||||
hpa := &asv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: hpaName,
|
||||
Labels: deployLabels,
|
||||
Annotations: deployAnnotations,
|
||||
},
|
||||
Spec: asv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: asv1.CrossVersionObjectReference{
|
||||
Kind: DeploymentKind,
|
||||
Name: depl.ObjectMeta.Name,
|
||||
APIVersion: DeploymentVersion,
|
||||
},
|
||||
MinReplicas: &minRepl,
|
||||
MaxReplicas: maxRepl,
|
||||
TargetCPUUtilizationPercentage: &targetCPU,
|
||||
},
|
||||
}
|
||||
|
||||
existingHpa, err := deploy.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(depl.ObjectMeta.Namespace).Get(hpaName, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
if existingHpa.Labels[fv1.EXECUTOR_INSTANCEID_LABEL] != deploy.instanceID {
|
||||
patch := fmt.Sprintf(`{"metadata":{"annotations":{"%v":"%v"}}}`, types.EXECUTOR_INSTANCEID_LABEL, deploy.instanceID)
|
||||
existingHpa, err = deploy.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(depl.ObjectMeta.Namespace).Patch(hpaName, k8sTypes.StrategicMergePatchType, []byte(patch))
|
||||
// to adopt orphan service
|
||||
if existingHpa.Annotations[fv1.EXECUTOR_INSTANCEID_LABEL] != deploy.instanceID {
|
||||
existingHpa.Annotations = hpa.Annotations
|
||||
existingHpa.Labels = hpa.Labels
|
||||
existingHpa.Spec = hpa.Spec
|
||||
existingHpa, err = deploy.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(depl.ObjectMeta.Namespace).Update(existingHpa)
|
||||
if err != nil {
|
||||
deploy.logger.Warn("error patching executor instance ID of HPA", zap.Error(err),
|
||||
deploy.logger.Warn("error adopting HPA", zap.Error(err),
|
||||
zap.String("HPA", hpaName), zap.String("ns", depl.ObjectMeta.Namespace))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return existingHpa, err
|
||||
} else if k8s_err.IsNotFound(err) {
|
||||
hpa := asv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: hpaName,
|
||||
Labels: deployLabels,
|
||||
Annotations: deployAnnotations,
|
||||
},
|
||||
Spec: asv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: asv1.CrossVersionObjectReference{
|
||||
Kind: DeploymentKind,
|
||||
Name: depl.ObjectMeta.Name,
|
||||
APIVersion: DeploymentVersion,
|
||||
},
|
||||
MinReplicas: &minRepl,
|
||||
MaxReplicas: maxRepl,
|
||||
TargetCPUUtilizationPercentage: &targetCPU,
|
||||
},
|
||||
}
|
||||
|
||||
cHpa, err := deploy.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(depl.ObjectMeta.Namespace).Create(&hpa)
|
||||
cHpa, err := deploy.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(depl.ObjectMeta.Namespace).Create(hpa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if k8s_err.IsAlreadyExists(err) {
|
||||
cHpa, err = deploy.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(depl.ObjectMeta.Namespace).Get(hpaName, metav1.GetOptions{})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cHpa, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) getHpa(ns, name string) (*asv1.HorizontalPodAutoscaler, error) {
|
||||
@@ -405,41 +430,51 @@ func (deploy *NewDeploy) deleteHpa(ns string, name string) error {
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) createOrGetSvc(deployLabels map[string]string, deployAnnotations map[string]string, svcName string, svcNamespace string) (*apiv1.Service, error) {
|
||||
service := &apiv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: svcName,
|
||||
Labels: deployLabels,
|
||||
Annotations: deployAnnotations,
|
||||
},
|
||||
Spec: apiv1.ServiceSpec{
|
||||
Ports: []apiv1.ServicePort{
|
||||
{
|
||||
Name: "http-env",
|
||||
Port: int32(80),
|
||||
TargetPort: intstr.FromInt(8888),
|
||||
},
|
||||
},
|
||||
Selector: deployLabels,
|
||||
Type: apiv1.ServiceTypeClusterIP,
|
||||
},
|
||||
}
|
||||
|
||||
existingSvc, err := deploy.kubernetesClient.CoreV1().Services(svcNamespace).Get(svcName, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
if existingSvc.Labels[fv1.EXECUTOR_INSTANCEID_LABEL] != deploy.instanceID {
|
||||
patch := fmt.Sprintf(`{"metadata":{"annotations":{"%v":"%v"}}}`, types.EXECUTOR_INSTANCEID_LABEL, deploy.instanceID)
|
||||
existingSvc, err = deploy.kubernetesClient.CoreV1().Services(svcNamespace).Patch(svcName, k8sTypes.StrategicMergePatchType, []byte(patch))
|
||||
// to adopt orphan service
|
||||
if existingSvc.Annotations[fv1.EXECUTOR_INSTANCEID_LABEL] != deploy.instanceID {
|
||||
existingSvc.Annotations = service.Annotations
|
||||
existingSvc.Labels = service.Labels
|
||||
existingSvc.Spec.Ports = service.Spec.Ports
|
||||
existingSvc.Spec.Selector = service.Spec.Selector
|
||||
existingSvc.Spec.Type = service.Spec.Type
|
||||
existingSvc, err = deploy.kubernetesClient.CoreV1().Services(svcNamespace).Update(existingSvc)
|
||||
if err != nil {
|
||||
deploy.logger.Warn("error patching executor instance ID of service", zap.Error(err),
|
||||
deploy.logger.Warn("error adopting service", zap.Error(err),
|
||||
zap.String("service", svcName), zap.String("ns", svcNamespace))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return existingSvc, err
|
||||
} else if k8s_err.IsNotFound(err) {
|
||||
service := &apiv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: svcName,
|
||||
Labels: deployLabels,
|
||||
Annotations: deployAnnotations,
|
||||
},
|
||||
Spec: apiv1.ServiceSpec{
|
||||
Ports: []apiv1.ServicePort{
|
||||
{
|
||||
Name: "http-env",
|
||||
Port: int32(80),
|
||||
TargetPort: intstr.FromInt(8888),
|
||||
},
|
||||
},
|
||||
Selector: deployLabels,
|
||||
Type: apiv1.ServiceTypeClusterIP,
|
||||
},
|
||||
}
|
||||
|
||||
svc, err := deploy.kubernetesClient.CoreV1().Services(svcNamespace).Create(service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if k8s_err.IsAlreadyExists(err) {
|
||||
svc, err = deploy.kubernetesClient.CoreV1().Services(svcNamespace).Get(svcName, metav1.GetOptions{})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return svc, nil
|
||||
}
|
||||
@@ -508,3 +543,57 @@ func (deploy *NewDeploy) cleanupNewdeploy(ns string, name string) error {
|
||||
|
||||
return result.ErrorOrNil()
|
||||
}
|
||||
|
||||
// referencedResourcesRVSum returns the sum of resource version of all resources the function references to.
|
||||
// We used to update timestamp in the deployment environment field in order to trigger a rolling update when
|
||||
// the function referenced resources get updated. However, use timestamp means we are not able to avoid tri-
|
||||
// ggering a rolling update when executor tries to adopt orphaned deployment due to timestamp changed which
|
||||
// is unwanted. In order to let executor adopt deployment without triggering a rolling update, we need an
|
||||
// identical way to get a value that can reflect resources changed without affecting by the time.
|
||||
// To achieve this goal, the sum of the resource version of all referenced resources is a good fit for our
|
||||
// scenario since the sum of the resource version is always the same as long as no resources changed.
|
||||
func referencedResourcesRVSum(client *kubernetes.Clientset, namespace string, secrets []fv1.SecretReference, cfgmaps []fv1.ConfigMapReference) (int, error) {
|
||||
rvCount := 0
|
||||
|
||||
if len(secrets) > 0 {
|
||||
list, err := client.CoreV1().Secrets(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
objmap := make(map[string]apiv1.Secret)
|
||||
for _, secret := range list.Items {
|
||||
objmap[secret.Namespace+"/"+secret.Name] = secret
|
||||
}
|
||||
|
||||
for _, ref := range secrets {
|
||||
s, ok := objmap[ref.Namespace+"/"+ref.Name]
|
||||
if ok {
|
||||
rv, _ := strconv.ParseInt(s.ResourceVersion, 10, 32)
|
||||
rvCount += int(rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(cfgmaps) > 0 {
|
||||
list, err := client.CoreV1().ConfigMaps(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
objmap := make(map[string]apiv1.ConfigMap)
|
||||
for _, cfg := range list.Items {
|
||||
objmap[cfg.Namespace+"/"+cfg.Name] = cfg
|
||||
}
|
||||
|
||||
for _, ref := range cfgmaps {
|
||||
s, ok := objmap[ref.Namespace+"/"+ref.Name]
|
||||
if ok {
|
||||
rv, _ := strconv.ParseInt(s.ResourceVersion, 10, 32)
|
||||
rvCount += int(rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rvCount, nil
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package newdeploy
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -44,6 +43,7 @@ import (
|
||||
"github.com/fission/fission/pkg/crd"
|
||||
"github.com/fission/fission/pkg/executor/executortype"
|
||||
"github.com/fission/fission/pkg/executor/fscache"
|
||||
"github.com/fission/fission/pkg/executor/reaper"
|
||||
fetcherConfig "github.com/fission/fission/pkg/fetcher/config"
|
||||
"github.com/fission/fission/pkg/throttler"
|
||||
"github.com/fission/fission/pkg/types"
|
||||
@@ -143,7 +143,7 @@ func (deploy *NewDeploy) GetFuncSvc(ctx context.Context, fn *fv1.Function) (*fsc
|
||||
// TODO: client-go doesn't support to pass in context.
|
||||
// Once it supports context, we should change the signature of method.
|
||||
// https://github.com/kubernetes/kubernetes/issues/46503
|
||||
return deploy.createFunction(fn, false)
|
||||
return deploy.createFunction(fn)
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) GetFuncSvcFromCache(fn *fv1.Function) (*fscache.FuncSvc, error) {
|
||||
@@ -224,14 +224,17 @@ func (deploy *NewDeploy) RefreshFuncPods(logger *zap.Logger, f fv1.Function) err
|
||||
return err
|
||||
}
|
||||
|
||||
patch := fmt.Sprintf(`{"spec" : {"template": {"spec":{"containers":[{"name": "%s", "env":[{"name": "%s", "value": "%s"}]}]}}}}`,
|
||||
f.Metadata.Name,
|
||||
fv1.LastUpdateTimestamp,
|
||||
time.Now().String())
|
||||
|
||||
// Ideally there should be only one deployment but for now we rely on label/selector to ensure that condition
|
||||
for _, deployment := range dep.Items {
|
||||
_, err := deploy.kubernetesClient.AppsV1().Deployments(deployment.ObjectMeta.Namespace).Patch(deployment.ObjectMeta.Name,
|
||||
rvCount, err := referencedResourcesRVSum(deploy.kubernetesClient, deployment.Namespace, f.Spec.Secrets, f.Spec.ConfigMaps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patch := fmt.Sprintf(`{"spec" : {"template": {"spec":{"containers":[{"name": "%s", "env":[{"name": "%s", "value": "%v"}]}]}}}}`,
|
||||
f.Metadata.Name, fv1.ResourceVersionCount, rvCount)
|
||||
|
||||
_, err = deploy.kubernetesClient.AppsV1().Deployments(deployment.ObjectMeta.Namespace).Patch(deployment.ObjectMeta.Name,
|
||||
k8sTypes.StrategicMergePatchType,
|
||||
[]byte(patch))
|
||||
if err != nil {
|
||||
@@ -241,65 +244,23 @@ func (deploy *NewDeploy) RefreshFuncPods(logger *zap.Logger, f fv1.Function) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) AdoptOrphanResources() {
|
||||
l := map[string]string{
|
||||
types.EXECUTOR_TYPE: string(fv1.ExecutorTypeNewdeploy),
|
||||
}
|
||||
|
||||
podList, err := deploy.kubernetesClient.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{
|
||||
LabelSelector: labels.Set(l).AsSelector().String(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
deploy.logger.Error("error getting pod list", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
podWG := &sync.WaitGroup{}
|
||||
|
||||
for i := range podList.Items {
|
||||
pod := &podList.Items[i]
|
||||
if !utils.IsReadyPod(pod) {
|
||||
continue
|
||||
}
|
||||
|
||||
podWG.Add(1)
|
||||
go func() {
|
||||
defer podWG.Done()
|
||||
|
||||
// avoid too many requests arrive Kubernetes API server at the same time.
|
||||
time.Sleep(time.Duration(rand.Intn(30)) * time.Millisecond)
|
||||
|
||||
patch := fmt.Sprintf(`{"metadata":{"annotations":{"%v":"%v"}}}`, types.EXECUTOR_INSTANCEID_LABEL, deploy.instanceID)
|
||||
pod, err = deploy.kubernetesClient.CoreV1().Pods(pod.Namespace).Patch(pod.Name, k8sTypes.StrategicMergePatchType, []byte(patch))
|
||||
if err != nil {
|
||||
// just log the error since it won't affect the function serving
|
||||
deploy.logger.Warn("error patching executor instance ID of pod", zap.Error(err),
|
||||
zap.String("pod", pod.Name), zap.String("ns", pod.Namespace))
|
||||
return
|
||||
}
|
||||
|
||||
deploy.logger.Info("adopt newdeploy function pod",
|
||||
zap.String("pod", pod.Name), zap.Any("labels", pod.Labels), zap.Any("annotations", pod.Annotations))
|
||||
}()
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) AdoptExistingResources() {
|
||||
fnList, err := deploy.fissionClient.Functions(metav1.NamespaceAll).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
deploy.logger.Error("error getting function list", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
deployWG := &sync.WaitGroup{}
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
for i := range fnList.Items {
|
||||
fn := &fnList.Items[i]
|
||||
if fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fv1.ExecutorTypeNewdeploy {
|
||||
deployWG.Add(1)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer deployWG.Done()
|
||||
defer wg.Done()
|
||||
|
||||
_, err = deploy.fnCreate(fn, true)
|
||||
_, err = deploy.fnCreate(fn)
|
||||
if err != nil {
|
||||
deploy.logger.Warn("failed to adopt resources for function", zap.Error(err))
|
||||
return
|
||||
@@ -309,8 +270,36 @@ func (deploy *NewDeploy) AdoptOrphanResources() {
|
||||
}
|
||||
}
|
||||
|
||||
podWG.Wait()
|
||||
deployWG.Wait()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) CleanupOldExecutorObjects() {
|
||||
deploy.logger.Info("Newdeploy starts to clean orphaned resources", zap.String("instanceID", deploy.instanceID))
|
||||
|
||||
errs := &multierror.Error{}
|
||||
listOpts := metav1.ListOptions{
|
||||
LabelSelector: labels.Set(map[string]string{types.EXECUTOR_TYPE: string(fv1.ExecutorTypeNewdeploy)}).AsSelector().String(),
|
||||
}
|
||||
|
||||
err := reaper.CleanupHpa(deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
|
||||
err = reaper.CleanupDeployments(deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
|
||||
err = reaper.CleanupServices(deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
|
||||
if errs.ErrorOrNil() != nil {
|
||||
// TODO retry reaper; logged and ignored for now
|
||||
deploy.logger.Error("Failed to cleanup old executor objects", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) initFuncController() (k8sCache.Store, k8sCache.Controller) {
|
||||
@@ -324,7 +313,7 @@ func (deploy *NewDeploy) initFuncController() (k8sCache.Store, k8sCache.Controll
|
||||
go func() {
|
||||
fn := obj.(*fv1.Function)
|
||||
deploy.logger.Debug("create deployment for function", zap.Any("fn", fn.Metadata), zap.Any("fnspec", fn.Spec))
|
||||
_, err := deploy.createFunction(fn, true)
|
||||
_, err := deploy.createFunction(fn)
|
||||
if err != nil {
|
||||
deploy.logger.Error("error eager creating function",
|
||||
zap.Error(err),
|
||||
@@ -406,14 +395,14 @@ func (deploy *NewDeploy) getEnvFunctions(m *metav1.ObjectMeta) []fv1.Function {
|
||||
return relatedFunctions
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) createFunction(fn *fv1.Function, firstcreate bool) (*fscache.FuncSvc, error) {
|
||||
func (deploy *NewDeploy) createFunction(fn *fv1.Function) (*fscache.FuncSvc, error) {
|
||||
if fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypeNewdeploy {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fsvcObj, err := deploy.throttler.RunOnce(string(fn.Metadata.UID), func(ableToCreate bool) (interface{}, error) {
|
||||
if ableToCreate {
|
||||
return deploy.fnCreate(fn, firstcreate)
|
||||
return deploy.fnCreate(fn)
|
||||
}
|
||||
return deploy.fsCache.GetByFunctionUID(fn.Metadata.UID)
|
||||
})
|
||||
@@ -446,7 +435,7 @@ func (deploy *NewDeploy) deleteFunction(fn *fv1.Function) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (deploy *NewDeploy) fnCreate(fn *fv1.Function, firstcreate bool) (*fscache.FuncSvc, error) {
|
||||
func (deploy *NewDeploy) fnCreate(fn *fv1.Function) (*fscache.FuncSvc, error) {
|
||||
env, err := deploy.fissionClient.
|
||||
Environments(fn.Spec.Environment.Namespace).
|
||||
Get(fn.Spec.Environment.Name)
|
||||
@@ -478,7 +467,7 @@ func (deploy *NewDeploy) fnCreate(fn *fv1.Function, firstcreate bool) (*fscache.
|
||||
}
|
||||
svcAddress := fmt.Sprintf("%v.%v", svc.Name, svc.Namespace)
|
||||
|
||||
depl, err := deploy.createOrGetDeployment(fn, env, objName, deployLabels, deployAnnotations, ns, firstcreate)
|
||||
depl, err := deploy.createOrGetDeployment(fn, env, objName, deployLabels, deployAnnotations, ns)
|
||||
if err != nil {
|
||||
deploy.logger.Error("error creating deployment", zap.Error(err), zap.String("deployment", objName))
|
||||
go deploy.cleanupNewdeploy(ns, objName)
|
||||
@@ -567,7 +556,7 @@ func (deploy *NewDeploy) updateFunction(oldFn *fv1.Function, newFn *fv1.Function
|
||||
deploy.logger.Info("function type changed to new deployment, creating resources",
|
||||
zap.Any("old_function", oldFn.Metadata),
|
||||
zap.Any("new_function", newFn.Metadata))
|
||||
_, err := deploy.createFunction(newFn, true)
|
||||
_, err := deploy.createFunction(newFn)
|
||||
if err != nil {
|
||||
deploy.updateStatus(oldFn, err, "error changing the function's type to newdeploy")
|
||||
}
|
||||
@@ -686,7 +675,17 @@ func (deploy *NewDeploy) updateFuncDeployment(fn *fv1.Function, env *fv1.Environ
|
||||
ns = fn.Metadata.Namespace
|
||||
}
|
||||
|
||||
newDeployment, err := deploy.getDeploymentSpec(fn, env, fnObjName, ns, deployLabels, deploy.getDeployAnnotations(fn.Metadata))
|
||||
existingDepl, err := deploy.kubernetesClient.AppsV1().Deployments(ns).Get(fnObjName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// the resource version inside function packageRef is changed,
|
||||
// so the content of fetchRequest in deployment cmd is different.
|
||||
// Therefore, the deployment update will trigger a rolling update.
|
||||
newDeployment, err := deploy.getDeploymentSpec(fn, env,
|
||||
existingDepl.Spec.Replicas, // use current replicas instead of minscale in the ExecutionStrategy.
|
||||
fnObjName, ns, deployLabels, deploy.getDeployAnnotations(fn.Metadata))
|
||||
if err != nil {
|
||||
deploy.updateStatus(fn, err, "failed to get new deployment spec while updating function")
|
||||
return err
|
||||
|
||||
@@ -146,7 +146,7 @@ func MakeGenericPool(
|
||||
return gp, nil
|
||||
}
|
||||
|
||||
func (gp *GenericPool) getDeployLabels() map[string]string {
|
||||
func (gp *GenericPool) getEnvironmentPoolLabels() map[string]string {
|
||||
return map[string]string{
|
||||
types.EXECUTOR_TYPE: string(fv1.ExecutorTypePoolmgr),
|
||||
types.ENVIRONMENT_NAME: gp.env.Metadata.Name,
|
||||
@@ -246,6 +246,11 @@ func (gp *GenericPool) _choosePod(newLabels map[string]string) (*apiv1.Pod, erro
|
||||
// modified, this should fail; in that case just
|
||||
// retry.
|
||||
chosenPod.ObjectMeta.Labels = newLabels
|
||||
|
||||
// Append executor instance id to pod annotations to
|
||||
// indicate this pod is managed by this executor.
|
||||
chosenPod.ObjectMeta.Annotations = gp.getDeployAnnotations()
|
||||
|
||||
_, err = gp.kubernetesClient.CoreV1().Pods(gp.namespace).Update(chosenPod)
|
||||
if err != nil {
|
||||
gp.logger.Error("failed to relabel pod", zap.Error(err), zap.String("pod", chosenPod.ObjectMeta.Name))
|
||||
@@ -259,7 +264,7 @@ func (gp *GenericPool) _choosePod(newLabels map[string]string) (*apiv1.Pod, erro
|
||||
}
|
||||
|
||||
func (gp *GenericPool) labelsForFunction(metadata *metav1.ObjectMeta) map[string]string {
|
||||
label := gp.getDeployLabels()
|
||||
label := gp.getEnvironmentPoolLabels()
|
||||
label[types.FUNCTION_NAME] = metadata.Name
|
||||
label[types.FUNCTION_UID] = string(metadata.UID)
|
||||
label[types.FUNCTION_NAMESPACE] = metadata.Namespace // function CRD must stay within same namespace of environment CRD
|
||||
@@ -274,11 +279,6 @@ func (gp *GenericPool) scheduleDeletePod(name string) {
|
||||
// cleaned up. (We need a better solutions for both those things; log
|
||||
// aggregation and storage will help.)
|
||||
gp.logger.Error("error in pod - scheduling cleanup", zap.String("pod", name))
|
||||
// Ignore sleep here if istio feature is enabled, function pod
|
||||
// will be deleted after 6 mins (terminationGracePeriodSeconds).
|
||||
if !gp.useIstio {
|
||||
time.Sleep(5 * time.Minute)
|
||||
}
|
||||
gp.kubernetesClient.CoreV1().Pods(gp.namespace).Delete(name, nil)
|
||||
}()
|
||||
}
|
||||
@@ -351,7 +351,7 @@ func (gp *GenericPool) getPoolName() string {
|
||||
// A pool is a deployment of generic containers for an env. This
|
||||
// creates the pool but doesn't wait for any pods to be ready.
|
||||
func (gp *GenericPool) createPool() error {
|
||||
deployLabels := gp.getDeployLabels()
|
||||
deployLabels := gp.getEnvironmentPoolLabels()
|
||||
deployAnnotations := gp.getDeployAnnotations()
|
||||
|
||||
// Use long terminationGracePeriodSeconds for connection draining in case that
|
||||
@@ -365,12 +365,15 @@ func (gp *GenericPool) createPool() error {
|
||||
if podAnnotations == nil {
|
||||
podAnnotations = make(map[string]string)
|
||||
}
|
||||
|
||||
// Here, we don't append executor instance-id to pod annotations
|
||||
// to prevent unwanted rolling updates occur. Pool manager will
|
||||
// append executor instance-id to pod annotations when a pod is chosen
|
||||
// for function specialization.
|
||||
|
||||
if gp.useIstio && gp.env.Spec.AllowAccessToExternalNetwork {
|
||||
podAnnotations["sidecar.istio.io/inject"] = "false"
|
||||
}
|
||||
for k, v := range deployAnnotations {
|
||||
podAnnotations[k] = v
|
||||
}
|
||||
|
||||
container, err := util.MergeContainer(&apiv1.Container{
|
||||
Name: gp.env.Metadata.Name,
|
||||
@@ -456,20 +459,20 @@ func (gp *GenericPool) createPool() error {
|
||||
deployment.Spec.Template.Spec = *newPodSpec
|
||||
}
|
||||
|
||||
_, err = gp.kubernetesClient.AppsV1().Deployments(gp.namespace).Get(deployment.Name, metav1.GetOptions{})
|
||||
depl, err := gp.kubernetesClient.AppsV1().Deployments(gp.namespace).Get(deployment.Name, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
patch := fmt.Sprintf(`{"metadata":{"annotations":{"%v":"%v"}}}`, fv1.EXECUTOR_INSTANCEID_LABEL, gp.instanceId)
|
||||
depl, err := gp.kubernetesClient.AppsV1().Deployments(gp.namespace).Patch(deployment.Name, k8sTypes.StrategicMergePatchType, []byte(patch))
|
||||
if err == nil {
|
||||
gp.deployment = depl
|
||||
return nil
|
||||
if depl.Annotations[fv1.EXECUTOR_INSTANCEID_LABEL] != gp.instanceId {
|
||||
deployment.Annotations[fv1.EXECUTOR_INSTANCEID_LABEL] = gp.instanceId
|
||||
depl, err = gp.kubernetesClient.AppsV1().Deployments(gp.namespace).Update(deployment)
|
||||
}
|
||||
gp.deployment = depl
|
||||
return err
|
||||
} else if !k8sErrs.IsNotFound(err) {
|
||||
gp.logger.Error("error getting deployment in kubernetes", zap.Error(err), zap.String("deployment", deployment.Name))
|
||||
return err
|
||||
}
|
||||
|
||||
depl, err := gp.kubernetesClient.AppsV1().Deployments(gp.namespace).Create(deployment)
|
||||
depl, err = gp.kubernetesClient.AppsV1().Deployments(gp.namespace).Create(deployment)
|
||||
if err != nil {
|
||||
gp.logger.Error("error creating deployment in kubernetes", zap.Error(err), zap.String("deployment", deployment.Name))
|
||||
return err
|
||||
@@ -526,7 +529,8 @@ func (gp *GenericPool) waitForReadyPod() error {
|
||||
func (gp *GenericPool) createSvc(name string, labels map[string]string) (*apiv1.Service, error) {
|
||||
service := apiv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: apiv1.ServiceSpec{
|
||||
Type: apiv1.ServiceTypeClusterIP,
|
||||
@@ -546,7 +550,7 @@ func (gp *GenericPool) createSvc(name string, labels map[string]string) (*apiv1.
|
||||
|
||||
func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscache.FuncSvc, error) {
|
||||
gp.logger.Info("choosing pod from pool", zap.Any("function", fn.Metadata))
|
||||
newLabels := gp.labelsForFunction(&fn.Metadata)
|
||||
funcLabels := gp.labelsForFunction(&fn.Metadata)
|
||||
|
||||
if gp.useIstio {
|
||||
// Istio only allows accessing pod through k8s service, and requests come to
|
||||
@@ -589,7 +593,7 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
|
||||
}
|
||||
}
|
||||
|
||||
pod, err := gp.choosePod(newLabels)
|
||||
pod, err := gp.choosePod(funcLabels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -608,7 +612,7 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
|
||||
svcName = fmt.Sprintf("%s-%v", svcName, fn.Metadata.UID)
|
||||
}
|
||||
|
||||
svc, err := gp.createSvc(svcName, newLabels)
|
||||
svc, err := gp.createSvc(svcName, funcLabels)
|
||||
if err != nil {
|
||||
gp.scheduleDeletePod(pod.ObjectMeta.Name)
|
||||
return nil, err
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"go.uber.org/zap"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -183,7 +184,7 @@ func (gpm *GenericPoolManager) TapService(svcHost string) error {
|
||||
// containers in it are reporting a ready status for the healthCheck.
|
||||
func (gpm *GenericPoolManager) IsValid(fsvc *fscache.FuncSvc) bool {
|
||||
for _, obj := range fsvc.KubernetesObjects {
|
||||
if obj.Kind == "pod" {
|
||||
if strings.ToLower(obj.Kind) == "pod" {
|
||||
pod, err := gpm.kubernetesClient.CoreV1().Pods(obj.Namespace).Get(obj.Name, metav1.GetOptions{})
|
||||
if err == nil && utils.IsReadyPod(pod) {
|
||||
// Normally, the address format is http://[pod-ip]:[port], however, if the
|
||||
@@ -192,7 +193,11 @@ func (gpm *GenericPoolManager) IsValid(fsvc *fscache.FuncSvc) bool {
|
||||
// Otherwise, we need to ensure that the address contains pod ip.
|
||||
if gpm.enableIstio ||
|
||||
(!gpm.enableIstio && strings.Contains(fsvc.Address, pod.Status.PodIP)) {
|
||||
gpm.logger.Debug("valid address", zap.String("address", fsvc.Address))
|
||||
gpm.logger.Debug("valid address",
|
||||
zap.String("address", fsvc.Address),
|
||||
zap.Any("function", fsvc.Function),
|
||||
zap.String("executor", string(fsvc.Executor)),
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -243,7 +248,7 @@ func (gpm *GenericPoolManager) RefreshFuncPods(logger *zap.Logger, f fv1.Functio
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) AdoptOrphanResources() {
|
||||
func (gpm *GenericPoolManager) AdoptExistingResources() {
|
||||
envs, err := gpm.fissionClient.Environments(metav1.NamespaceAll).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
gpm.logger.Error("error getting environment list", zap.Error(err))
|
||||
@@ -251,15 +256,15 @@ func (gpm *GenericPoolManager) AdoptOrphanResources() {
|
||||
}
|
||||
|
||||
envMap := make(map[string]fv1.Environment, len(envs.Items))
|
||||
envWG := &sync.WaitGroup{}
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
for i := range envs.Items {
|
||||
env := envs.Items[i]
|
||||
|
||||
if gpm.getEnvPoolsize(&env) > 0 {
|
||||
envWG.Add(1)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer envWG.Done()
|
||||
defer wg.Done()
|
||||
_, err := gpm.getPool(&env)
|
||||
if err != nil {
|
||||
gpm.logger.Error("adopt pool failed", zap.Error(err))
|
||||
@@ -285,17 +290,15 @@ func (gpm *GenericPoolManager) AdoptOrphanResources() {
|
||||
return
|
||||
}
|
||||
|
||||
podWG := &sync.WaitGroup{}
|
||||
|
||||
for i := range podList.Items {
|
||||
pod := &podList.Items[i]
|
||||
if !utils.IsReadyPod(pod) {
|
||||
continue
|
||||
}
|
||||
|
||||
podWG.Add(1)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer podWG.Done()
|
||||
defer wg.Done()
|
||||
|
||||
// avoid too many requests arrive Kubernetes API server at the same time.
|
||||
time.Sleep(time.Duration(rand.Intn(30)) * time.Millisecond)
|
||||
@@ -324,7 +327,7 @@ func (gpm *GenericPoolManager) AdoptOrphanResources() {
|
||||
env, ok8 := envMap[fmt.Sprintf("%v/%v", envNS, envName)]
|
||||
|
||||
if !(ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8) {
|
||||
gpm.logger.Warn("failed to adopt pod for function due to lack necessary information",
|
||||
gpm.logger.Warn("failed to adopt pod for function due to lack of necessary information",
|
||||
zap.String("pod", pod.Name), zap.Any("labels", pod.Labels), zap.Any("annotations", pod.Annotations),
|
||||
zap.String("env", env.Metadata.Name))
|
||||
return
|
||||
@@ -355,16 +358,14 @@ func (gpm *GenericPoolManager) AdoptOrphanResources() {
|
||||
Atime: time.Now(),
|
||||
}
|
||||
|
||||
// If fsvc already exists we just skip the duplicate one. And let reaper to recycle the duplicate pod.
|
||||
// This is for the case that there are multiple function pods for the same function due to unknown reason.
|
||||
_, err := gpm.fsCache.GetByFunction(fsvc.Function)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = gpm.fsCache.Add(fsvc)
|
||||
if err != nil {
|
||||
gpm.logger.Warn("failed to adopt pod for function", zap.Error(err), zap.String("pod", pod.Name))
|
||||
// If fsvc already exists we just skip the duplicate one. And let reaper to recycle the duplicate pods.
|
||||
// This is for the case that there are multiple function pods for the same function due to unknown reason.
|
||||
if !fscache.IsNameExistError(err) {
|
||||
gpm.logger.Warn("failed to adopt pod for function", zap.Error(err), zap.String("pod", pod.Name))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -373,8 +374,31 @@ func (gpm *GenericPoolManager) AdoptOrphanResources() {
|
||||
}()
|
||||
}
|
||||
|
||||
envWG.Wait()
|
||||
podWG.Wait()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) CleanupOldExecutorObjects() {
|
||||
gpm.logger.Info("Poolmanager starts to clean orphaned resources", zap.String("instanceID", gpm.instanceId))
|
||||
|
||||
errs := &multierror.Error{}
|
||||
listOpts := metav1.ListOptions{
|
||||
LabelSelector: labels.Set(map[string]string{types.EXECUTOR_TYPE: string(fv1.ExecutorTypePoolmgr)}).AsSelector().String(),
|
||||
}
|
||||
|
||||
err := reaper.CleanupDeployments(gpm.logger, gpm.kubernetesClient, gpm.instanceId, listOpts)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
|
||||
err = reaper.CleanupPods(gpm.logger, gpm.kubernetesClient, gpm.instanceId, listOpts)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
|
||||
if errs.ErrorOrNil() != nil {
|
||||
// TODO retry reaper; logged and ignored for now
|
||||
gpm.logger.Error("Failed to cleanup old executor objects", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) service() {
|
||||
@@ -578,9 +602,12 @@ func (gpm *GenericPoolManager) idleObjectReaper() {
|
||||
}
|
||||
if deleted {
|
||||
for i := range fsvc.KubernetesObjects {
|
||||
gpm.logger.Debug("release idle function resources",
|
||||
zap.String("function", fsvc.Name), zap.String("address", fsvc.Address),
|
||||
zap.String("executor", string(fsvc.Executor)))
|
||||
gpm.logger.Info("release idle function resources",
|
||||
zap.String("function", fsvc.Function.Name),
|
||||
zap.String("address", fsvc.Address),
|
||||
zap.String("executor", string(fsvc.Executor)),
|
||||
zap.String("pod", fsvc.Name),
|
||||
)
|
||||
reaper.CleanupKubeObject(gpm.logger, gpm.kubernetesClient, &fsvc.KubernetesObjects[i])
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fission/fission/pkg/utils"
|
||||
"go.uber.org/zap"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -29,6 +28,7 @@ import (
|
||||
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
|
||||
"github.com/fission/fission/pkg/crd"
|
||||
"github.com/fission/fission/pkg/types"
|
||||
"github.com/fission/fission/pkg/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -36,49 +36,6 @@ var (
|
||||
delOpt = meta_v1.DeleteOptions{PropagationPolicy: &deletePropagation}
|
||||
)
|
||||
|
||||
// CleanupOldExecutorObjects cleans up resources created by old executor instances
|
||||
func CleanupOldExecutorObjects(logger *zap.Logger, kubernetesClient *kubernetes.Clientset, instanceId string) {
|
||||
err := cleanup(logger, kubernetesClient, instanceId)
|
||||
if err != nil {
|
||||
// TODO retry reaper; logged and ignored for now
|
||||
logger.Error("Failed to cleanup old executor objects", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func cleanup(logger *zap.Logger, client *kubernetes.Clientset, instanceId string) error {
|
||||
// Pods might still be running user functions, so we give them
|
||||
// a few minutes before terminating them. This time is the
|
||||
// maximum function runtime, plus the time a router might
|
||||
// still route to an old instance, i.e. router cache expiry
|
||||
// time.
|
||||
time.Sleep(6 * time.Minute)
|
||||
|
||||
err := cleanupServices(logger, client, instanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cleanupHpa(logger, client, instanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deployments are used for idle pools and can be cleaned up
|
||||
// immediately. (We should "adopt" these instead of creating
|
||||
// a new pool.)
|
||||
err = cleanupDeployments(logger, client, instanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cleanupPods(logger, client, instanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupKubeObject deletes given kubernetes object
|
||||
func CleanupKubeObject(logger *zap.Logger, kubeClient *kubernetes.Clientset, kubeobj *apiv1.ObjectReference) {
|
||||
switch strings.ToLower(kubeobj.Kind) {
|
||||
@@ -112,28 +69,19 @@ func CleanupKubeObject(logger *zap.Logger, kubeClient *kubernetes.Clientset, kub
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupDeployments(logger *zap.Logger, client *kubernetes.Clientset, instanceId string) error {
|
||||
deploymentList, err := client.AppsV1().Deployments(meta_v1.NamespaceAll).List(meta_v1.ListOptions{})
|
||||
func CleanupDeployments(logger *zap.Logger, client *kubernetes.Clientset, instanceId string, listOps meta_v1.ListOptions) error {
|
||||
deploymentList, err := client.AppsV1().Deployments(meta_v1.NamespaceAll).List(listOps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, dep := range deploymentList.Items {
|
||||
id, ok := dep.ObjectMeta.Annotations[types.EXECUTOR_INSTANCEID_LABEL]
|
||||
if ok && id != instanceId {
|
||||
logger.Debug("cleaning up deployment", zap.String("deployment", dep.ObjectMeta.Name))
|
||||
err := client.AppsV1().Deployments(dep.ObjectMeta.Namespace).Delete(dep.ObjectMeta.Name, &delOpt)
|
||||
if err != nil {
|
||||
logger.Error("error cleaning up deployment",
|
||||
zap.Error(err),
|
||||
zap.String("deployment_name", dep.ObjectMeta.Name),
|
||||
zap.String("deployment_namespace", dep.ObjectMeta.Namespace))
|
||||
}
|
||||
// ignore err
|
||||
if !ok {
|
||||
// Backward compatibility with older label name
|
||||
id, ok = dep.ObjectMeta.Labels[types.EXECUTOR_INSTANCEID_LABEL]
|
||||
}
|
||||
// Backward compatibility with older label name
|
||||
pid, pok := dep.ObjectMeta.Annotations[types.POOLMGR_INSTANCEID_LABEL]
|
||||
if pok && pid != instanceId {
|
||||
logger.Debug("cleaning up deployment", zap.String("deployment", dep.ObjectMeta.Name))
|
||||
if ok && id != instanceId {
|
||||
logger.Info("cleaning up deployment", zap.String("deployment", dep.ObjectMeta.Name))
|
||||
err := client.AppsV1().Deployments(dep.ObjectMeta.Namespace).Delete(dep.ObjectMeta.Name, &delOpt)
|
||||
if err != nil {
|
||||
logger.Error("error cleaning up deployment",
|
||||
@@ -147,15 +95,19 @@ func cleanupDeployments(logger *zap.Logger, client *kubernetes.Clientset, instan
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanupPods(logger *zap.Logger, client *kubernetes.Clientset, instanceId string) error {
|
||||
podList, err := client.CoreV1().Pods(meta_v1.NamespaceAll).List(meta_v1.ListOptions{})
|
||||
func CleanupPods(logger *zap.Logger, client *kubernetes.Clientset, instanceId string, listOps meta_v1.ListOptions) error {
|
||||
podList, err := client.CoreV1().Pods(meta_v1.NamespaceAll).List(listOps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pod := range podList.Items {
|
||||
id, ok := pod.ObjectMeta.Annotations[types.EXECUTOR_INSTANCEID_LABEL]
|
||||
if !ok {
|
||||
// Backward compatibility with older label name
|
||||
id, ok = pod.ObjectMeta.Labels[types.EXECUTOR_INSTANCEID_LABEL]
|
||||
}
|
||||
if ok && id != instanceId {
|
||||
logger.Debug("cleaning up pod", zap.String("pod", pod.ObjectMeta.Name))
|
||||
logger.Info("cleaning up pod", zap.String("pod", pod.ObjectMeta.Name))
|
||||
err := client.CoreV1().Pods(pod.ObjectMeta.Namespace).Delete(pod.ObjectMeta.Name, nil)
|
||||
if err != nil {
|
||||
logger.Error("error cleaning up pod",
|
||||
@@ -165,31 +117,23 @@ func cleanupPods(logger *zap.Logger, client *kubernetes.Clientset, instanceId st
|
||||
}
|
||||
// ignore err
|
||||
}
|
||||
// Backward compatibility with older label name
|
||||
pid, pok := pod.ObjectMeta.Annotations[types.POOLMGR_INSTANCEID_LABEL]
|
||||
if pok && pid != instanceId {
|
||||
logger.Debug("cleaning up pod", zap.String("pod", pod.ObjectMeta.Name))
|
||||
err := client.CoreV1().Pods(pod.ObjectMeta.Namespace).Delete(pod.ObjectMeta.Name, nil)
|
||||
if err != nil {
|
||||
logger.Error("error cleaning up pod",
|
||||
zap.Error(err),
|
||||
zap.String("pod_name", pod.ObjectMeta.Name),
|
||||
zap.String("pod_namespace", pod.ObjectMeta.Namespace))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanupServices(logger *zap.Logger, client *kubernetes.Clientset, instanceId string) error {
|
||||
svcList, err := client.CoreV1().Services(meta_v1.NamespaceAll).List(meta_v1.ListOptions{})
|
||||
func CleanupServices(logger *zap.Logger, client *kubernetes.Clientset, instanceId string, listOps meta_v1.ListOptions) error {
|
||||
svcList, err := client.CoreV1().Services(meta_v1.NamespaceAll).List(listOps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, svc := range svcList.Items {
|
||||
id, ok := svc.ObjectMeta.Annotations[types.EXECUTOR_INSTANCEID_LABEL]
|
||||
if !ok {
|
||||
// Backward compatibility with older label name
|
||||
id, ok = svc.ObjectMeta.Labels[types.EXECUTOR_INSTANCEID_LABEL]
|
||||
}
|
||||
if ok && id != instanceId {
|
||||
logger.Debug("cleaning up service", zap.String("service", svc.ObjectMeta.Name))
|
||||
logger.Info("cleaning up service", zap.String("service", svc.ObjectMeta.Name))
|
||||
err := client.CoreV1().Services(svc.ObjectMeta.Namespace).Delete(svc.ObjectMeta.Name, nil)
|
||||
if err != nil {
|
||||
logger.Error("error cleaning up service",
|
||||
@@ -203,16 +147,20 @@ func cleanupServices(logger *zap.Logger, client *kubernetes.Clientset, instanceI
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanupHpa(logger *zap.Logger, client *kubernetes.Clientset, instanceId string) error {
|
||||
hpaList, err := client.AutoscalingV1().HorizontalPodAutoscalers(meta_v1.NamespaceAll).List(meta_v1.ListOptions{})
|
||||
func CleanupHpa(logger *zap.Logger, client *kubernetes.Clientset, instanceId string, listOps meta_v1.ListOptions) error {
|
||||
hpaList, err := client.AutoscalingV1().HorizontalPodAutoscalers(meta_v1.NamespaceAll).List(listOps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, hpa := range hpaList.Items {
|
||||
id, ok := hpa.ObjectMeta.Annotations[types.EXECUTOR_INSTANCEID_LABEL]
|
||||
if !ok {
|
||||
// Backward compatibility with older label name
|
||||
id, ok = hpa.ObjectMeta.Labels[types.EXECUTOR_INSTANCEID_LABEL]
|
||||
}
|
||||
if ok && id != instanceId {
|
||||
logger.Debug("cleaning up HPA", zap.String("hpa", hpa.ObjectMeta.Name))
|
||||
logger.Info("cleaning up HPA", zap.String("hpa", hpa.ObjectMeta.Name))
|
||||
err := client.AutoscalingV1().HorizontalPodAutoscalers(hpa.ObjectMeta.Namespace).Delete(hpa.ObjectMeta.Name, nil)
|
||||
if err != nil {
|
||||
logger.Error("error cleaning up HPA",
|
||||
|
||||
@@ -17,6 +17,9 @@ limitations under the License.
|
||||
package util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
@@ -28,6 +31,20 @@ import (
|
||||
// when creating the environment deployment since kubelet will retry to
|
||||
// pull image until successes.
|
||||
func ApplyImagePullSecret(secret string, podspec apiv1.PodSpec) *apiv1.PodSpec {
|
||||
podspec.ImagePullSecrets = []apiv1.LocalObjectReference{{Name: secret}}
|
||||
if len(secret) > 0 {
|
||||
podspec.ImagePullSecrets = []apiv1.LocalObjectReference{{Name: secret}}
|
||||
}
|
||||
return &podspec
|
||||
}
|
||||
|
||||
func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) {
|
||||
waitCh := make(chan struct{})
|
||||
go func() {
|
||||
defer close(waitCh)
|
||||
wg.Wait()
|
||||
}()
|
||||
select {
|
||||
case <-waitCh:
|
||||
case <-time.After(timeout):
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,16 +102,13 @@ func (opts *TestSubCommand) do(input cli.Input) error {
|
||||
functionUrl.RawQuery = query.Encode()
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
if deadline := input.Duration(flagkey.FnTestTimeout); deadline > 0 {
|
||||
var closeCtx func()
|
||||
ctx, closeCtx = context.WithTimeout(ctx, deadline)
|
||||
defer closeCtx()
|
||||
}
|
||||
ctx, closeCtx := context.WithTimeout(context.Background(), input.Duration(flagkey.FnTestTimeout))
|
||||
defer closeCtx()
|
||||
|
||||
headers := input.StringSlice(flagkey.FnTestHeader)
|
||||
|
||||
resp, err := doHTTPRequest(ctx, input.String(flagkey.HtMethod), functionUrl.String(), input.String(flagkey.FnTestBody), headers)
|
||||
resp, err := doHTTPRequest(ctx, functionUrl.String(),
|
||||
input.StringSlice(flagkey.FnTestHeader),
|
||||
input.String(flagkey.HtMethod),
|
||||
input.String(flagkey.FnTestBody))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -123,21 +120,25 @@ func (opts *TestSubCommand) do(input cli.Input) error {
|
||||
}
|
||||
|
||||
if resp.StatusCode < 400 {
|
||||
fmt.Print(string(body))
|
||||
os.Stdout.Write(body)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Error calling function %s: %d; Please try again or fix the error: %s", m.Name, resp.StatusCode, string(body))
|
||||
err = printPodLogs(input)
|
||||
console.Errorf("Error calling function %s: %d; Please try again or fix the error: %s\n", m.Name, resp.StatusCode, string(body))
|
||||
log, err := printPodLogs(opts.client, m)
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting function logs from pod: %v. Try to get logs from log database", err)
|
||||
return Log(input)
|
||||
console.Errorf("Error getting function logs from controller: %v. Try to get logs from log database.", err)
|
||||
err = Log(input)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error retrieving function log from log database")
|
||||
}
|
||||
} else {
|
||||
console.Info(log)
|
||||
}
|
||||
|
||||
return nil
|
||||
return errors.New("error getting function response")
|
||||
}
|
||||
|
||||
func doHTTPRequest(ctx context.Context, method, url, body string, headers []string) (*http.Response, error) {
|
||||
func doHTTPRequest(ctx context.Context, url string, headers []string, method, body string) (*http.Response, error) {
|
||||
method, err := httptrigger.GetMethod(method)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -163,41 +164,21 @@ func doHTTPRequest(ctx context.Context, method, url, body string, headers []stri
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func printPodLogs(input cli.Input) error {
|
||||
fnName := input.String(flagkey.FnName)
|
||||
|
||||
u, err := util.GetApplicationUrl("application=fission-api")
|
||||
func printPodLogs(client *client.Client, fnMeta *metav1.ObjectMeta) (string, error) {
|
||||
reader, statusCode, err := client.FunctionPodLogs(fnMeta)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", errors.Wrap(err, "error executing get logs request")
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
queryURL, err := url.Parse(u)
|
||||
body, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error parsing the base URL")
|
||||
}
|
||||
queryURL.Path = fmt.Sprintf("/proxy/logs/%s", fnName)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error creating logs request")
|
||||
return "", errors.Wrap(err, "error reading the response body")
|
||||
}
|
||||
|
||||
httpClient := http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "execute get logs request")
|
||||
if statusCode != http.StatusOK {
|
||||
return string(body), errors.Errorf("error getting logs from controller, status code: '%v'", statusCode)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New("get logs from pod directly")
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read the response body")
|
||||
}
|
||||
|
||||
fmt.Println(string(body))
|
||||
return nil
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func CreateArchive(client *client.Client, includeFiles []string, noZip bool, ins
|
||||
}
|
||||
|
||||
path := filepath.Join(rootDir, path)
|
||||
files, err := utils.FindAllGlobs([]string{path})
|
||||
files, err := utils.FindAllGlobs(path)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, errors.Wrap(err, "error finding all globs"))
|
||||
continue
|
||||
@@ -198,7 +198,7 @@ func makeArchiveFile(archiveNameHint string, archiveInput []string, noZip bool)
|
||||
archiveName := archiveName(archiveNameHint, archiveInput)
|
||||
|
||||
// Get files from inputs as number of files decide next steps
|
||||
files, err := utils.FindAllGlobs(archiveInput)
|
||||
files, err := utils.FindAllGlobs(archiveInput...)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error finding all globs")
|
||||
}
|
||||
|
||||
@@ -427,13 +427,13 @@ func localArchiveFromSpec(specDir string, aus *spectypes.ArchiveUploadSpec) (*fv
|
||||
files = append(files, aus.IncludeGlobs[0])
|
||||
} else {
|
||||
for _, relativeGlob := range aus.IncludeGlobs {
|
||||
absGlob := rootDir + "/" + relativeGlob
|
||||
f, err := filepath.Glob(absGlob)
|
||||
absGlob := filepath.Join(rootDir, relativeGlob)
|
||||
console.Verbose(2, "try to find globs in path '%v'", absGlob)
|
||||
fs, err := utils.FindAllGlobs(absGlob)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Invalid glob in archive %v: %v", aus.Name, relativeGlob)
|
||||
}
|
||||
files = append(files, f...)
|
||||
// xxx handle excludeGlobs here
|
||||
files = append(files, fs...)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,13 +105,20 @@ func (opts *InitSubCommand) complete(input cli.Input) error {
|
||||
func (opts *InitSubCommand) run(input cli.Input) error {
|
||||
specDir := util.GetSpecDir(input)
|
||||
|
||||
readme := filepath.Join(specDir, "README")
|
||||
config := filepath.Join(specDir, "fission-deployment-config.yaml")
|
||||
|
||||
if _, err := os.Stat(config); err == nil {
|
||||
return errors.Errorf("Spec DeploymentConfig already exists in directory '%v'", specDir)
|
||||
}
|
||||
|
||||
// Add a bit of documentation to the spec dir here
|
||||
err := ioutil.WriteFile(filepath.Join(specDir, "README"), []byte(SPEC_README), 0644)
|
||||
err := ioutil.WriteFile(readme, []byte(SPEC_README), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeDeploymentConfig(specDir, opts.deployConfig)
|
||||
err = writeDeploymentConfig(config, opts.deployConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error writing deployment config")
|
||||
}
|
||||
@@ -124,7 +131,7 @@ func (opts *InitSubCommand) run(input cli.Input) error {
|
||||
|
||||
// writeDeploymentConfig serializes the DeploymentConfig to YAML and writes it to a new
|
||||
// fission-config.yaml in specDir.
|
||||
func writeDeploymentConfig(specDir string, dc *spectypes.DeploymentConfig) error {
|
||||
func writeDeploymentConfig(file string, dc *spectypes.DeploymentConfig) error {
|
||||
y, err := yaml.Marshal(dc)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -134,7 +141,7 @@ func writeDeploymentConfig(specDir string, dc *spectypes.DeploymentConfig) error
|
||||
"# See the README in this directory for background and usage information.\n" +
|
||||
"# Do not edit the UID below: that will break 'fission spec apply'\n")
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(specDir, "fission-deployment-config.yaml"), append(msg, y...), 0644)
|
||||
err = ioutil.WriteFile(file, append(msg, y...), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -297,35 +297,26 @@ func (fr *FissionResources) Validate(input cli.Input) error {
|
||||
for _, p := range fr.Packages {
|
||||
packages[MapKey(&p.Metadata)] = false
|
||||
|
||||
if strings.HasPrefix(p.Spec.Deployment.URL, ARCHIVE_URL_PREFIX) {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
as := map[string]string{
|
||||
"source": p.Spec.Source.URL,
|
||||
"deployment": p.Spec.Deployment.URL,
|
||||
}
|
||||
|
||||
if strings.HasPrefix(p.Spec.Deployment.URL, ARCHIVE_URL_PREFIX) {
|
||||
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
|
||||
for archiveType, u := range as {
|
||||
if strings.HasPrefix(u, ARCHIVE_URL_PREFIX) {
|
||||
aname := strings.TrimPrefix(u, ARCHIVE_URL_PREFIX)
|
||||
if len(aname) > 0 {
|
||||
if _, ok := archives[aname]; !ok {
|
||||
result = multierror.Append(result, fmt.Errorf(
|
||||
"%v: package '%v' references unknown %v archive '%v%v'",
|
||||
fr.SourceMap.Locations["Package"][p.Metadata.Namespace][p.Metadata.Name],
|
||||
p.Metadata.Name,
|
||||
archiveType,
|
||||
ARCHIVE_URL_PREFIX,
|
||||
aname))
|
||||
} else {
|
||||
archives[aname] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ func Error(msg interface{}) {
|
||||
os.Stderr.WriteString(fmt.Sprintf("%v: %v\n", color.RedString("Error"), trimNewline(msg)))
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
os.Stderr.WriteString(fmt.Sprintf("%v: %v\n", color.RedString("Error"), trimNewline(msg)))
|
||||
}
|
||||
|
||||
func Warn(msg interface{}) {
|
||||
os.Stdout.WriteString(fmt.Sprintf("%v: %v\n", color.YellowString("Warning"), trimNewline(msg)))
|
||||
}
|
||||
|
||||
@@ -91,7 +91,6 @@ const (
|
||||
)
|
||||
|
||||
const EXECUTOR_INSTANCEID_LABEL = fv1.EXECUTOR_INSTANCEID_LABEL
|
||||
const POOLMGR_INSTANCEID_LABEL = fv1.POOLMGR_INSTANCEID_LABEL
|
||||
|
||||
const (
|
||||
ChecksumTypeSHA256 = fv1.ChecksumTypeSHA256
|
||||
|
||||
+13
-7
@@ -21,7 +21,6 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@@ -33,6 +32,7 @@ import (
|
||||
"github.com/mholt/archiver"
|
||||
"github.com/pkg/errors"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -103,20 +103,26 @@ func GetTempDir() (string, error) {
|
||||
}
|
||||
|
||||
// FindAllGlobs returns a list of globs of input list.
|
||||
func FindAllGlobs(inputList []string) ([]string, error) {
|
||||
func FindAllGlobs(paths ...string) ([]string, error) {
|
||||
files := make([]string, 0)
|
||||
for _, glob := range inputList {
|
||||
f, err := filepath.Glob(glob)
|
||||
for _, p := range paths {
|
||||
// use absolute path to find files
|
||||
path, err := filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid glob %v: %v", glob, err)
|
||||
return nil, errors.Wrapf(err, "error getting absolute path of path '%v'", p)
|
||||
}
|
||||
files = append(files, f...)
|
||||
globs, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid glob %v: %v", path, err)
|
||||
}
|
||||
files = append(files, globs...)
|
||||
// xxx handle excludeGlobs here
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func MakeZipArchive(targetName string, globs ...string) (string, error) {
|
||||
files, err := FindAllGlobs(globs)
|
||||
files, err := FindAllGlobs(globs...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+1
-1
@@ -533,6 +533,7 @@ run_all_tests() {
|
||||
$ROOT/test/tests/test_specs/test_spec.sh \
|
||||
$ROOT/test/tests/test_specs/test_spec_multifile.sh \
|
||||
$ROOT/test/tests/test_specs/test_spec_merge/test_spec_merge.sh \
|
||||
$ROOT/test/tests/test_specs/test_spec_archive/test_spec_archive.sh \
|
||||
$ROOT/test/tests/test_environments/test_tensorflow_serving_env.sh \
|
||||
$ROOT/test/tests/test_environments/test_go_env.sh \
|
||||
$ROOT/test/tests/mqtrigger/nats/test_mqtrigger.sh \
|
||||
@@ -540,7 +541,6 @@ run_all_tests() {
|
||||
$ROOT/test/tests/test_huge_response/test_huge_response.sh
|
||||
FAILURES=$?
|
||||
|
||||
# FIXME: run tests with newdeploy one by one.
|
||||
export JOBS=3
|
||||
$ROOT/test/run_test.sh \
|
||||
$ROOT/test/tests/test_backend_newdeploy.sh \
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
Fission Specs
|
||||
=============
|
||||
|
||||
This is a set of specifications for a Fission app. This includes functions,
|
||||
environments, and triggers; we collectively call these things "resources".
|
||||
|
||||
How to use these specs
|
||||
----------------------
|
||||
|
||||
These specs are handled with the 'fission spec' command. See 'fission spec --help'.
|
||||
|
||||
'fission spec apply' will "apply" all resources specified in this directory to your
|
||||
cluster. That means it checks what resources exist on your cluster, what resources are
|
||||
specified in the specs directory, and reconciles the difference by creating, updating or
|
||||
deleting resources on the cluster.
|
||||
|
||||
'fission spec apply' will also package up your source code (or compiled binaries) and
|
||||
upload the archives to the cluster if needed. It uses 'ArchiveUploadSpec' resources in
|
||||
this directory to figure out which files to archive.
|
||||
|
||||
You can use 'fission spec apply --watch' to watch for file changes and continuously keep
|
||||
the cluster updated.
|
||||
|
||||
You can add YAMLs to this directory by writing them manually, but it's easier to generate
|
||||
them. Use 'fission function create --spec' to generate a function spec,
|
||||
'fission environment create --spec' to generate an environment spec, and so on.
|
||||
|
||||
You can edit any of the files in this directory, except 'fission-deployment-config.yaml',
|
||||
which contains a UID that you should never change. To apply your changes simply use
|
||||
'fission spec apply'.
|
||||
|
||||
fission-deployment-config.yaml
|
||||
------------------------------
|
||||
|
||||
fission-deployment-config.yaml contains a UID. This UID is what fission uses to correlate
|
||||
resources on the cluster to resources in this directory.
|
||||
|
||||
All resources created by 'fission spec apply' are annotated with this UID. Resources on
|
||||
the cluster that are _not_ annotated with this UID are never modified or deleted by
|
||||
fission.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: fission.io/v1
|
||||
kind: Environment
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: dummyfoobarnode
|
||||
namespace: default
|
||||
spec:
|
||||
builder:
|
||||
command: build
|
||||
image: fission/node-builder:1.6.0
|
||||
keeparchive: false
|
||||
poolsize: 3
|
||||
runtime:
|
||||
image: fission/node-env:1.6.0
|
||||
terminationGracePeriod: 20
|
||||
version: 2
|
||||
@@ -0,0 +1,7 @@
|
||||
# This file is generated by the 'fission spec init' command.
|
||||
# See the README in this directory for background and usage information.
|
||||
# Do not edit the UID below: that will break 'fission spec apply'
|
||||
apiVersion: fission.io/v1
|
||||
kind: DeploymentConfig
|
||||
name: test-spec-archive
|
||||
uid: 04b21526-8873-4dc2-b897-e87ed5347670
|
||||
@@ -0,0 +1,56 @@
|
||||
apiVersion: fission.io/v1
|
||||
kind: Function
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: sourcearchive
|
||||
namespace: default
|
||||
spec:
|
||||
InvokeStrategy:
|
||||
ExecutionStrategy:
|
||||
ExecutorType: poolmgr
|
||||
MaxScale: 0
|
||||
MinScale: 0
|
||||
SpecializationTimeout: 0
|
||||
TargetCPUPercent: 0
|
||||
StrategyType: execution
|
||||
configmaps: null
|
||||
environment:
|
||||
name: dummyfoobarnode
|
||||
namespace: default
|
||||
functionTimeout: 60
|
||||
package:
|
||||
functionName: source
|
||||
packageref:
|
||||
name: sourcearchive
|
||||
namespace: default
|
||||
resources: {}
|
||||
secrets: null
|
||||
|
||||
---
|
||||
apiVersion: fission.io/v1
|
||||
kind: Function
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: deployarchive
|
||||
namespace: default
|
||||
spec:
|
||||
InvokeStrategy:
|
||||
ExecutionStrategy:
|
||||
ExecutorType: poolmgr
|
||||
MaxScale: 0
|
||||
MinScale: 0
|
||||
SpecializationTimeout: 0
|
||||
TargetCPUPercent: 0
|
||||
StrategyType: execution
|
||||
configmaps: null
|
||||
environment:
|
||||
name: dummyfoobarnode
|
||||
namespace: default
|
||||
functionTimeout: 60
|
||||
package:
|
||||
functionName: deploy
|
||||
packageref:
|
||||
name: deployarchive
|
||||
namespace: default
|
||||
resources: {}
|
||||
secrets: null
|
||||
@@ -0,0 +1,24 @@
|
||||
include:
|
||||
- func/*
|
||||
kind: ArchiveUploadSpec
|
||||
name: functions-deploy-archive
|
||||
|
||||
---
|
||||
apiVersion: fission.io/v1
|
||||
kind: Package
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: deployarchive
|
||||
namespace: default
|
||||
spec:
|
||||
deployment:
|
||||
checksum: {}
|
||||
type: url
|
||||
url: archive://functions-source-archive
|
||||
environment:
|
||||
name: dummyfoobarnode
|
||||
namespace: default
|
||||
source:
|
||||
checksum: {}
|
||||
status:
|
||||
buildstatus: none
|
||||
@@ -0,0 +1,24 @@
|
||||
include:
|
||||
- func/*
|
||||
kind: ArchiveUploadSpec
|
||||
name: functions-source-archive
|
||||
|
||||
---
|
||||
apiVersion: fission.io/v1
|
||||
kind: Package
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: sourcearchive
|
||||
namespace: default
|
||||
spec:
|
||||
deployment:
|
||||
checksum: {}
|
||||
environment:
|
||||
name: dummyfoobarnode
|
||||
namespace: default
|
||||
source:
|
||||
checksum: {}
|
||||
type: url
|
||||
url: archive://functions-deploy-archive
|
||||
status:
|
||||
buildstatus: pending
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
source $(dirname $0)/../../../utils.sh
|
||||
ROOT=` realpath $(dirname $0)/../../../../`
|
||||
|
||||
cleanup() {
|
||||
log "Cleaning up..."
|
||||
fission spec destroy
|
||||
rm -rf func
|
||||
popd
|
||||
}
|
||||
|
||||
if [ -z "${TEST_NOCLEANUP:-}" ]; then
|
||||
trap cleanup EXIT
|
||||
else
|
||||
log "TEST_NOCLEANUP is set; not cleaning up test artifacts afterwards."
|
||||
fi
|
||||
|
||||
pushd $(dirname $0)
|
||||
|
||||
[ -d specs ]
|
||||
[ -f specs/README ]
|
||||
[ -f specs/fission-deployment-config.yaml ]
|
||||
|
||||
mkdir -p func
|
||||
cp $ROOT/examples/nodejs/hello.js func/deploy.js
|
||||
cp $ROOT/examples/nodejs/hello.js func/source.js
|
||||
|
||||
fission spec destroy || true
|
||||
|
||||
log "Apply specs"
|
||||
fission --verbosity 2 spec apply
|
||||
|
||||
log "verify deployarchive function works"
|
||||
fission fn test --name deployarchive
|
||||
|
||||
timeout 60s bash -c "waitBuild sourcearchive"
|
||||
|
||||
log "verify sourcearchive function works"
|
||||
fission fn test --name sourcearchive
|
||||
|
||||
log "Test PASSED"
|
||||
Reference in New Issue
Block a user