Compare commits

...
13 Commits
Author SHA1 Message Date
Ta-Ching Chen d6a8734aae Fission 1.7.0 2019-12-02 21:26:28 +08:00
Ta-Ching ChenandGitHub 3ee98cd13a Fix release script not uploads OpenShift deploy YAML file (#1456) 2019-12-02 21:12:15 +08:00
Ta-Ching ChenandGitHub 275da18cf6 Let executor type manages how to do cleanup for old kubeobjects (#1455)
Add CleanupOldExecutorObjects to executor type interface in order
to let an executor type manages how to clean up the resources it created.
2019-12-02 19:47:31 +08:00
Ta-Ching ChenandGitHub 3f3b11ffbf Prevent deployment from rolling update due to different instance-id (#1454)
The pod template is embedded inside the deployment. So if
the pod annotation contains instance-id, the deployment
will get updated and thus triggers a rolling update whenever
a new executor starts which is unwanted.

After this PR, poolmanager will patches instance-id when a
pod is chosen for a function.

For newdeploy, unlike poolmanager manages the lifecycle
of function pod directly, newdeploy is only responsible
to create the deployment so we append instance-id to top-
level controller (deployment) only.
2019-12-02 17:28:19 +08:00
Ta-Ching ChenandGitHub 7f8cb69326 Make AdoptExistingResources optional (#1453) 2019-12-02 07:34:07 +08:00
Ta-Ching ChenandGitHub 003c304105 Prevent newdeploy updates deployment if no resources changed (#1452)
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 triggering 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 res-
ources changed without affecting by 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.
2019-12-02 06:35:07 +08:00
Ta-Ching ChenandGitHub 763ab475f2 Fix CLI unable to get pod logs from controller (#1451) 2019-12-01 22:38:35 +08:00
Ta-Ching ChenandGitHub 4b3f48b537 Fix spec shows source archive is not used (#1448) 2019-12-01 16:51:13 +08:00
Ta-Ching ChenandGitHub 6301a78814 Ignore hidden file when creating archive file (#1450) 2019-12-01 09:58:16 +08:00
Ta-Ching ChenandGitHub 506b427124 Fix spec init overrides existing deploymentconfig (#1449) 2019-11-29 23:59:10 +08:00
Ta-Ching ChenandGitHub 19ae7d5ac6 Fix adopted deployment uses old fetcher image (#1447)
When a new executor starts up, it adopts the orphan kubernetes resources created
by the old executor instance. However, the adopted resource won't reflect the changes
come with the new executor, for example, the fetcher image inside won't be changed.

To solve this, executor updates the resource spec (HPA/Deployment/Service) with the
latest resources spec. By doing this, we can prevent the inconsistency between resources
created by different executor instance, also minimizes the impact on users.
2019-11-29 21:52:59 +08:00
Ta-Ching ChenandGitHub 47aaa85108 Improve executor bootstrap speed (#1446) 2019-11-29 14:18:23 +08:00
Ta-Ching Chen 3067ecdee3 CHANGELOG 1.7.0-rc.2 2019-11-27 17:10:57 +00:00
38 changed files with 938 additions and 450 deletions
+52 -18
View File
@@ -1,22 +1,56 @@
# 1.7.0-rc.1
# 1.7.0-rc.2
[Documentation](https://docs.fission.io/)
## Downloads for 1.7.0-rc.1
## Downloads for 1.7.0-rc.2
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-all-1.7.0-rc.2-openshift.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-all-1.7.0-rc.2-openshift.yaml) | `67c0ffcb0e156b87237cd3a712a1435f09365701f4c516d5e9a83cb3f1c73041`
[fission-core-1.7.0-rc.2.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-core-1.7.0-rc.2.yaml) | `79e620b73b6a6b9f3f0290aa5b8b6daf7f77e938e34e839fc8b84cee00351b49`
[fission-core-1.7.0-rc.2-openshift.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-core-1.7.0-rc.2-openshift.yaml) | `a180331a308500cc6bc936b6c6f262bef69e6d75d5d0a1a54e5912845822ff51`
[fission-all-1.7.0-rc.2-minikube.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-all-1.7.0-rc.2-minikube.yaml) | `7c43a8acc3125e4a25444cedda15e045c90d911689cb18042ce7c4a0f7b6cc73`
[fission-core-1.7.0-rc.2-minikube.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-core-1.7.0-rc.2-minikube.yaml) | `9c052808104ae833e7d2ed23403f2c84a595f525ef7b9e53dee6b70a810cf032`
[fission-all-1.7.0-rc.2.yaml](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-all-1.7.0-rc.2.yaml) | `594ebff290c13ad590a0fa44bbbd45a5b5e0ee6fac853d197e7bc756773c5089`
[fission-core-1.7.0-rc.2.tgz](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-core-1.7.0-rc.2.tgz) | `8ba194e2bf9cf5ea95b851093675268d1bcb713bdf2f9f8ed0fae26fe7731718`
[fission-all-1.7.0-rc.2.tgz](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-all-1.7.0-rc.2.tgz) | `76359916272b408ee21e18a74e230d5f344bea5d95a050fc931547760a8b52b1`
[fission-cli-osx](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-cli-osx) | `0b79ba3bd1d74e3fdac12f966753c5268df3f32f7fdab3d11431fdfbd8d3e5d2`
[fission-cli-windows.exe](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-cli-windows.exe) | `9f94e3ad7248b7f5883e78a8a7c0fb41ec7467680be445582e7e5fb1f6a62a50`
[fission-cli-linux](https://github.com/fission/fission/releases/download/1.7.0-rc.2/fission-cli-linux) | `0f37a1164df007c540e36dead66226fc9919ad5182aa479baabc46da2e9ee72f`
# Change Log
## [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,6 +189,7 @@ 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))
@@ -165,7 +200,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))
@@ -211,16 +245,20 @@ 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))
- V1.2.1 [\#1178](https://github.com/fission/fission/pull/1178) ([vishal-biyani](https://github.com/vishal-biyani))
- Skaffold for Fission [\#1172](https://github.com/fission/fission/pull/1172) ([vishal-biyani](https://github.com/vishal-biyani))
- Add affinity support [\#1170](https://github.com/fission/fission/pull/1170) ([laurence-hudson-mindfoundry](https://github.com/laurence-hudson-mindfoundry))
- Using templated imagePullPolicy for containers in deployment.yaml [\#1137](https://github.com/fission/fission/pull/1137) ([msshroff](https://github.com/msshroff))
- 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,8 +274,6 @@ 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))
@@ -245,7 +281,6 @@ filename | sha256 hash
- Fix TravisCI go environment version to avoid go bugs [\#1154](https://github.com/fission/fission/pull/1154) ([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))
- Migrate from glide to official dependencies management tool: Go Module [\#1136](https://github.com/fission/fission/pull/1136) ([life1347](https://github.com/life1347))
- Fix misleading log when setup portforward [\#1134](https://github.com/fission/fission/pull/1134) ([life1347](https://github.com/life1347))
- V1.1.0 [\#1129](https://github.com/fission/fission/pull/1129) ([vishal-biyani](https://github.com/vishal-biyani))
@@ -274,7 +309,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 +425,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 +446,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))
@@ -929,7 +963,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 +970,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
View File
@@ -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.0`
`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.0`
`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`
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v1
name: fission-all
version: 1.7.0-rc.2
version: 1.7.0
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.0
@@ -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
+5 -2
View File
@@ -20,13 +20,13 @@ image: fission/fission-bundle
pullPolicy: IfNotPresent
## Fission image version
imageTag: 1.7.0-rc.2
imageTag: 1.7.0
## Fission fetcher repository
fetcherImage: fission/fetcher
## Fission fetcher image version
fetcherImageTag: 1.7.0-rc.2
fetcherImageTag: 1.7.0
## 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
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v1
name: fission-core
version: 1.7.0-rc.2
version: 1.7.0
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.0
@@ -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
+5 -2
View File
@@ -17,7 +17,7 @@ repository: index.docker.io
image: fission/fission-bundle
## Fission image version
imageTag: 1.7.0-rc.2
imageTag: 1.7.0
## 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.0
## 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
View File
@@ -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
}
+2 -3
View File
@@ -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 (
+31
View File
@@ -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
}
+58 -28
View File
@@ -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
}
+3 -3
View File
@@ -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
+18 -5
View File
@@ -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)
+4 -1
View File
@@ -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()
}
+183 -94
View File
@@ -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
+26 -22
View File
@@ -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
+51 -24
View File
@@ -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)
}
+29 -81
View File
@@ -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",
+18 -1
View File
@@ -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):
}
}
+27 -46
View File
@@ -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
}
+2 -2
View File
@@ -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")
}
+4 -4
View File
@@ -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...)
}
}
+11 -4
View File
@@ -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
}
+18 -27
View File
@@ -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
}
}
}
}
+5
View File
@@ -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)))
}
-1
View File
@@ -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
+22 -8
View File
@@ -21,7 +21,6 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"golang.org/x/net/context/ctxhttp"
"io"
"io/ioutil"
"net"
@@ -33,10 +32,12 @@ 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"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
"github.com/fission/fission/pkg/fission-cli/console"
)
func UrlForFunction(name, namespace string) string {
@@ -102,21 +103,34 @@ func GetTempDir() (string, error) {
return dir, err
}
// FindAllGlobs returns a list of globs of input list.
func FindAllGlobs(inputList []string) ([]string, error) {
// FindAllGlobs ignores all hidden files and returns a list of globs of input list.
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)
}
globs, err := filepath.Glob(path)
if err != nil {
return nil, errors.Errorf("invalid glob %v: %v", path, err)
}
for _, f := range globs {
// ignore hidden file.
if strings.HasPrefix(filepath.Base(f), ".") {
console.Verbose(2, "Ignore hidden file '%v'", f)
continue
}
files = append(files, f)
// xxx handle excludeGlobs here
}
files = append(files, f...)
}
return files, nil
}
func MakeZipArchive(targetName string, globs ...string) (string, error) {
files, err := FindAllGlobs(globs)
files, err := FindAllGlobs(globs...)
if err != nil {
return "", err
}
+2 -1
View File
@@ -532,7 +532,9 @@ run_all_tests() {
$ROOT/test/tests/test_router_cache_invalidation.sh \
$ROOT/test/tests/test_specs/test_spec.sh \
$ROOT/test/tests/test_specs/test_spec_multifile.sh \
$ROOT/test/tests/test_specs/test_ignore_hidden_file.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 +542,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 \
+65
View File
@@ -0,0 +1,65 @@
#!/bin/bash
set -euo pipefail
source $(dirname $0)/../../utils.sh
ROOT=` realpath $(dirname $0)/../../../`
TEST_ID=$(generate_test_id)
cleanup() {
log "Cleaning up..."
fission spec destroy || true
rm -rf document specs
rm -rf ${TEST_ID}
popd
}
if [ -z "${TEST_NOCLEANUP:-}" ]; then
trap cleanup EXIT
else
log "TEST_NOCLEANUP is set; not cleaning up test artifacts afterwards."
fi
tmp_dir="/tmp/test-$TEST_ID"
mkdir -p $tmp_dir
pushd $tmp_dir
mkdir -p document
cp $ROOT/examples/nodejs/hello.js document/h1.js
cp $ROOT/examples/nodejs/hello.js document/h2.js
log "Create hidden file"
touch document/.im_invisible
log "Create specs"
fission spec init
fission pkg list
#fission env create --name nodejs --image fission/node-env --period 5 --version 2 --spec
fission pkg create --name nodejs --env nodejs --deploy "document/*" --spec
log "Apply specs"
fission --verbosity 2 spec apply
mkdir ${TEST_ID}
fission pkg getdeploy --name nodejs > ${TEST_ID}/a.zip
unzip ${TEST_ID}/a.zip -d ${TEST_ID}/
log "Check whether hidden file exists"
if [ -f ${TEST_ID}/.im_invisible ];
then
log "Found hidden file"
ls -al ${TEST_ID}
exit 1
fi
log "Check file amount"
fileamount=$(ls -al ${TEST_ID} | grep -v total | wc -l)
if [ ! ${fileamount} -eq 5 ];
then
log "File amount incorrect, expect 5"
ls -al ${TEST_ID}
exit 1
fi
log "Test PASSED"
@@ -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"