Compare commits

...
39 Commits
Author SHA1 Message Date
Sanket SudakeandGitHub 9b5eb069d6 Update chart version to 1.14.1 (#2143)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-08-03 15:16:15 +05:30
Sanket SudakeandGitHub 672bdbba6f Fixed service name generation for function name < 17 chars (#2142)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-08-03 14:19:04 +05:30
Sanket SudakeandGitHub 8271dfae07 Update changelog for 1.14.0 (#2138)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-08-02 21:35:28 +05:30
Sanket SudakeandGitHub f4702fe066 Update chart version to 1.14.0 (#2127)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-08-02 12:18:11 +05:30
Sanket SudakeandGitHub fea8dbef1a Remove unused code from Fission (#2136)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-29 19:15:50 +05:30
Gaurav GahlotandGitHub 6a527d36a8 reading right flag key for rounds (#2135)
Signed-off-by: Gaurav Gahlot <gauravgahlot0107@gmail.com>
2021-07-29 19:13:06 +05:30
Sanket SudakeandGitHub 2292f472c9 Option to keep or remove prefix when router triggers prefix based function (#2133)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-29 18:50:26 +05:30
Sanket SudakeandGitHub 2aaaeee5a7 Update runc to v1.0.1 (#2134)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-29 15:57:06 +05:30
Sanket SudakeandGitHub ca161690da Disable analytics in upgrade tests and use latest node env in integration tests (#2128)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-27 11:24:01 +05:30
Sanket SudakeandGitHub 8bd1a71065 Change Ingress version from v1beta1 to v1 for K8s 1.22+ compatibility (#2124)
* Change Ingress version from v1beta1 to v1 for K8s 1.22+ compatibility

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Minor fix

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-26 09:50:42 +05:30
Sanket SudakeandGitHub eb72fdc717 Enable race detector for unit tests (#2085)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-23 13:24:10 +05:30
Sanket SudakeandGitHub a7f819a78e Optimize swagger doc generator for Fission CR types (#2123)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-22 19:05:37 +05:30
Sanket SudakeandGitHub d4d58e166b Avoid dumping request headers in router (#2122)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-22 16:19:39 +05:30
Sanket SudakeandGitHub 1d41a198cb Add CRDs link for easy references (#2121)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-22 12:41:29 +05:30
Sanket SudakeandGitHub 6ced9d03d3 Support to dump request/response in router when debug mode enabled (#2119)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-21 20:45:08 +05:30
Sanket SudakeandGitHub 19c7616a4b Newdeploy/container function service names should fit in 63 characters (#2117)
* Add error message on deployment provision failure

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Add 63 characters limit for objects created via newdeploy/container

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-19 15:43:10 +05:30
a82281ad1c containers as functions (#1681)
PR adds a new executortype which supports running containers as functions. New CLI under functions is added to create container as functions.

Co-authored-by: Harsh Thakur <harshthakur9030@gmail.com>
Co-authored-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-16 18:08:41 +05:30
Sanket SudakeandGitHub f3e1f9df90 Add go pprof support across fission components (#2112)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-16 13:34:03 +05:30
Sanket SudakeandGitHub c85b9e8cbc Update keda connectors version to latest (#2115)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-16 09:40:49 +05:30
Sanket SudakeandGitHub ece0475808 Labels and annotation support for functions and environments (#2113)
1. Support for adding labels and annotations to functions & environment via fission CLI through create & update command.
2. Change ensures labels and annotations assigned to the environment would reflect on pods created via executor type pool manager and new deploy.

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-15 17:22:01 +05:30
Sanket SudakeandGitHub 2e0bb9304a Covert maps to sync maps in executor fscache to avoid dirty reads (#2105)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-08 10:40:17 +05:30
Sanket SudakeandGitHub 74c0142968 Replace controller with generated SharedIndexerInformers (#2103)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-06 13:56:10 +05:30
Sanket SudakeandGitHub 7c71d90d17 Fix race condition in fetcher readyness check (#2101)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-06 10:56:06 +05:30
Sanket SudakeandGitHub f86fde81e6 Shared informers (#2092)
* Use shared informers across executor for fission CRs

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Add informer for configmap and secrets

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Use shared informers in router

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Add shared informer for canary config

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Add sharedinformer for ready pod check

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Refer informer instead of store in resolver

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Run informers before executors

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Add missing canary config handler calls

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Enable websocket test

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Run dump collection always

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-05 20:28:26 +05:30
Sanket SudakeandGitHub 43814450e5 Increase timeout for kubectl test (#2100)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-05 14:57:11 +05:30
Sanket SudakeandGitHub d5077ee816 Updated Go dependencies and added cache in workflows (#2099)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-07-04 12:38:27 +05:30
Sanket SudakeandGitHub 2cf2c6979e Use latest release for upgrade tests (#2097)
Fix upgrade CI flow and added checks at places required
2021-07-02 15:48:02 +05:30
Sanket SudakeandGitHub 154fe0d447 Retrieve pod metrics only if metrics server is running and Go lint fixes (#2094)
* Retrieve pod metrics only if metrics server is running

Currently we query pod metrics every 30 sec which floods executor logs,
added check which confirms if metrics server is running then only we start
querying pod metrics for identifying CPU utilization.

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Fixed couple of typos and misspells with Go CI

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Remove unnecessary conversions with Go CI

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-28 10:06:32 +05:30
Sanket SudakeandGitHub a493f0117d Move informers,clientset and listers to generated package (#2093)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-26 11:13:46 +05:30
Sanket SudakeandGitHub cc0400bdd4 Move clientset,listers,informers to pkg/client from pkg/apis (#2090)
* Move clientset,listers,informers to pkg/client from pkg/apis

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Update golangci-lint version and timeout

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-24 12:07:14 +05:30
Sanket SudakeandGitHub 8b57a035ee Add a separate lint and unit test flow (#2089)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-22 15:00:58 +05:30
dc57f83d19 Add multi k8s version and os support in ci tests (#2082)
* add multi env and multi k8s support on integraation tests

* test workflow

* test workflow

* test workflow

* remove test branch name

* correct kind action with image and version

* use same kind version as per kind images

* use OS as ubuntu-latest

* fetch upstream

* add .github/workflows to paths

* resolve conflicts

* make kindimage appear first on workflow

Co-authored-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-22 13:25:55 +05:30
Sanket SudakeandGitHub 611f556cf9 Update changelog (#2088)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-21 19:22:29 +05:30
Sanket SudakeandGitHub d06ad04341 Changes for 1.13.1 release (#2087)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-21 15:22:59 +05:30
Sanket SudakeandGitHub 72c89268ec Ignore errors from zap logger Sync (#2086)
Added golang CI lint
Reference issue: https://github.com/uber-go/zap/issues/880

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-21 11:58:48 +05:30
Sanket SudakeandGitHub 3f99b483cb Correct release name in release script for non helm install (#2084)
* Correct release name in release script for non helm install

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Increase test timeout to 20 minutes

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Enable codeql for master only

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-20 21:38:48 +05:30
Sanket SudakeandGitHub 9bef235252 Use codeql for code scanning (#2078)
Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-18 14:59:07 +05:30
18935dcfdd Lint fix (#2074)
Signed-off-by: Harsh Thakur <harshthakur9030@gmail.com>

Co-authored-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-18 13:41:23 +05:30
Sanket SudakeandGitHub db2fb81654 Update changelog for 1.13.0 release (#2073)
* Update changelog for 1.13.0 release

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Just add 1.13 notes

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* Correct PRs for 1.13

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>

* minor change

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
2021-06-16 18:08:02 +05:30
190 changed files with 8657 additions and 1945 deletions
+43
View File
@@ -0,0 +1,43 @@
name: Code Scanning
on:
push:
branches:
- master
paths:
- '**.go'
- go.mod
- go.sum
pull_request:
branches:
- master
paths:
- '**.go'
- go.mod
- go.sum
schedule:
- cron: "0 0 * * 0"
workflow_dispatch:
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: go
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+71
View File
@@ -0,0 +1,71 @@
name: Lint and Unit tests
on:
push:
branches:
- master
paths:
- '**.go'
- go.mod
- go.sum
pull_request:
branches:
- master
paths:
- '**.go'
- go.mod
- go.sum
workflow_dispatch:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.15
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: Check out code
uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Verify dependencies
run: |
go mod verify
go mod download
LINT_VERSION=1.41.1
curl -fsSL https://github.com/golangci/golangci-lint/releases/download/v${LINT_VERSION}/golangci-lint-${LINT_VERSION}-linux-amd64.tar.gz | \
tar xz --strip-components 1 --wildcards \*/golangci-lint
mkdir -p bin && mv golangci-lint bin/
- name: Run checks
run: |
STATUS=0
assert-nothing-changed() {
local diff
"$@" >/dev/null || return 1
if ! diff="$(git diff -U1 --color --exit-code)"; then
printf '\e[31mError: running `\e[1m%s\e[22m` results in modifications that you must check into version control:\e[0m\n%s\n\n' "$*" "$diff" >&2
git checkout -- .
STATUS=1
fi
}
assert-nothing-changed go fmt ./...
assert-nothing-changed go mod tidy
bin/golangci-lint run --out-format=github-actions --timeout=5m || STATUS=$?
exit $STATUS
- name: Run unit tests
run: ./hack/runtests.sh
+40 -17
View File
@@ -4,16 +4,38 @@ on:
push: push:
branches: branches:
- master - master
paths:
- '**.go'
- 'charts/**'
- 'test/**'
- go.mod
- go.sum
pull_request: pull_request:
branches: branches:
- master - master
paths:
- '**.go'
- 'charts/**'
- 'test/**'
- go.mod
- go.sum
workflow_dispatch: workflow_dispatch:
jobs: jobs:
# Job to run change detection # Job to run change detection
integration-test: integration-test:
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
kindimage : [ 'kindest/node:v1.19.11','kindest/node:v1.20.7', 'kindest/node:v1.21.2' ]
os: [ ubuntu-latest ]
steps: steps:
- name: setup go
uses: actions/setup-go@v2.1.3
with:
go-version: "1.15.12"
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2.3.4 uses: actions/checkout@v2.3.4
@@ -23,10 +45,12 @@ jobs:
repository: fission/examples repository: fission/examples
path: examples path: examples
- name: setup go - uses: actions/cache@v2
uses: actions/setup-go@v2.1.3
with: with:
go-version: "1.15.12" path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Helm installation - name: Helm installation
uses: Azure/setup-helm@v1 uses: Azure/setup-helm@v1
@@ -36,24 +60,20 @@ jobs:
- name: Kind Clutser - name: Kind Clutser
uses: engineerd/setup-kind@v0.5.0 uses: engineerd/setup-kind@v0.5.0
with: with:
image: ${{ matrix.kindimage }}
version: v0.11.1
config: kind.yaml config: kind.yaml
- name: Configuring and testing the Installation - name: Configuring and testing the Installation
run: | run: |
kubectl cluster-info --context kind-kind kubectl cluster-info --context kind-kind
kind get kubeconfig --internal >$HOME/.kube/config
kubectl get nodes kubectl get nodes
sudo apt-get install -y apache2-utils sudo apt-get install -y apache2-utils
- name: Static code analysis - name: Helm chart lint
run: | run: |
./hack/verify-gofmt.sh
./hack/verify-govet.sh
helm lint charts/fission-all/ charts/fission-core/ helm lint charts/fission-all/ charts/fission-core/
- name: Run unit tests
run: ./hack/runtests.sh
- name: Helm update - name: Helm update
run: helm repo add prometheus-community https://prometheus-community.github.io/helm-charts run: helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
@@ -71,9 +91,8 @@ jobs:
- name: Build and Install Fission CLI - name: Build and Install Fission CLI
run: | run: |
go build -o fission cmd/fission-cli/main.go sudo make install-fission-cli
sudo mv fission /usr/local/bin sudo chmod +x /usr/local/bin/fission
fission version
- name: Port-forward fission components - name: Port-forward fission components
run: | run: |
@@ -81,18 +100,22 @@ jobs:
kubectl port-forward svc/controller 8889:80 -nfission & kubectl port-forward svc/controller 8889:80 -nfission &
kubectl port-forward svc/nats-streaming 8890:4222 -nfission & kubectl port-forward svc/nats-streaming 8890:4222 -nfission &
- name: Get fission version
run: |
fission version
- name: Integration tests - name: Integration tests
run: ./test/kind_CI.sh run: ./test/kind_CI.sh
- name: Collect Fission Dump - name: Collect Fission Dump
if: ${{ failure() }} if: ${{ always() }}
run: | run: |
fission support dump command -v fission && fission support dump
- name: Archive fission dump - name: Archive fission dump
if: ${{ failure() }} if: ${{ failure() }}
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: fission-support-dump name: fission-dump
path: fission-dump/*.zip path: fission-dump/*.zip
retention-days: 5 retention-days: 5
+54 -10
View File
@@ -1,20 +1,50 @@
name: Upgrade functionality test name: Fission CI upgrade
on: [workflow_dispatch] on:
push:
branches:
- master
paths:
- '**.go'
- 'charts/**'
- 'test/**'
- go.mod
- go.sum
pull_request:
branches:
- master
paths:
- '**.go'
- 'charts/**'
- 'test/**'
- go.mod
- go.sum
workflow_dispatch:
jobs: jobs:
upgrade-test:
upgrade-functionality-test: runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest strategy:
fail-fast: false
matrix:
kindimage : [ 'kindest/node:v1.19.11' ]
os: [ ubuntu-latest ]
steps: steps:
- name: Checkout action sources
uses: actions/checkout@v2.3.4
- name: Setup go - name: Setup go
uses: actions/setup-go@v2.1.3 uses: actions/setup-go@v2.1.3
with: with:
go-version: '1.15.12' go-version: '1.15.12'
- name: Checkout action sources
uses: actions/checkout@v2.3.4
- uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Setup Helm - name: Setup Helm
uses: Azure/setup-helm@v1 uses: Azure/setup-helm@v1
with: with:
@@ -23,13 +53,14 @@ jobs:
- name: Setup Kind Clutser - name: Setup Kind Clutser
uses: engineerd/setup-kind@v0.5.0 uses: engineerd/setup-kind@v0.5.0
with: with:
config: kind.yaml image: ${{ matrix.kindimage }}
version: v0.11.1
- name: Setup kubectl & fetch node information - name: Setup kubectl & fetch node information
run: | run: |
kubectl cluster-info --context kind-kind kubectl cluster-info --context kind-kind
kind get kubeconfig --internal >$HOME/.kube/config
kubectl get nodes kubectl get nodes
kubectl get storageclasses.storage.k8s.io
- name: Dump system info - name: Dump system info
run: | run: |
@@ -51,3 +82,16 @@ jobs:
- name: Test previously created fission objects with new release - name: Test previously created fission objects with new release
run: | run: |
source ./test/upgrade_test/fission_objects.sh test_fission_objects source ./test/upgrade_test/fission_objects.sh test_fission_objects
- name: Collect Fission Dump
if: ${{ always() }}
run: |
command -v fission && fission support dump
- name: Archive fission dump
if: ${{ failure() }}
uses: actions/upload-artifact@v2
with:
name: fission-dump
path: fission-dump/*.zip
retention-days: 5
-4
View File
@@ -29,9 +29,5 @@ environments/php7/vendor/
.DS_Store .DS_Store
vendor/ vendor/
local/ local/
environments/dotnet20/Builder/.vs/
environments/dotnet20/.vs/
environments/dotnet20/obj/
environments/dotnet20/bin/
build/ build/
+38
View File
@@ -0,0 +1,38 @@
linters:
enable:
# Default linter
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
# Additional linters
- gofmt
- goimports
- misspell
- nakedret
- unconvert
# Enable in future
# - bodyclose
# - dogsled
# - dupl
# - gosec
# - ifshort
# - nilerr
# - prealloc
# - revive
# - unparam
# - wrapcheck
# - gocritic
linters-settings:
errcheck:
ignore: go.uber.org/zap:Sync
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: github.com/fission/fission
+120 -19
View File
@@ -1,24 +1,125 @@
# 1.12.0
[Documentation](https://docs.fission.io/)
## Downloads for 1.12.0
filename | sha256 hash
-------- | -----------
[fission-all-1.12.0.tgz](https://github.com/fission/fission/releases/download/1.12.0/fission-all-1.12.0.tgz) | `10dfa6df66cfcc7f4887086357857ff25cb7e7e2f906b9c11e2d70e1a7bc8113`
[fission-core-1.12.0.tgz](https://github.com/fission/fission/releases/download/1.12.0/fission-core-1.12.0.tgz) | `89d745e2130cf7ff281b35100482d6560a2880f0a4e34ffda6428070eccd1945`
[fission-cli-linux](https://github.com/fission/fission/releases/download/1.12.0/fission-cli-linux) | `bcb3d84a424af45fb8ba5e846238475e481c01907eb10d38a3a614b288da785c`
[fission-cli-windows.exe](https://github.com/fission/fission/releases/download/1.12.0/fission-cli-windows.exe) | `005f5404545d49546259ee801b52987cb375816ba1c341f8e8fc6f316b16c0b2`
[fission-cli-osx](https://github.com/fission/fission/releases/download/1.12.0/fission-cli-osx) | `d6c192b96cca741e264c6bacb900ebe40419c4a68e2cec93a889c5c2874ecd5b`
[fission-core-1.12.0.yaml](https://github.com/fission/fission/releases/download/1.12.0/fission-core-1.12.0.yaml) | `2908fc54ed514b0c600664c5b7cacc35dd9fdb242269c1be3e589d53fbc4f8e5`
[fission-all-1.12.0.yaml](https://github.com/fission/fission/releases/download/1.12.0/fission-all-1.12.0.yaml) | `2a95051c91a507944805eed26103d1ccfa1e222951645a314814cac912e41391`
[fission-all-1.12.0-minikube.yaml](https://github.com/fission/fission/releases/download/1.12.0/fission-all-1.12.0-minikube.yaml) | `91bf41f63ac449861ddf3fc5a0b0ecd061a20f7e17331b8d913f17ac76e65649`
[fission-core-1.12.0-minikube.yaml](https://github.com/fission/fission/releases/download/1.12.0/fission-core-1.12.0-minikube.yaml) | `b71a5f0314e32d0fe3a98c5cbe22308c265203daeea342855a847c1185ce368a`
[fission-all-1.12.0-openshift.yaml](https://github.com/fission/fission/releases/download/1.12.0/fission-all-1.12.0-openshift.yaml) | `906fb10ea9d052e455c9f50613ee6b54ab8bd93d7d7350740ceaf8fdf8618ac4`
[fission-core-1.12.0-openshift.yaml](https://github.com/fission/fission/releases/download/1.12.0/fission-core-1.12.0-openshift.yaml) | `b46908adf6dda8601ac975b6fa0b32e7564b08d54347e106ce04b5e2d01e47b1`
# Changelog # Changelog
## [v1.14.0](https://github.com/fission/fission/tree/v1.14.0) (2021-08-02)
[Full Changelog](https://github.com/fission/fission/compare/v1.13.1...v1.14.0)
## [1.14.0](https://github.com/fission/fission/tree/1.14.0) (2021-08-02)
[Full Changelog](https://github.com/fission/fission/compare/1.13.1...1.14.0)
**Merged pull requests:**
- Remove unused code from Fission [\#2136](https://github.com/fission/fission/pull/2136) ([sanketsudake](https://github.com/sanketsudake))
- Reading right flag key for rounds [\#2135](https://github.com/fission/fission/pull/2135) ([gauravgahlot](https://github.com/gauravgahlot))
- Update runc to v1.0.1 [\#2134](https://github.com/fission/fission/pull/2134) ([sanketsudake](https://github.com/sanketsudake))
- Option to keep or remove prefix when router triggers prefix based function [\#2133](https://github.com/fission/fission/pull/2133) ([sanketsudake](https://github.com/sanketsudake))
- Disable analytics in upgrade tests and use latest node env in integration tests [\#2128](https://github.com/fission/fission/pull/2128) ([sanketsudake](https://github.com/sanketsudake))
- Update chart version to 1.14.0 [\#2127](https://github.com/fission/fission/pull/2127) ([sanketsudake](https://github.com/sanketsudake))
- Change Ingress version from v1beta1 to v1 for K8s 1.22+ compatibility [\#2124](https://github.com/fission/fission/pull/2124) ([sanketsudake](https://github.com/sanketsudake))
- Optimize swagger doc generator for Fission CR types [\#2123](https://github.com/fission/fission/pull/2123) ([sanketsudake](https://github.com/sanketsudake))
- Avoid dumping request headers in router [\#2122](https://github.com/fission/fission/pull/2122) ([sanketsudake](https://github.com/sanketsudake))
- Add CRDs link for easy references [\#2121](https://github.com/fission/fission/pull/2121) ([sanketsudake](https://github.com/sanketsudake))
- Support to dump request/response in router when debug mode enabled [\#2119](https://github.com/fission/fission/pull/2119) ([sanketsudake](https://github.com/sanketsudake))
- Newdeploy/container function service names should fit in 63 characters [\#2117](https://github.com/fission/fission/pull/2117) ([sanketsudake](https://github.com/sanketsudake))
- Update keda connectors version to latest [\#2115](https://github.com/fission/fission/pull/2115) ([sanketsudake](https://github.com/sanketsudake))
- Labels and annotation support for functions and environments [\#2113](https://github.com/fission/fission/pull/2113) ([sanketsudake](https://github.com/sanketsudake))
- Add go pprof support across fission components [\#2112](https://github.com/fission/fission/pull/2112) ([sanketsudake](https://github.com/sanketsudake))
- Covert maps to sync maps in executor fscache to avoid dirty reads [\#2105](https://github.com/fission/fission/pull/2105) ([sanketsudake](https://github.com/sanketsudake))
- Replace controller with generated SharedIndexerInformers [\#2103](https://github.com/fission/fission/pull/2103) ([sanketsudake](https://github.com/sanketsudake))
- Fix race condition in fetcher readiness check [\#2101](https://github.com/fission/fission/pull/2101) ([sanketsudake](https://github.com/sanketsudake))
- Increase timeout for kubectl test [\#2100](https://github.com/fission/fission/pull/2100) ([sanketsudake](https://github.com/sanketsudake))
- Updated Go dependencies and added cache in workflows [\#2099](https://github.com/fission/fission/pull/2099) ([sanketsudake](https://github.com/sanketsudake))
- Use latest release for upgrade tests [\#2097](https://github.com/fission/fission/pull/2097) ([sanketsudake](https://github.com/sanketsudake))
- Retrieve pod metrics only if metrics server is running and Go lint fixes [\#2094](https://github.com/fission/fission/pull/2094) ([sanketsudake](https://github.com/sanketsudake))
- Move informers,clientset and listers to generated package [\#2093](https://github.com/fission/fission/pull/2093) ([sanketsudake](https://github.com/sanketsudake))
- Shared informers [\#2092](https://github.com/fission/fission/pull/2092) ([sanketsudake](https://github.com/sanketsudake))
- Move clientset,listers,informers to pkg/client from pkg/apis [\#2090](https://github.com/fission/fission/pull/2090) ([sanketsudake](https://github.com/sanketsudake))
- Add a separate lint and unit test flow [\#2089](https://github.com/fission/fission/pull/2089) ([sanketsudake](https://github.com/sanketsudake))
- Update changelog [\#2088](https://github.com/fission/fission/pull/2088) ([sanketsudake](https://github.com/sanketsudake))
- Enable race detector for unit tests [\#2085](https://github.com/fission/fission/pull/2085) ([sanketsudake](https://github.com/sanketsudake))
- Add multi k8s version and os support in ci tests [\#2082](https://github.com/fission/fission/pull/2082) ([suramya-shah](https://github.com/suramya-shah))
- containers as functions [\#1681](https://github.com/fission/fission/pull/1681) ([sahil-lakhwani](https://github.com/sahil-lakhwani))
- Lint fix [\#2074](https://github.com/fission/fission/pull/2074) ([RealHarshThakur](https://github.com/RealHarshThakur))
## [v1.13.1](https://github.com/fission/fission/tree/v1.13.1) (2021-06-21)
[Full Changelog](https://github.com/fission/fission/compare/1.13.1...v1.13.1)
## [1.13.1](https://github.com/fission/fission/tree/1.13.1) (2021-06-21)
[Full Changelog](https://github.com/fission/fission/compare/v1.13.0...1.13.1)
**Merged pull requests:**
- Changes for 1.13.1 release [\#2087](https://github.com/fission/fission/pull/2087) ([sanketsudake](https://github.com/sanketsudake))
- Ignore errors from zap logger Sync [\#2086](https://github.com/fission/fission/pull/2086) ([sanketsudake](https://github.com/sanketsudake))
- Correct release name in release script for non helm install [\#2084](https://github.com/fission/fission/pull/2084) ([sanketsudake](https://github.com/sanketsudake))
- Use codeql for code scanning [\#2078](https://github.com/fission/fission/pull/2078) ([sanketsudake](https://github.com/sanketsudake))
- Lint fix [\#2074](https://github.com/fission/fission/pull/2074) ([RealHarshThakur](https://github.com/RealHarshThakur))
## [v1.13.0](https://github.com/fission/fission/tree/v1.13.0) (2021-06-16)
[Full Changelog](https://github.com/fission/fission/compare/v1.12.0...v1.13.0)
## [1.13.0](https://github.com/fission/fission/tree/1.13.0) (2021-06-16)
[Full Changelog](https://github.com/fission/fission/compare/v1.12.0...1.13.0)
**Merged pull requests:**
- Generate and archive fission cli per OS and platform [\#2071](https://github.com/fission/fission/pull/2071) ([sanketsudake](https://github.com/sanketsudake))
- Changes for releasing 1.13 [\#2070](https://github.com/fission/fission/pull/2070) ([sanketsudake](https://github.com/sanketsudake))
- Fix golang lint errors [\#2068](https://github.com/fission/fission/pull/2068) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Correct docker platforms and optimize make commands [\#2066](https://github.com/fission/fission/pull/2066) ([sanketsudake](https://github.com/sanketsudake))
- Added GCP PubSub Image, enabled Keda by default and updated versions of all Keda connector images [\#2065](https://github.com/fission/fission/pull/2065) ([vishal-biyani](https://github.com/vishal-biyani))
- Support for multiple HTTP verbs in routes/HTTPTrigger [\#2064](https://github.com/fission/fission/pull/2064) ([sanketsudake](https://github.com/sanketsudake))
- Add a SharedIndexInformer for services and deployments to NewDeploy executor [\#2061](https://github.com/fission/fission/pull/2061) ([sanketsudake](https://github.com/sanketsudake))
- Move RBAC v1beta1 to v1 [\#2059](https://github.com/fission/fission/pull/2059) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Collect fission dump if CI tests fails & enable manual trigger [\#2058](https://github.com/fission/fission/pull/2058) ([sanketsudake](https://github.com/sanketsudake))
- Capture timestamp whenever cleanup is triggered in tests [\#2054](https://github.com/fission/fission/pull/2054) ([sanketsudake](https://github.com/sanketsudake))
- Add websocket test [\#2053](https://github.com/fission/fission/pull/2053) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Wait for small time to avoid timeout in specs tests [\#2052](https://github.com/fission/fission/pull/2052) ([sanketsudake](https://github.com/sanketsudake))
- Added fission upgrade workflow [\#2050](https://github.com/fission/fission/pull/2050) ([jithindevasia](https://github.com/jithindevasia))
- Add make targets for CRDs [\#2049](https://github.com/fission/fission/pull/2049) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Add role required by fetcher pod to fetch events & pods in helm chart [\#2048](https://github.com/fission/fission/pull/2048) ([sanketsudake](https://github.com/sanketsudake))
- Enable prefix based routing [\#2047](https://github.com/fission/fission/pull/2047) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Change container name for runtime container to environment name in newdeploy [\#2044](https://github.com/fission/fission/pull/2044) ([sanketsudake](https://github.com/sanketsudake))
- Change examples references to examples repostiory [\#2038](https://github.com/fission/fission/pull/2038) ([sanketsudake](https://github.com/sanketsudake))
- OnlyOnce preupgrade check [\#2037](https://github.com/fission/fission/pull/2037) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Add router cache back for performance comparison [\#2036](https://github.com/fission/fission/pull/2036) ([sanketsudake](https://github.com/sanketsudake))
- Cleanup and remove hardcoding from delete-crds script [\#2035](https://github.com/fission/fission/pull/2035) ([sanketsudake](https://github.com/sanketsudake))
- Crds update [\#2033](https://github.com/fission/fission/pull/2033) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Use latest 1.14 go version in CI [\#2032](https://github.com/fission/fission/pull/2032) ([sanketsudake](https://github.com/sanketsudake))
- Handle SIGTERM signal in mqtrigger subscriber [\#2029](https://github.com/fission/fission/pull/2029) ([sanketsudake](https://github.com/sanketsudake))
- Update deprecated functions/methods calls [\#2027](https://github.com/fission/fission/pull/2027) ([gauravgahlot](https://github.com/gauravgahlot))
- Keda podspec [\#2021](https://github.com/fission/fission/pull/2021) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Add metrics to Reaper [\#2019](https://github.com/fission/fission/pull/2019) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Change alpine base image from 3.10 to 3.13 [\#2017](https://github.com/fission/fission/pull/2017) ([sanketsudake](https://github.com/sanketsudake))
- \[Snyk\] Security upgrade golang from 1.12.13 to 1.16.3 [\#2016](https://github.com/fission/fission/pull/2016) ([snyk-bot](https://github.com/snyk-bot))
- Add Google Analytics reporting in place of existing custom reporting [\#2015](https://github.com/fission/fission/pull/2015) ([sanketsudake](https://github.com/sanketsudake))
- Add preupgrade check for RPP [\#2014](https://github.com/fission/fission/pull/2014) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Restoring one example as it is used in many places including Helm charts [\#2011](https://github.com/fission/fission/pull/2011) ([vishal-biyani](https://github.com/vishal-biyani))
- Websocket reaper [\#1982](https://github.com/fission/fission/pull/1982) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Increase default concurrency [\#1980](https://github.com/fission/fission/pull/1980) ([therahulbhati](https://github.com/therahulbhati))
- Serve only one request and garbage collect [\#1978](https://github.com/fission/fission/pull/1978) ([therahulbhati](https://github.com/therahulbhati))
- Fix wrong checksum was assigned [\#1968](https://github.com/fission/fission/pull/1968) ([life1347](https://github.com/life1347))
- Add a warning if spec is applied on a dirty worktree [\#1966](https://github.com/fission/fission/pull/1966) ([hellozee](https://github.com/hellozee))
- Fix log sort order when multiple series are returned [\#1956](https://github.com/fission/fission/pull/1956) ([LittleFox94](https://github.com/LittleFox94))
- Add a SharedIndexInformer for pods to router. [\#1951](https://github.com/fission/fission/pull/1951) ([irgeek](https://github.com/irgeek))
- Feature request per pod [\#1946](https://github.com/fission/fission/pull/1946) ([therahulbhati](https://github.com/therahulbhati))
- feat: Add image pull policy when create KEDA connector deployments [\#1944](https://github.com/fission/fission/pull/1944) ([chenjr0719](https://github.com/chenjr0719))
- Maintain CRD folder to keep track of Schemas [\#1938](https://github.com/fission/fission/pull/1938) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Feature/router core component pod config [\#1936](https://github.com/fission/fission/pull/1936) ([adamkgray](https://github.com/adamkgray))
- Add fields to pod spec [\#1932](https://github.com/fission/fission/pull/1932) ([RealHarshThakur](https://github.com/RealHarshThakur))
- Multiarch release [\#1929](https://github.com/fission/fission/pull/1929) ([RealHarshThakur](https://github.com/RealHarshThakur))
- updated helm charts to v2 and updated prometheus chart url, version [\#1923](https://github.com/fission/fission/pull/1923) ([tusharRepo](https://github.com/tusharRepo))
## [v1.12.0](https://github.com/fission/fission/tree/v1.12.0) (2021-02-11)
[Full Changelog](https://github.com/fission/fission/compare/1.12.0...v1.12.0)
## [1.12.0](https://github.com/fission/fission/tree/1.12.0) (2021-02-11) ## [1.12.0](https://github.com/fission/fission/tree/1.12.0) (2021-02-11)
[Full Changelog](https://github.com/fission/fission/compare/v1.12.0...1.12.0) [Full Changelog](https://github.com/fission/fission/compare/v1.12.0...1.12.0)
+33
View File
@@ -0,0 +1,33 @@
# Profiling Fission with Pprof
Fission uses [net/pprof](https://pkg.go.dev/net/http/pprof) for profiling the code across Fission components.
It would be helpful in identifying performance bottlenecks.
To enable profiling, just set `pprof.enabled` to `true` while installing Fission helm chart.
## Pprof data of component pod
Do port forwarding to port 6060 of the pod,
```sh
kubectl port-forward pod/executor-668dfd7c89-2b2ff 6060:6060
```
Run different commands to get or analyze pprof data,
```sh
go tool pprof http://localhost:6060/debug/pprof/flamegraph
go tool pprof http://localhost:6060/debug/pprof/profile\?seconds\=60
```
You can also analyze with binary to get correct references of source,
```sh
# Download binary from pod
kubectl cp fission/executor-668dfd7c89-2b2ff:/fission-bundle fission-bundle
go tool pprof -http ":49816" fission-bundle http://localhost:49513/debug/pprof
```
You can also download pprof data and visualize/analyze with different compatible tools.
+21 -6
View File
@@ -56,13 +56,22 @@ test-run: code-checks
@rm -f coverage.txt @rm -f coverage.txt
### Binaries ### Binaries
fission-cli: %-cli:
@mkdir -p $(BINDIR) @mkdir -p $(BINDIR)
GOOS=$(GOOS) GOARCH=$(GOARCH) $(GO) build \ GOOS=$(GOOS) GOARCH=$(GOARCH) $(GO) build \
-gcflags '$(GCFLAGS)' \ -gcflags '$(GCFLAGS)' \
-asmflags '$(ASMFLAGS)' \ -asmflags '$(ASMFLAGS)' \
-ldflags "$(GO_LDFLAGS)" \ -ldflags "$(GO_LDFLAGS)" \
-o $(BINDIR)/fission-$(VERSION)-$(GOOS)-$(GOARCH)$(FISSION-CLI-SUFFIX) ./cmd/fission-cli -o $(BINDIR)/$(subst -cli,,$@)-$(VERSION)-$(GOOS)-$(GOARCH)$(FISSION-CLI-SUFFIX) ./$<
fission-cli: cmd/fission-cli
fission-bundle-cli: cmd/fission-bundle
fetcher-cli: cmd/fetcher
builder-cli: cmd/builder
preupgradechecks-cli: cmd/preupgradechecks
reporter-cli: cmd/reporter
local-bins: fission-cli fission-bundle-cli fetcher-cli builder-cli preupgradechecks-cli reporter-cli
all-fission-cli: all-fission-cli:
$(MAKE) fission-cli GOOS=windows GOARCH=amd64 $(MAKE) fission-cli GOOS=windows GOARCH=amd64
@@ -72,7 +81,7 @@ all-fission-cli:
$(MAKE) fission-cli GOOS=darwin GOARCH=amd64 $(MAKE) fission-cli GOOS=darwin GOARCH=amd64
install-fission-cli: fission-cli install-fission-cli: fission-cli
mv $(BINDIR)/fission-$(VERSION)-$(GOOS)-$(GOARCH)$(FISSION-CLI-SUFFIX) /usr/local/bin/ mv $(BINDIR)/fission-$(VERSION)-$(GOOS)-$(GOARCH)$(FISSION-CLI-SUFFIX) /usr/local/bin/fission
### Container images ### Container images
FISSION_IMGS := fission-bundle-multiarch-img \ FISSION_IMGS := fission-bundle-multiarch-img \
@@ -103,6 +112,10 @@ reporter-multiarch-img: cmd/reporter/Dockerfile.reporter
--build-arg BUILDVERSION=$(VERSION) \ --build-arg BUILDVERSION=$(VERSION) \
$(DOCKER_FLAGS) -f $< . $(DOCKER_FLAGS) -f $< .
### Codegen
codegen:
@./hack/update-codegen.sh
### CRDs ### CRDs
generate-crds: generate-crds:
controller-gen crd:trivialVersions=false,preserveUnknownFields=false \ controller-gen crd:trivialVersions=false,preserveUnknownFields=false \
@@ -129,8 +142,10 @@ clean:
### Misc ### Misc
generate-swagger-doc: generate-swagger-doc:
@cd pkg/apis/core/v1/tool && ./update-generated-swagger-docs.sh @./hack/update-swagger-docs.sh
make release: all-generators: codegen generate-crds generate-swagger-doc
release:
@./hack/release.sh $(VERSION) @./hack/release.sh $(VERSION)
@./hack/releas-tag.sh $(VERSION) @./hack/release-tag.sh $(VERSION)
+2 -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` `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` `repository` | Image base repository | `index.docker.io`
`image` | Fission image repository | `fission/fission-bundle` `image` | Fission image repository | `fission/fission-bundle`
`imageTag` | Fission image tag | `1.13.0` `imageTag` | Fission image tag | `1.13.1`
`pullPolicy` | Image pull policy | `IfNotPresent` `pullPolicy` | Image pull policy | `IfNotPresent`
`fetcher.image` | Fission fetcher repository | `fission/fetcher` `fetcher.image` | Fission fetcher repository | `fission/fetcher`
`fetcher.imageTag` | Fission fetcher image tag | `1.13.0` `fetcher.imageTag` | Fission fetcher image tag | `1.13.1`
`controllerPort` | Fission Controller service port | `31313` `controllerPort` | Fission Controller service port | `31313`
`routerPort` | Fission Router service port | ` 31314` `routerPort` | Fission Router service port | ` 31314`
`functionNamespace` | Namespace in which to run fission functions (this is different from the release namespace) | `fission-function` `functionNamespace` | Namespace in which to run fission functions (this is different from the release namespace) | `fission-function`
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v2 apiVersion: v2
name: fission-all name: fission-all
version: 1.13.0 version: 1.14.1
description: Fission is a fast serverless framework for Kubernetes. description: Fission is a fast serverless framework for Kubernetes.
keywords: keywords:
- fission - fission
@@ -12,7 +12,7 @@ maintainers:
- name: Sanket Sudake - name: Sanket Sudake
email: sanket@infracloud.io email: sanket@infracloud.io
engine: gotpl engine: gotpl
appVersion: 1.13.0 appVersion: 1.14.1
type: application type: application
dependencies: dependencies:
- name: prometheus - name: prometheus
@@ -340,6 +340,8 @@ spec:
value: {{ .Values.traceSamplingRate | default "0.5" | quote }} value: {{ .Values.traceSamplingRate | default "0.5" | quote }}
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
- name: POD_NAMESPACE - name: POD_NAMESPACE
valueFrom: valueFrom:
fieldRef: fieldRef:
@@ -364,6 +366,10 @@ spec:
ports: ports:
- containerPort: 8888 - containerPort: 8888
name: http name: http
{{- if .Values.pprof.enabled }}
- containerPort: 6060
name: pprof
{{- end }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
volumes: volumes:
- name: config-volume - name: config-volume
@@ -432,6 +438,8 @@ spec:
value: {{ .Values.fetcher.resource.mem.limits | quote }} value: {{ .Values.fetcher.resource.mem.limits | quote }}
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
readinessProbe: readinessProbe:
httpGet: httpGet:
path: "/healthz" path: "/healthz"
@@ -450,6 +458,10 @@ spec:
name: metrics name: metrics
- containerPort: 8888 - containerPort: 8888
name: http name: http
{{- if .Values.pprof.enabled }}
- containerPort: 6060
name: pprof
{{- end }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
{{- if .Values.extraCoreComponentPodConfig }} {{- if .Values.extraCoreComponentPodConfig }}
{{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}} {{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}}
@@ -506,6 +518,8 @@ spec:
value: {{ .Values.fetcher.resource.mem.limits | quote }} value: {{ .Values.fetcher.resource.mem.limits | quote }}
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
{{- if .Values.extraCoreComponentPodConfig }} {{- if .Values.extraCoreComponentPodConfig }}
{{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}} {{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}}
@@ -542,6 +556,8 @@ spec:
value: {{ .Values.traceSamplingRate | default "0.5" | quote }} value: {{ .Values.traceSamplingRate | default "0.5" | quote }}
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
{{- if .Values.extraCoreComponentPodConfig }} {{- if .Values.extraCoreComponentPodConfig }}
{{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}} {{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}}
@@ -676,6 +692,8 @@ spec:
env: env:
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
{{- if .Values.extraCoreComponentPodConfig }} {{- if .Values.extraCoreComponentPodConfig }}
{{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}} {{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}}
@@ -815,6 +833,8 @@ spec:
value: {{ .Values.traceSamplingRate | default "0.5" | quote }} value: {{ .Values.traceSamplingRate | default "0.5" | quote }}
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
{{- if .Values.extraCoreComponentPodConfig }} {{- if .Values.extraCoreComponentPodConfig }}
{{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}} {{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}}
@@ -866,6 +886,8 @@ spec:
value: {{ .Values.traceSamplingRate | default "0.5" | quote }} value: {{ .Values.traceSamplingRate | default "0.5" | quote }}
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
# TLS authentication is TLS with authentication (2 way) # TLS authentication is TLS with authentication (2 way)
# More info: https://docs.confluent.io/current/kafka/authentication_ssl.html#ssl-overview # More info: https://docs.confluent.io/current/kafka/authentication_ssl.html#ssl-overview
{{- if .Values.kafka.authentication.tls.enabled }} {{- if .Values.kafka.authentication.tls.enabled }}
@@ -965,6 +987,8 @@ spec:
key: key key: key
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
{{- if .Values.extraCoreComponentPodConfig }} {{- if .Values.extraCoreComponentPodConfig }}
{{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}} {{ toYaml .Values.extraCoreComponentPodConfig | indent 6 -}}
@@ -1010,6 +1034,8 @@ spec:
value: "{{.Values.pruneInterval}}" value: "{{.Values.pruneInterval}}"
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
{{- if and (.Values.persistence.enabled) (eq (.Values.persistence.storageType | default "local") "s3") }} {{- if and (.Values.persistence.enabled) (eq (.Values.persistence.storageType | default "local") "s3") }}
- name: STORAGE_S3_ENDPOINT - name: STORAGE_S3_ENDPOINT
value: {{ .Values.persistence.s3.endPoint }} value: {{ .Values.persistence.s3.endPoint }}
@@ -1045,6 +1071,10 @@ spec:
ports: ports:
- containerPort: 8000 - containerPort: 8000
name: http name: http
{{- if .Values.pprof.enabled }}
- containerPort: 6060
name: pprof
{{- end }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
{{- if and (.Values.persistence.enabled) (ne (.Values.persistence.storageType | default "local") "s3") }} {{- if and (.Values.persistence.enabled) (ne (.Values.persistence.storageType | default "local") "s3") }}
volumes: volumes:
+6
View File
@@ -63,6 +63,8 @@ spec:
value: {{ .Values.router.useEncodedPath | default false | quote }} value: {{ .Values.router.useEncodedPath | default false | quote }}
- name: DEBUG_ENV - name: DEBUG_ENV
value: {{ .Values.debugEnv | quote }} value: {{ .Values.debugEnv | quote }}
- name: PPROF_ENABLED
value: {{ .Values.pprof.enabled | quote }}
- name: DISPLAY_ACCESS_LOG - name: DISPLAY_ACCESS_LOG
value: {{ .Values.router.displayAccessLog | default false | quote }} value: {{ .Values.router.displayAccessLog | default false | quote }}
resources: resources:
@@ -85,6 +87,10 @@ spec:
name: metrics name: metrics
- containerPort: 8888 - containerPort: 8888
name: http name: http
{{- if .Values.pprof.enabled }}
- containerPort: 6060
name: pprof
{{- end }}
serviceAccountName: fission-svc serviceAccountName: fission-svc
{{- if .Values.router.extraCoreComponentPodConfig }} {{- if .Values.router.extraCoreComponentPodConfig }}
{{ toYaml .Values.router.extraCoreComponentPodConfig | indent 6 -}} {{ toYaml .Values.router.extraCoreComponentPodConfig | indent 6 -}}
+12 -8
View File
@@ -20,7 +20,7 @@ image: fission/fission-bundle
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
## Fission image version ## Fission image version
imageTag: 1.13.0 imageTag: 1.14.1
## Port at which Fission controller service should be exposed ## Port at which Fission controller service should be exposed
controllerPort: 31313 controllerPort: 31313
@@ -50,7 +50,7 @@ fetcher:
## Fetcher repository ## Fetcher repository
image: fission/fetcher image: fission/fetcher
## Fetcher image version ## Fetcher image version
imageTag: 1.13.0 imageTag: 1.14.1
## Fetcher is only for to downloading or uploading archive. ## Fetcher is only for to downloading or uploading archive.
## Normally, you don't need to change the value here, unless necessary. ## Normally, you don't need to change the value here, unless necessary.
@@ -350,19 +350,23 @@ mqt_keda:
connector_images: connector_images:
kafka: kafka:
image: fission/keda-kafka-http-connector image: fission/keda-kafka-http-connector
tag: v0.6 tag: v0.7
rabbitmq: rabbitmq:
image: fission/keda-rabbitmq-http-connector image: fission/keda-rabbitmq-http-connector
tag: v0.6 tag: v0.7
awskinesis: awskinesis:
image: fission/keda-aws-kinesis-http-connector image: fission/keda-aws-kinesis-http-connector
tag: v0.6 tag: v0.7
aws_sqs: aws_sqs:
image: fission/keda-aws-sqs-http-connector image: fission/keda-aws-sqs-http-connector
tag: v0.6 tag: v0.7
nats_steaming: nats_steaming:
image: fission/keda-nats-streaming-http-connector image: fission/keda-nats-streaming-http-connector
tag: v0.6 tag: v0.7
gcp_pub_sub: gcp_pub_sub:
image: fission/keda-gcp-pubsub-http-connector image: fission/keda-gcp-pubsub-http-connector
tag: v0.1 tag: v0.2
## Enable Pprof based profiling
pprof:
enabled: false
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v2 apiVersion: v2
name: fission-core name: fission-core
version: 1.13.0 version: 1.14.1
description: Fission is a fast serverless framework for Kubernetes. description: Fission is a fast serverless framework for Kubernetes.
keywords: keywords:
- fission - fission
@@ -12,7 +12,7 @@ maintainers:
- name: Sanket Sudake - name: Sanket Sudake
email: sanket@infracloud.io email: sanket@infracloud.io
engine: gotpl engine: gotpl
appVersion: 1.13.0 appVersion: 1.14.1
type: application type: application
dependencies: dependencies:
- name: prometheus - name: prometheus
+8 -8
View File
@@ -17,7 +17,7 @@ repository: index.docker.io
image: fission/fission-bundle image: fission/fission-bundle
## Fission image version ## Fission image version
imageTag: 1.13.0 imageTag: 1.14.1
## Image pull policy ## Image pull policy
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
@@ -43,7 +43,7 @@ fetcher:
## Fetcher repository ## Fetcher repository
image: fission/fetcher image: fission/fetcher
## Fetcher image version ## Fetcher image version
imageTag: 1.13.0 imageTag: 1.14.1
## Fetcher is only for to downloading or uploading archive. ## Fetcher is only for to downloading or uploading archive.
## Normally, you don't need to change the value here, unless necessary. ## Normally, you don't need to change the value here, unless necessary.
@@ -239,19 +239,19 @@ mqt_keda:
connector_images: connector_images:
kafka: kafka:
image: fission/keda-kafka-http-connector image: fission/keda-kafka-http-connector
tag: v0.6 tag: v0.7
rabbitmq: rabbitmq:
image: fission/keda-rabbitmq-http-connector image: fission/keda-rabbitmq-http-connector
tag: v0.6 tag: v0.7
awskinesis: awskinesis:
image: fission/keda-aws-kinesis-http-connector image: fission/keda-aws-kinesis-http-connector
tag: v0.6 tag: v0.7
aws_sqs: aws_sqs:
image: fission/keda-aws-sqs-http-connector image: fission/keda-aws-sqs-http-connector
tag: v0.6 tag: v0.7
nats_steaming: nats_steaming:
image: fission/keda-nats-streaming-http-connector image: fission/keda-nats-streaming-http-connector
tag: v0.6 tag: v0.7
gcp_pub_sub: gcp_pub_sub:
image: fission/keda-gcp-pubsub-http-connector image: fission/keda-gcp-pubsub-http-connector
tag: v0.1 tag: v0.2
+4 -6
View File
@@ -24,22 +24,20 @@ import (
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
"github.com/fission/fission/cmd/builder/app" "github.com/fission/fission/cmd/builder/app"
"github.com/fission/fission/pkg/utils/profile"
) )
// Usage: builder <shared volume path> // Usage: builder <shared volume path>
func main() { func main() {
profile.ProfileIfEnabled()
config := zap.NewProductionConfig() config := zap.NewProductionConfig()
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, err := config.Build() logger, err := config.Build()
if err != nil { if err != nil {
log.Fatalf("can't initialize zap logger: %v", err) log.Fatalf("can't initialize zap logger: %v", err)
} }
defer func() { defer logger.Sync()
err := logger.Sync()
if err != nil {
log.Fatal(err)
}
}()
shareVolume := os.Args[1] shareVolume := os.Args[1]
if _, err := os.Stat(shareVolume); err != nil { if _, err := os.Stat(shareVolume); err != nil {
+12 -12
View File
@@ -24,6 +24,7 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"sync/atomic"
"contrib.go.opencensus.io/exporter/jaeger" "contrib.go.opencensus.io/exporter/jaeger"
"go.opencensus.io/plugin/ochttp" "go.opencensus.io/plugin/ochttp"
@@ -33,6 +34,10 @@ import (
"github.com/fission/fission/pkg/fetcher" "github.com/fission/fission/pkg/fetcher"
) )
var (
readyToServe uint32
)
func registerTraceExporter(collectorEndpoint string) error { func registerTraceExporter(collectorEndpoint string) error {
if collectorEndpoint == "" { if collectorEndpoint == "" {
return nil return nil
@@ -80,17 +85,17 @@ func Run(logger *zap.Logger) {
} }
} }
if err := registerTraceExporter(*collectorEndpoint); err != nil { go func() {
logger.Fatal("could not register trace exporter", zap.Error(err), zap.String("collector_endpoint", *collectorEndpoint)) if err := registerTraceExporter(*collectorEndpoint); err != nil {
} logger.Fatal("could not register trace exporter", zap.Error(err), zap.String("collector_endpoint", *collectorEndpoint))
}
}()
f, err := fetcher.MakeFetcher(logger, dir, *secretDir, *configDir) f, err := fetcher.MakeFetcher(logger, dir, *secretDir, *configDir)
if err != nil { if err != nil {
logger.Fatal("error making fetcher", zap.Error(err)) logger.Fatal("error making fetcher", zap.Error(err))
} }
readyToServe := false
// do specialization in other goroutine to prevent blocking in newdeploy // do specialization in other goroutine to prevent blocking in newdeploy
go func() { go func() {
if *specializeOnStart { if *specializeOnStart {
@@ -106,9 +111,8 @@ func Run(logger *zap.Logger) {
if err != nil { if err != nil {
logger.Fatal("error specializing function pod", zap.Error(err)) logger.Fatal("error specializing function pod", zap.Error(err))
} }
readyToServe = true
} }
atomic.StoreUint32(&readyToServe, 1)
}() }()
mux := http.NewServeMux() mux := http.NewServeMux()
@@ -120,7 +124,7 @@ func Run(logger *zap.Logger) {
mux.HandleFunc("/wsevent/end", f.WsEndHandler) mux.HandleFunc("/wsevent/end", f.WsEndHandler)
readinessHandler := func(w http.ResponseWriter, r *http.Request) { readinessHandler := func(w http.ResponseWriter, r *http.Request) {
if !*specializeOnStart || readyToServe { if atomic.LoadUint32(&readyToServe) == 1 {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} else { } else {
w.WriteHeader(http.StatusServiceUnavailable) w.WriteHeader(http.StatusServiceUnavailable)
@@ -132,10 +136,6 @@ func Run(logger *zap.Logger) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
// For backward compatibility
// TODO: remove this path in future
mux.HandleFunc("/readniess-healthz", readinessHandler)
logger.Info("fetcher ready to receive requests") logger.Info("fetcher ready to receive requests")
err = http.ListenAndServe(":8000", &ochttp.Handler{ err = http.ListenAndServe(":8000", &ochttp.Handler{
Handler: mux, Handler: mux,
+4 -6
View File
@@ -23,21 +23,19 @@ import (
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
"github.com/fission/fission/cmd/fetcher/app" "github.com/fission/fission/cmd/fetcher/app"
"github.com/fission/fission/pkg/utils/profile"
) )
// Usage: fetcher <shared volume path> // Usage: fetcher <shared volume path>
func main() { func main() {
profile.ProfileIfEnabled()
config := zap.NewProductionConfig() config := zap.NewProductionConfig()
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, err := config.Build() logger, err := config.Build()
if err != nil { if err != nil {
log.Fatalf("can't initialize zap logger: %v", err) log.Fatalf("can't initialize zap logger: %v", err)
} }
defer func() { defer logger.Sync()
err := logger.Sync()
if err != nil {
log.Fatal(err)
}
}()
app.Run(logger) app.Run(logger)
} }
+3 -6
View File
@@ -40,6 +40,7 @@ import (
"github.com/fission/fission/pkg/router" "github.com/fission/fission/pkg/router"
"github.com/fission/fission/pkg/storagesvc" "github.com/fission/fission/pkg/storagesvc"
"github.com/fission/fission/pkg/timer" "github.com/fission/fission/pkg/timer"
"github.com/fission/fission/pkg/utils/profile"
) )
func runController(logger *zap.Logger, port int) { func runController(logger *zap.Logger, port int) {
@@ -237,6 +238,7 @@ Options:
--builderMgr Start builder manager. --builderMgr Start builder manager.
--version Print version information --version Print version information
` `
profile.ProfileIfEnabled()
var logger *zap.Logger var logger *zap.Logger
var config zap.Config var config zap.Config
@@ -255,12 +257,7 @@ Options:
if err != nil { if err != nil {
log.Fatalf("I can't initialize zap logger: %v", err) log.Fatalf("I can't initialize zap logger: %v", err)
} }
defer func() { defer logger.Sync()
err := logger.Sync()
if err != nil {
log.Fatal(err)
}
}()
version := fmt.Sprintf("Fission Bundle Version: %v", info.BuildInfo().String()) version := fmt.Sprintf("Fission Bundle Version: %v", info.BuildInfo().String())
arguments, err := docopt.ParseArgs(usage, nil, version) arguments, err := docopt.ParseArgs(usage, nil, version)
if err != nil { if err != nil {
+1 -6
View File
@@ -42,12 +42,7 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("can't initialize zap logger: %v", err) log.Fatalf("can't initialize zap logger: %v", err)
} }
defer func() { defer logger.Sync()
err := logger.Sync()
if err != nil {
log.Fatal(err)
}
}()
usage := `Package to perform operations needed prior to fission installation usage := `Package to perform operations needed prior to fission installation
Usage: Usage:
+2 -1
View File
@@ -99,7 +99,7 @@ func (client *PreUpgradeTaskClient) LatestSchemaApplied() error {
return errors.New("Could not get the Function CRD") return errors.New("Could not get the Function CRD")
} }
// Any new field added in Function spec can be checked here provided the substring matches the description in CRD Validation of the field // Any new field added in Function spec can be checked here provided the substring matches the description in CRD Validation of the field
if !strings.Contains(funcCRD.Spec.String(), "RequestsPerPod") || !strings.Contains(funcCRD.Spec.String(), "OnceOnly") { if !strings.Contains(funcCRD.Spec.String(), "RequestsPerPod") || !strings.Contains(funcCRD.Spec.String(), "OnceOnly") || !strings.Contains(funcCRD.Spec.String(), "PodSpec") {
return errors.New("Apply the newer CRDs before upgrading") return errors.New("Apply the newer CRDs before upgrading")
} }
@@ -112,6 +112,7 @@ func (client *PreUpgradeTaskClient) LatestSchemaApplied() error {
if !strings.Contains(mqtCRD.Spec.String(), "PodSpec") { if !strings.Contains(mqtCRD.Spec.String(), "PodSpec") {
return errors.New("Apply the newer CRDs before upgrading") return errors.New("Apply the newer CRDs before upgrading")
} }
return nil return nil
} }
+2 -1
View File
@@ -18,8 +18,9 @@ package app
import ( import (
"log" "log"
"github.com/fission/fission/pkg/tracker"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fission/fission/pkg/tracker"
) )
func eventCommandHandler(cmd *cobra.Command, args []string) error { func eventCommandHandler(cmd *cobra.Command, args []string) error {
+11
View File
@@ -0,0 +1,11 @@
# Custom Resource Definitions Reference
- [CanaryConfig](https://doc.crds.dev/github.com/fission/fission/fission.io/CanaryConfig/v1)
- [Environment](https://doc.crds.dev/github.com/fission/fission/fission.io/Environment/v1)
- [Function](https://doc.crds.dev/github.com/fission/fission/fission.io/Function/v1)
- [HTTPTrigger](https://doc.crds.dev/github.com/fission/fission/fission.io/HTTPTrigger/v1)
- [KubernetesWatchTrigger](https://doc.crds.dev/github.com/fission/fission/fission.io/KubernetesWatchTrigger/v1)
- [MessageQueueTrigger](https://doc.crds.dev/github.com/fission/fission/fission.io/MessageQueueTrigger/v1)
- [Package](https://doc.crds.dev/github.com/fission/fission/fission.io/Package/v1)
- [TimeTrigger](https://doc.crds.dev/github.com/fission/fission/fission.io/TimeTrigger/v1)
File diff suppressed because it is too large Load Diff
+3
View File
@@ -76,6 +76,9 @@ spec:
description: TLS is for user to specify a Secret that contains TLS key and certificate. The domain name in the key and crt must match the value of Host field. description: TLS is for user to specify a Secret that contains TLS key and certificate. The domain name in the key and crt must match the value of Host field.
type: string type: string
type: object type: object
keepPrefix:
description: When function is exposed with Prefix based path, keepPrefix decides whether to keep or trim prefix in URL while invoking function.
type: boolean
method: method:
description: Use Methods instead of Method. This field is going to be deprecated in a future release HTTP method to access a function. description: Use Methods instead of Method. This field is going to be deprecated in a future release HTTP method to access a function.
type: string type: string
+1 -1
View File
@@ -6,7 +6,7 @@ import (
) )
// Handler is the entry point for this fission function // Handler is the entry point for this fission function
func Handler(w http.ResponseWriter, r *http.Request) { //nolint: deadcode func Handler(w http.ResponseWriter, r *http.Request) { //nolint:golint,unused,deadcode
msg := "Hello, CNCF Webinar!\n" msg := "Hello, CNCF Webinar!\n"
_, err := w.Write([]byte(msg)) _, err := w.Write([]byte(msg))
if err != nil { if err != nil {
-7
View File
@@ -1,7 +0,0 @@
# Git hooks
* pre-push: Check, build and test the changes.
```bash
$ cp githooks/* .git/hooks/
```
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
set -e
ROOT=`realpath $(dirname $0)/../..`
pushd $ROOT
make
popd
+20 -21
View File
@@ -3,12 +3,12 @@ module github.com/fission/fission
go 1.15 go 1.15
require ( require (
contrib.go.opencensus.io/exporter/jaeger v0.1.0 contrib.go.opencensus.io/exporter/jaeger v0.2.1
github.com/Azure/azure-sdk-for-go v12.4.0-beta+incompatible github.com/Azure/azure-sdk-for-go v12.4.0-beta+incompatible
github.com/Azure/go-autorest/autorest v0.11.18 // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect
github.com/Microsoft/go-winio v0.4.16 // indirect github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/Shopify/sarama v1.23.1 github.com/Shopify/sarama v1.29.1
github.com/aws/aws-sdk-go v1.36.33 // indirect github.com/aws/aws-sdk-go v1.36.33 // indirect
github.com/blend/go-sdk v1.20210116.5 // indirect github.com/blend/go-sdk v1.20210116.5 // indirect
github.com/bsm/sarama-cluster v2.1.15+incompatible github.com/bsm/sarama-cluster v2.1.15+incompatible
@@ -23,48 +23,47 @@ require (
github.com/dustin/go-humanize v1.0.0 github.com/dustin/go-humanize v1.0.0
github.com/emicklei/go-restful v2.9.6+incompatible github.com/emicklei/go-restful v2.9.6+incompatible
github.com/emicklei/go-restful-openapi v1.2.0 github.com/emicklei/go-restful-openapi v1.2.0
github.com/fatih/color v1.12.0
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
github.com/go-git/go-git/v5 v5.2.0 github.com/go-git/go-git/v5 v5.2.0
github.com/go-ini/ini v1.62.0 // indirect github.com/go-ini/ini v1.62.0 // indirect
github.com/go-openapi/spec v0.19.3 github.com/go-openapi/spec v0.19.3
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/gorilla/mux v1.7.0 github.com/gorilla/mux v1.8.0
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
github.com/graymeta/stow v0.0.0-20180719215413-7b5498c561bb github.com/graymeta/stow v0.0.0-20180719215413-7b5498c561bb
github.com/hashicorp/go-multierror v1.0.0 github.com/hashicorp/go-multierror v1.1.1
github.com/imdario/mergo v0.3.9 github.com/imdario/mergo v0.3.12
github.com/influxdata/influxdb v1.2.0 github.com/influxdata/influxdb v1.2.0
github.com/life1347/color v1.7.0
github.com/marstr/guid v1.1.0 // indirect github.com/marstr/guid v1.1.0 // indirect
github.com/mholt/archiver v0.0.0-20180417220235-e4ef56d48eb0 github.com/mholt/archiver v0.0.0-20180417220235-e4ef56d48eb0
github.com/minio/minio-go v6.0.14+incompatible github.com/minio/minio-go v6.0.14+incompatible
github.com/nats-io/nats-streaming-server v0.17.0 github.com/nats-io/nats-streaming-server v0.22.0
github.com/nats-io/nats.go v1.9.1 github.com/nats-io/nats.go v1.11.0
github.com/nats-io/stan.go v0.6.0 github.com/nats-io/stan.go v0.9.0
github.com/nwaples/rardecode v1.1.0 // indirect github.com/nwaples/rardecode v1.1.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect github.com/opencontainers/runc v1.0.1 // indirect
github.com/ory/dockertest v3.3.5+incompatible github.com/ory/dockertest v3.3.5+incompatible
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.7.1 github.com/prometheus/client_golang v1.7.1
github.com/prometheus/common v0.10.0 github.com/prometheus/common v0.10.0
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.7.0
github.com/ulikunitz/xz v0.5.9 // indirect github.com/ulikunitz/xz v0.5.9 // indirect
github.com/wcharczuk/go-chart v2.0.1+incompatible github.com/wcharczuk/go-chart v2.0.1+incompatible
go.opencensus.io v0.22.4 go.opencensus.io v0.23.0
go.uber.org/zap v1.10.0 go.uber.org/zap v1.18.1
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb golang.org/x/net v0.0.0-20210614182718-04defd469f4e
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect k8s.io/api v0.19.12
k8s.io/api v0.19.2 k8s.io/apiextensions-apiserver v0.19.12
k8s.io/apiextensions-apiserver v0.19.2 k8s.io/apimachinery v0.19.12
k8s.io/apimachinery v0.19.2 k8s.io/client-go v0.19.12
k8s.io/client-go v0.19.2
k8s.io/klog v1.0.0 k8s.io/klog v1.0.0
k8s.io/metrics v0.19.2 k8s.io/metrics v0.19.12
) )
+305 -104
View File
File diff suppressed because it is too large Load Diff
-5
View File
@@ -1,5 +0,0 @@
FROM golang:1.16.3
ENV GO111MODULE on
RUN mkdir -p /go/src/github.com/fission
RUN wget -qO- https://download.docker.com/linux/static/stable/x86_64/docker-17.03.0-ce.tgz | tar xvz -C /usr/local/bin/ --strip 1
RUN wget -qO- https://get.helm.sh/helm-v3.3.0-linux-amd64.tar.gz | tar xvz -C /usr/local/bin/ --strip 1
-1
View File
@@ -1 +0,0 @@
kubectl get crds -o custom-columns=:metadata.name | grep 'fission.io' | xargs kubectl delete crds
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
API_GROUP_VERSIONS="
core/v1
"
API_PACKAGES="
"
+58 -54
View File
@@ -3,11 +3,11 @@
set -e set -e
#set -x #set -x
DIR=$(realpath $(dirname $0))/../ DIR=$(realpath $(dirname "$0"))/../
BUILDDIR=$(realpath $DIR)/build BUILDDIR=$(realpath "$DIR")/build
artifacts=() artifacts=()
source $(realpath ${DIR}/test/init_tools.sh) source $(realpath "${DIR}"/test/init_tools.sh)
doit() { doit() {
echo "! $*" echo "! $*"
@@ -18,7 +18,7 @@ doit() {
check_branch() { check_branch() {
local version=$1 local version=$1
curr_branch=$(git rev-parse --abbrev-ref HEAD) curr_branch=$(git rev-parse --abbrev-ref HEAD)
if [ $curr_branch != "release-${version}" ]; then if [ "$curr_branch" != "release-${version}" ]; then
echo "Not on release-${version} branch." echo "Not on release-${version} branch."
exit 1 exit 1
fi fi
@@ -69,22 +69,18 @@ update_github_charts_repo() {
local version=$1 local version=$1
local chartsrepo=$2 local chartsrepo=$2
pushd $chartsrepo pushd "$chartsrepo"
cp $BUILDDIR/charts/fission-all-${version}.tgz . cp "$BUILDDIR"/charts/fission-all-"${version}".tgz .
cp $BUILDDIR/charts/fission-core-${version}.tgz . cp "$BUILDDIR"/charts/fission-core-"${version}".tgz .
./index.sh ./index.sh
popd popd
} }
gh_release() { gh_release() {
local version=$1 local version=$1
cp "${DIR}"/hack/notes.md relnotes.md
RELFILES="" create_downloads_table ${version} >> relnotes.md
for relfile in ${artifacts[@]}; do doit gh release create "$version" --draft --prerelease --title "$version" --notes-file $(realpath "${DIR}"/relnotes.md) --target "$gitcommit" "${artifacts[@]}"
RELFILES+="\"${relfile}\" "
done
doit gh release create $version --draft --prerelease --title $version --notes-file $(realpath ${DIR}/hack/notes.md) --target $gitcommit ${RELFILES}
} }
generate_changelog() { generate_changelog() {
@@ -95,15 +91,13 @@ generate_changelog() {
echo "[Documentation](https://docs.fission.io/)" >>new_CHANGELOG.md echo "[Documentation](https://docs.fission.io/)" >>new_CHANGELOG.md
echo echo
create_downloads_table ${version} >>new_CHANGELOG.md
# generate changelog from github # generate changelog from github
github_changelog_generator -u fission -p fission -t ${GITHUB_TOKEN} --future-release ${version} --no-issues -o tmp_CHANGELOG.md github_changelog_generator -u fission -p fission -t "${GITHUB_TOKEN}" --future-release "${version}" --no-issues -o tmp_CHANGELOG.md
sed -i '$ d' tmp_CHANGELOG.md sed -i '$ d' tmp_CHANGELOG.md
# concatenate two files # concatenate two files
cat tmp_CHANGELOG.md >>new_CHANGELOG.md cat tmp_CHANGELOG.md >>new_CHANGELOG.md
mv new_CHANGELOG.md ${DIR}/CHANGELOG.md mv new_CHANGELOG.md "${DIR}"/CHANGELOG.md
rm tmp_CHANGELOG.md rm tmp_CHANGELOG.md
} }
@@ -112,7 +106,9 @@ create_downloads_table() {
local release_tag=$1 local release_tag=$1
local url_prefix="https://github.com/fission/fission/releases/download" local url_prefix="https://github.com/fission/fission/releases/download"
echo "## Downloads for ${version}" echo
echo
echo "#### Downloads for ${version}"
echo echo
echo echo
@@ -130,33 +126,28 @@ release_environment_check() {
local version=$1 local version=$1
local chartsrepo=$2 local chartsrepo=$2
check_branch $version check_branch "$version"
check_clean check_clean
if [ ! -f $HOME/.github-token ]; then if [ ! -f "$HOME"/.github-token ]; then
echo "Error finding github access token at ${HOME}/.github-token" echo "Error finding github access token at ${HOME}/.github-token"
exit 1 exit 1
fi fi
if [ ! -d $chartsrepo ]; then if [ ! -d "$chartsrepo" ]; then
echo "Error finding chart repo at $chartsrepo" echo "Error finding chart repo at $chartsrepo"
exit 1 exit 1
fi fi
if [ ! -d $FISSION_HOME ]; then
echo "The FISSION_HOME variable should be set to directory where Fission and fission-charts are checked out"
exit 1
fi
} }
build_charts() { build_charts() {
local version=$1 local version=$1
mkdir -p $BUILDDIR/charts mkdir -p "$BUILDDIR"/charts
pushd $DIR/charts pushd "$DIR"/charts
find . -iname *.~?~ | xargs -r rm find . -iname *.~?~ | xargs -r rm
for c in fission-all fission-core; do for c in fission-all fission-core; do
doit helm package -u $c/ doit helm package -u $c/
mv *.tgz $BUILDDIR/charts/ mv ./*.tgz "$BUILDDIR"/charts/
done done
popd popd
} }
@@ -164,11 +155,11 @@ build_charts() {
build_yamls() { build_yamls() {
local version=$1 local version=$1
mkdir -p ${BUILDDIR}/yamls mkdir -p "${BUILDDIR}"/yamls
pushd ${DIR}/charts pushd "${DIR}"/charts
find . -iname *.~?~ | xargs -r rm find . -iname *.~?~ | xargs -r rm
releaseName=fission-$(echo ${version} | sed 's/\./-/g') releaseName=fission-$(echo "${version}" | sed 's/\./-/g')
for c in fission-all fission-core; do for c in fission-all fission-core; do
# fetch dependencies # fetch dependencies
@@ -176,15 +167,26 @@ build_yamls() {
doit helm dependency update doit helm dependency update
popd popd
echo "Release name", "$releaseName"
cmdprefix="helm template ${releaseName} ${c} --namespace fission --validate"
# for minikube and other environments that don't support LoadBalancer # for minikube and other environments that don't support LoadBalancer
doit helm template ${c} -n ${releaseName} --namespace fission --set analytics=false,analyticsNonHelmInstall=true,serviceType=NodePort,routerServiceType=NodePort >${c}-${version}-minikube.yaml command="$cmdprefix --set analytics=false,analyticsNonHelmInstall=true,serviceType=NodePort,routerServiceType=NodePort"
echo "$command"
$command >${c}-"${version}"-minikube.yaml
# for environments that support LoadBalancer # for environments that support LoadBalancer
doit helm template ${c} -n ${releaseName} --namespace fission --set analytics=false,analyticsNonHelmInstall=true >${c}-${version}.yaml command="$cmdprefix --set analytics=false,analyticsNonHelmInstall=true"
echo "$command"
$command >${c}-"${version}".yaml
# for OpenShift # for OpenShift
doit helm template ${c} -n ${releaseName} --namespace fission --set analytics=false,analyticsNonHelmInstall=true,logger.enableSecurityContext=true,prometheus.enabled=false >${c}-${version}-openshift.yaml command="$cmdprefix --set analytics=false,analyticsNonHelmInstall=true,logger.enableSecurityContext=true,prometheus.enabled=false"
echo "$command"
$command >${c}-"${version}"-openshift.yaml
# copy yaml files to build directory # copy yaml files to build directory
mv *.yaml ${BUILDDIR}/yamls/ mv ./*.yaml "${BUILDDIR}"/yamls/
done done
popd popd
@@ -212,12 +214,12 @@ build_all() {
exit 1 exit 1
fi fi
if [ -e $BUILDDIR ]; then if [ -e "$BUILDDIR" ]; then
echo "Removing existing build dir ($BUILDDIR)." echo "Removing existing build dir ($BUILDDIR)."
rm -rf $BUILDDIR rm -rf "$BUILDDIR"
fi fi
mkdir -p $BUILDDIR mkdir -p "$BUILDDIR"
# generate swagger (OpenApi 2.0) doc before building bundle image # generate swagger (OpenApi 2.0) doc before building bundle image
VERSION=$version TIMESTAMP=$date COMMITSHA=$gitcommit make generate-swagger-doc VERSION=$version TIMESTAMP=$date COMMITSHA=$gitcommit make generate-swagger-doc
@@ -240,7 +242,7 @@ build_images() {
fi fi
local gitcommit=$3 local gitcommit=$3
if [ -z "gitcommit" ]; then if [ -z "$gitcommit" ]; then
echo "Git commit unspecified" echo "Git commit unspecified"
exit 1 exit 1
fi fi
@@ -259,7 +261,7 @@ check_commands() {
export GITHUB_TOKEN=$(cat ~/.github-token) export GITHUB_TOKEN=$(cat ~/.github-token)
version=$1 version=$1
if [ -z $version ]; then if [ -z "$version" ]; then
echo "Release version not mentioned" echo "Release version not mentioned"
exit 1 exit 1
fi fi
@@ -268,23 +270,25 @@ date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
gitcommit=$(git rev-parse HEAD) gitcommit=$(git rev-parse HEAD)
chartsrepo=$2 chartsrepo=$2
if [ -z $chartsrepo ]; then if [ -z "$chartsrepo" ]; then
chartsrepo="$DIR../fission-charts" chartsrepo="$DIR../fission-charts"
fi fi
check_commands check_commands
release_environment_check $version $chartsrepo release_environment_check "$version" "$chartsrepo"
build_all $version $date $gitcommit build_all "$version" "$date" "$gitcommit"
build_images $version $date $gitcommit build_charts "$version"
build_charts $version build_yamls "$version"
build_yamls $version
attach_github_release_cli $version attach_github_release_cli "$version"
attach_github_release_charts $version attach_github_release_charts "$version"
attach_github_release_yamls $version attach_github_release_yamls "$version"
update_github_charts_repo $version $chartsrepo update_github_charts_repo "$version" "$chartsrepo"
generate_changelog $version gh_release "$version"
gh_release $version
generate_changelog "$version"
build_images "$version" "$date" "$gitcommit"
echo "############ DONE #############" echo "############ DONE #############"
echo "Congratulation, ${version} is ready to ship !!" echo "Congratulation, ${version} is ready to ship !!"
+1 -1
View File
@@ -31,7 +31,7 @@ echo "" > coverage.txt
# The executor unit test only works with NodePort-type services for # The executor unit test only works with NodePort-type services for
# now. So disable it for our travis ci tests except some partial tests. # now. So disable it for our travis ci tests except some partial tests.
for d in $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go' | grep -v executor | grep -v 'benchmark') github.com/fission/fission/pkg/executor/util; do for d in $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go' | grep -v executor | grep -v 'benchmark') github.com/fission/fission/pkg/executor/util; do
go test -v -coverprofile=profile.out -covermode=atomic $d go test -race -v -coverprofile=profile.out -covermode=atomic $d
if [ -f profile.out ]; then if [ -f profile.out ]; then
cat profile.out >> coverage.txt cat profile.out >> coverage.txt
rm profile.out rm profile.out
-93
View File
@@ -1,93 +0,0 @@
#!/bin/bash
#
# Download kubectl, save kubeconfig, and ensure we can access the test cluster
#
set -e
TOOL_DIR=$HOME/tool
if [ ! -d $TOOL_DIR ]
then
mkdir -p $TOOL_DIR
fi
# Get staticcheck
STATICCHECK_VERSION=2019.2.3
if [ ! -f $TOOL_DIR/staticcheck ] || (staticcheck -version | grep -v $STATICCHECK_VERSION)
then
curl -LO https://github.com/dominikh/go-tools/releases/download/${STATICCHECK_VERSION}/staticcheck_linux_amd64.tar.gz
tar xzvf staticcheck_linux_amd64.tar.gz
mv staticcheck/staticcheck $TOOL_DIR/staticcheck
fi
K8SCLI_DIR=$HOME/k8scli
if [ ! -d $K8SCLI_DIR ]
then
mkdir -p $K8SCLI_DIR
fi
# Get helm
HELM_VERSION=3.3.0
if [ ! -f $K8SCLI_DIR/helm ] || (helm version --client | grep -v $HELM_VERSION)
then
curl -LO https://get.helm.sh/helm-v3.3.0-linux-amd64.tar.gz
tar xzvf helm-*.tar.gz
mv linux-amd64/helm $K8SCLI_DIR/helm
fi
# If we don't have gcloud credentials, bail out of these tests.
if [ -z "$FISSION_CI_SERVICE_ACCOUNT" ]
then
echo "Skipping tests, no cluster credentials"
exit 0
fi
# Get kubectl
if [ ! -f $K8SCLI_DIR/kubectl ]
then
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
chmod +x ./kubectl
mv kubectl $K8SCLI_DIR/kubectl
fi
mkdir ${HOME}/.kube
# echo $KUBECONFIG_CONTENTS | base64 -D - > ${HOME}/.kube/config
# kubectl version
# gcloud stuff
# https://stackoverflow.com/questions/38762590/how-to-install-google-cloud-sdk-on-travis
if [ ! -d "${HOME}/google-cloud-sdk/bin" ]
then
rm -rf $HOME/google-cloud-sdk
export CLOUDSDK_CORE_DISABLE_PROMPTS=1
curl https://sdk.cloud.google.com | bash
fi
# ensure we have the gcloud binary
gcloud version
# get gcloud credentials
echo $FISSION_CI_SERVICE_ACCOUNT | base64 -d - > ${HOME}/gcloud-service-key.json
gcloud auth activate-service-account --key-file ${HOME}/gcloud-service-key.json
# get kube config
gcloud container clusters get-credentials fission-ci --zone us-central1-a --project $GKE_PROJECT_NAME
# remove gcloud creds
unset FISSION_CI_SERVICE_ACCOUNT
rm ${HOME}/gcloud-service-key.json
# does it work?
if [ ! -f ${HOME}/.kube/config ]
then
echo "Missing kubeconfig"
exit 1
fi
kubectl get node
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
if [ ! -d "../code-generator" ]; then
echo "Please get code-generator from fission org"
exit 1
fi
SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister" \
github.com/fission/fission/pkg/generated \
github.com/fission/fission/pkg/apis \
"core:v1" \
--output-base "$(dirname "${BASH_SOURCE[0]}")/../../../.." \
--go-header-file "$(dirname "${BASH_SOURCE[0]}")/boilerplate.txt"
+56
View File
@@ -0,0 +1,56 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
source "$(dirname "${BASH_SOURCE}")/lib/init.sh"
SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/..
# Generates types_swagger_doc_generated file for the given group version.
# $1: Name of the group version
# $2: Path to the directory where types.go for that group version exists. This
# is the directory where the file will be generated.
kube::swagger::gen_types_swagger_doc() {
local group_version=$1
local gv_dir=$2
local TMPFILE="${TMPDIR:-/tmp}/zz_generated.swagger_doc_generated.$(date +%s).go"
echo "Generating swagger type docs for ${group_version} at ${gv_dir}"
sed 's/YEAR/2017/' hack/boilerplate.txt > "$TMPFILE"
echo "package ${group_version##*/}" >> "$TMPFILE"
cat >> "$TMPFILE" <<EOF
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//
// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if
// they are on one line! For multiple line or blocks that you want to ignore use ---.
// Any context after a --- is ignored.
//
// Those methods can be generated by using hack/update-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE
EOF
go run tools/genswaggertypedocs/swagger_type_docs.go -s \
${gv_dir}/types*.go \
-f - \
>> "$TMPFILE"
echo "// AUTO-GENERATED FUNCTIONS END HERE" >> "$TMPFILE"
gofmt -w -s "$TMPFILE"
mv "$TMPFILE" ""${gv_dir}"/zz_generated.swagger_doc_generated.go"
}
util::group-version-to-pkg-path() {
local group_version="$1"
echo "pkg/apis/${group_version}"
}
for gv in ${API_GROUP_VERSIONS}; do
rm -f "${SCRIPT_ROOT}/${gv}/zz_generated.swagger_doc_generated.go"
util::group-version-to-pkg-path "${gv}"
kube::swagger::gen_types_swagger_doc "${gv}" "$(util::group-version-to-pkg-path "${gv}")"
done
-21
View File
@@ -1,21 +0,0 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
find_files() {
find . -not \( \
\( \
-wholename '*/vendor/*' \
\) -prune \
\) -name '*.go'
}
GOFMT="gofmt -s"
bad_files=$(find_files | xargs $GOFMT -l)
if [[ -n "${bad_files}" ]]; then
echo "!!! '$GOFMT' needs to be run on the following files: "
echo "${bad_files}"
exit 1
fi
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
go vet -v $(go list ./...| grep -v "vendor" | grep -v "examples" | grep -v "genclient" | grep -v "demos" | grep -v "test")
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
go list ./...| grep -v vendor | grep -v "examples" | grep -v "demos" | grep -v "genclient" | grep -v "test" | xargs -I@ staticcheck @
-1
View File
@@ -8,7 +8,6 @@ kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4 apiVersion: kind.x-k8s.io/v1alpha4
nodes: nodes:
- role: control-plane - role: control-plane
image: kindest/node:v1.19.11
kubeadmConfigPatches: kubeadmConfigPatches:
- | - |
kind: InitConfiguration kind: InitConfiguration
+3 -9
View File
@@ -1,18 +1,12 @@
# Fission CRD generation # Fission CRD generation
* Clone https://github.com/fission/code-generator to generate fission CRD object deepcopy and client methods. * Clone [code-generator](https://github.com/fission/code-generator) to generate fission CRD object deepcopy and client methods.
* MUST run code-generator in the fission root directory. * MUST run code-generator in the fission root directory.
``` bash ``` bash
$ cd $GOPATH/src/github.com/fission/fission/ $ make codgen
$ bash $GOPATH/src/k8s.io/code-generator/generate-groups.sh \
all \
github.com/fission/fission/pkg/apis/genclient \
github.com/fission/fission/pkg/apis \
"core:v1" \
--go-header-file $GOPATH/src/github.com/fission/fission/pkg/apis/boilerplate.txt
``` ```
# Reference ## Reference
* https://blog.openshift.com/kubernetes-deep-dive-code-generation-customresources/ * https://blog.openshift.com/kubernetes-deep-dive-code-generation-customresources/
+1
View File
@@ -55,6 +55,7 @@ const (
const ( const (
ExecutorTypePoolmgr ExecutorType = "poolmgr" ExecutorTypePoolmgr ExecutorType = "poolmgr"
ExecutorTypeNewdeploy ExecutorType = "newdeploy" ExecutorTypeNewdeploy ExecutorType = "newdeploy"
ExecutorTypeContainer ExecutorType = "container"
) )
const ( const (
-7
View File
@@ -1,7 +0,0 @@
# How to update swagger (OpenAPI) struct description
Run `update-generated-swagger-docs.sh` and it will parse all comments in `types.go`.
```bash
./update-generated-swagger-docs.sh
```
-67
View File
@@ -1,67 +0,0 @@
#!/usr/bin/env bash
# Copyright 2016 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Please refer https://github.com/kubernetes/kubernetes/tree/master/hack for original file
#
# Contains swagger related util functions.
#
set -o errexit
set -o nounset
set -o pipefail
# Generates types_swagger_doc_generated file for the given group version.
# $1: Name of the group version
# $2: Path to the directory where types.go for that group version exists. This
# is the directory where the file will be generated.
kube::swagger::gen_types_swagger_doc() {
local group_version=$1
local gv_dir=$2
local TMPFILE
TMPFILE="${TMPDIR:-/tmp}/types_swagger_doc_generated.$(date +%s).go"
echo "Generating swagger type docs for ${group_version} at ${gv_dir}"
{
echo -e "$(cat boilerplate.generatego.txt)\n"
echo "package ${group_version##*/}"
cat <<EOF
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//
// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if
// they are on one line! For multiple line or blocks that you want to ignore use ---.
// Any context after a --- is ignored.
//
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
EOF
} > "${TMPFILE}"
go run ./swagger_type_docs.go -s \
"${gv_dir}/types.go" \
-f - \
>> "${TMPFILE}"
echo "// AUTO-GENERATED FUNCTIONS END HERE" >> "${TMPFILE}"
gofmt -w -s "${TMPFILE}"
mv "${TMPFILE}" "${gv_dir}/types_swagger_doc_generated.go"
}
@@ -1,39 +0,0 @@
#!/usr/bin/env bash
# Copyright 2015 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Generates `types_swagger_doc_generated.go` files for API group
# versions. That file contains functions on API structs that return
# the comments that should be surfaced for the corresponding API type
# in our API docs.
#
# Please refer https://github.com/kubernetes/kubernetes/tree/master/hack for original file
#
set -o errexit
set -o nounset
set -o pipefail
FISSION_CRD_VERSION=v1
source "swagger.sh"
# To avoid compile errors, remove the currently existing files.
for group_version in "${FISSION_CRD_VERSION}"; do
kube::swagger::gen_types_swagger_doc "${group_version}" ../../${FISSION_CRD_VERSION}
done
+11
View File
@@ -409,6 +409,11 @@ type (
// This is optional. If not specified default value will be taken as false // This is optional. If not specified default value will be taken as false
// +optional // +optional
OnceOnly bool `json:"onceOnly,omitempty"` OnceOnly bool `json:"onceOnly,omitempty"`
// Podspec specifies podspec to use for executor type container based functions
// Different arguments mentioned for container based function are populated inside a pod.
// +optional
PodSpec *apiv1.PodSpec `json:"podspec,omitempty"`
} }
// InvokeStrategy is a set of controls over how the function executes. // InvokeStrategy is a set of controls over how the function executes.
@@ -450,6 +455,7 @@ type (
// Available value: // Available value:
// - poolmgr // - poolmgr
// - newdeploy // - newdeploy
// - container
// +optional // +optional
ExecutorType ExecutorType `json:"ExecutorType"` ExecutorType ExecutorType `json:"ExecutorType"`
@@ -658,6 +664,11 @@ type (
// +optional // +optional
Prefix *string `json:"prefix,omitempty"` Prefix *string `json:"prefix,omitempty"`
// When function is exposed with Prefix based path,
// keepPrefix decides whether to keep or trim prefix in URL while invoking function.
// +optional
KeepPrefix bool `json:"keepPrefix,omitempty"`
// Use Methods instead of Method. This field is going to be deprecated in a future release // Use Methods instead of Method. This field is going to be deprecated in a future release
// HTTP method to access a function. // HTTP method to access a function.
// +optional // +optional
+5 -1
View File
@@ -274,6 +274,10 @@ func (spec FunctionSpec) Validate() error {
result = multierror.Append(result, spec.InvokeStrategy.Validate()) result = multierror.Append(result, spec.InvokeStrategy.Validate())
} }
if spec.InvokeStrategy.ExecutionStrategy.ExecutorType == ExecutorTypeContainer && spec.PodSpec == nil {
result = multierror.Append(result, MakeValidationErr(ErrorInvalidObject, "FunctionSpec.PodSpec", "", "executor type container requires a pod spec"))
}
// TODO Add below validation warning // TODO Add below validation warning
/*if spec.FunctionTimeout <= 0 { /*if spec.FunctionTimeout <= 0 {
result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "FunctionTimeout value", spec.FunctionTimeout, "not a valid value. Should always be more than 0")) result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "FunctionTimeout value", spec.FunctionTimeout, "not a valid value. Should always be more than 0"))
@@ -300,7 +304,7 @@ func (es ExecutionStrategy) Validate() error {
result := &multierror.Error{} result := &multierror.Error{}
switch es.ExecutorType { switch es.ExecutorType {
case ExecutorTypeNewdeploy, ExecutorTypePoolmgr: // no op case ExecutorTypeNewdeploy, ExecutorTypePoolmgr, ExecutorTypeContainer: // no op
default: default:
result = multierror.Append(result, MakeValidationErr(ErrorUnsupportedType, "ExecutionStrategy.ExecutorType", es.ExecutorType, "not a valid executor type")) result = multierror.Append(result, MakeValidationErr(ErrorUnsupportedType, "ExecutionStrategy.ExecutorType", es.ExecutorType, "not a valid executor type"))
} }
@@ -431,6 +431,11 @@ func (in *FunctionSpec) DeepCopyInto(out *FunctionSpec) {
*out = new(int) *out = new(int)
**out = **in **out = **in
} }
if in.PodSpec != nil {
in, out := &in.PodSpec, &out.PodSpec
*out = new(corev1.PodSpec)
(*in).DeepCopyInto(*out)
}
return return
} }
@@ -1,5 +1,5 @@
/* /*
Copyright 2019 The Fission Authors. Copyright The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package v1 package v1
// This file contains a collection of methods that can be used from go-restful to // This file contains a collection of methods that can be used from go-restful to
@@ -24,9 +23,8 @@ package v1
// they are on one line! For multiple line or blocks that you want to ignore use ---. // they are on one line! For multiple line or blocks that you want to ignore use ---.
// Any context after a --- is ignored. // Any context after a --- is ignored.
// //
// Those methods can be generated by using hack/update-generated-swagger-docs.sh // Those methods can be generated by using hack/update-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_Archive = map[string]string{ var map_Archive = map[string]string{
"": "Archive contains or references a collection of source or binary files.", "": "Archive contains or references a collection of source or binary files.",
"type": "Type defines how the package is specified: literal or URL. Available value:\n - literal\n - url", "type": "Type defines how the package is specified: literal or URL. Available value:\n - literal\n - url",
@@ -149,7 +147,7 @@ func (EnvironmentSpec) SwaggerDoc() map[string]string {
var map_ExecutionStrategy = map[string]string{ var map_ExecutionStrategy = map[string]string{
"": "ExecutionStrategy specifies low-level parameters for function execution, such as the number of instances.\n\nMinScale affects the cold start behavior for a function. If MinScale is 0 then the deployment is created on first invocation of function and is good for requests of asynchronous nature. If MinScale is greater than 0 then MinScale number of pods are created at the time of creation of function. This ensures faster response during first invocation at the cost of consuming resources.\n\nMaxScale is the maximum number of pods that function will scale to based on TargetCPUPercent and resources allocated to the function pod.", "": "ExecutionStrategy specifies low-level parameters for function execution, such as the number of instances.\n\nMinScale affects the cold start behavior for a function. If MinScale is 0 then the deployment is created on first invocation of function and is good for requests of asynchronous nature. If MinScale is greater than 0 then MinScale number of pods are created at the time of creation of function. This ensures faster response during first invocation at the cost of consuming resources.\n\nMaxScale is the maximum number of pods that function will scale to based on TargetCPUPercent and resources allocated to the function pod.",
"ExecutorType": "ExecutorType is the executor type of a function used. Defaults to \"poolmgr\".\n\nAvailable value:\n - poolmgr\n - newdeploy", "ExecutorType": "ExecutorType is the executor type of a function used. Defaults to \"poolmgr\".\n\nAvailable value:\n - poolmgr\n - newdeploy\n - container",
"MinScale": "This is only for newdeploy to set up minimum replicas of deployment.", "MinScale": "This is only for newdeploy to set up minimum replicas of deployment.",
"MaxScale": "This is only for newdeploy to set up maximum replicas of deployment.", "MaxScale": "This is only for newdeploy to set up maximum replicas of deployment.",
"TargetCPUPercent": "This is only for newdeploy to set up target CPU utilization of HPA.", "TargetCPUPercent": "This is only for newdeploy to set up target CPU utilization of HPA.",
@@ -210,6 +208,7 @@ var map_FunctionSpec = map[string]string{
"concurrency": "Maximum number of pods to be specialized which will serve requests This is optional. If not specified default value will be taken as 500", "concurrency": "Maximum number of pods to be specialized which will serve requests This is optional. If not specified default value will be taken as 500",
"requestsPerPod": "RequestsPerPod indicates the maximum number of concurrent requests that can be served by a specialized pod This is optional. If not specified default value will be taken as 1", "requestsPerPod": "RequestsPerPod indicates the maximum number of concurrent requests that can be served by a specialized pod This is optional. If not specified default value will be taken as 1",
"onceOnly": "OnceOnly specifies if specialized pod will serve exactly one request in its lifetime and would be garbage collected after serving that one request This is optional. If not specified default value will be taken as false", "onceOnly": "OnceOnly specifies if specialized pod will serve exactly one request in its lifetime and would be garbage collected after serving that one request This is optional. If not specified default value will be taken as false",
"podspec": "Podspec specifies podspec to use for executor type container based functions Different arguments mentioned for container based function are populated inside a pod.",
} }
func (FunctionSpec) SwaggerDoc() map[string]string { func (FunctionSpec) SwaggerDoc() map[string]string {
@@ -237,6 +236,7 @@ var map_HTTPTriggerSpec = map[string]string{
"host": "Deprecated: the original idea of this field is not for setting Ingress. Since we have IngressConfig now, remove Host after couple releases.", "host": "Deprecated: the original idea of this field is not for setting Ingress. Since we have IngressConfig now, remove Host after couple releases.",
"relativeurl": "RelativeURL is the exposed URL for external client to access a function with.", "relativeurl": "RelativeURL is the exposed URL for external client to access a function with.",
"prefix": "Prefix with which functions are exposed. NOTE: Prefix takes precedence over URL/RelativeURL. Note that it does not treat slashes specially (\"/foobar/\" will be matched by the prefix \"/foobar\").", "prefix": "Prefix with which functions are exposed. NOTE: Prefix takes precedence over URL/RelativeURL. Note that it does not treat slashes specially (\"/foobar/\" will be matched by the prefix \"/foobar\").",
"keepPrefix": "When function is exposed with Prefix based path, keepPrefix decides whether to keep or trim prefix in URL while invoking function.",
"method": "Use Methods instead of Method. This field is going to be deprecated in a future release HTTP method to access a function.", "method": "Use Methods instead of Method. This field is going to be deprecated in a future release HTTP method to access a function.",
"methods": "HTTP methods to access a function", "methods": "HTTP methods to access a function",
"functionref": "FunctionReference is a reference to the target function.", "functionref": "FunctionReference is a reference to the target function.",
+1 -1
View File
@@ -82,7 +82,7 @@ func (c *Client) Build(req *builder.PackageBuildRequest) (*builder.PackageBuildR
} }
pkgBuildResp := builder.PackageBuildResponse{} pkgBuildResp := builder.PackageBuildResponse{}
err = json.Unmarshal([]byte(rBody), &pkgBuildResp) err = json.Unmarshal(rBody, &pkgBuildResp)
if err != nil { if err != nil {
c.logger.Error("error parsing resp body", zap.Error(err)) c.logger.Error("error parsing resp body", zap.Error(err))
return nil, err return nil, err
+11 -4
View File
@@ -17,11 +17,15 @@ limitations under the License.
package buildermgr package buildermgr
import ( import (
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
k8sInformers "k8s.io/client-go/informers"
"github.com/fission/fission/pkg/crd" "github.com/fission/fission/pkg/crd"
fetcherConfig "github.com/fission/fission/pkg/fetcher/config" fetcherConfig "github.com/fission/fission/pkg/fetcher/config"
genInformer "github.com/fission/fission/pkg/generated/informers/externalversions"
) )
// Start the buildermgr service. // Start the buildermgr service.
@@ -46,9 +50,12 @@ func Start(logger *zap.Logger, storageSvcUrl string, envBuilderNamespace string)
envWatcher := makeEnvironmentWatcher(bmLogger, fissionClient, kubernetesClient, fetcherConfig, envBuilderNamespace) envWatcher := makeEnvironmentWatcher(bmLogger, fissionClient, kubernetesClient, fetcherConfig, envBuilderNamespace)
go envWatcher.watchEnvironments() go envWatcher.watchEnvironments()
k8sInformerFactory := k8sInformers.NewSharedInformerFactory(kubernetesClient, time.Second*30)
informerFactory := genInformer.NewSharedInformerFactory(fissionClient, 60*time.Minute)
podInformer := k8sInformerFactory.Core().V1().Pods().Informer()
pkgInformer := informerFactory.Core().V1().Packages().Informer()
pkgWatcher := makePackageWatcher(bmLogger, fissionClient, pkgWatcher := makePackageWatcher(bmLogger, fissionClient,
kubernetesClient, envBuilderNamespace, storageSvcUrl) kubernetesClient, envBuilderNamespace, storageSvcUrl, &podInformer, &pkgInformer)
go pkgWatcher.watchPackages() pkgWatcher.Run()
return nil
select {}
} }
+22 -24
View File
@@ -25,7 +25,6 @@ import (
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache" k8sCache "k8s.io/client-go/tools/cache"
@@ -40,26 +39,26 @@ type (
logger *zap.Logger logger *zap.Logger
fissionClient *crd.FissionClient fissionClient *crd.FissionClient
k8sClient *kubernetes.Clientset k8sClient *kubernetes.Clientset
podStore k8sCache.Store podInformer *k8sCache.SharedIndexInformer
pkgStore k8sCache.Store pkgInformer *k8sCache.SharedIndexInformer
builderNamespace string builderNamespace string
storageSvcUrl string storageSvcUrl string
buildCache *cache.Cache
} }
) )
func makePackageWatcher(logger *zap.Logger, fissionClient *crd.FissionClient, k8sClientSet *kubernetes.Clientset, func makePackageWatcher(logger *zap.Logger, fissionClient *crd.FissionClient, k8sClientSet *kubernetes.Clientset,
builderNamespace string, storageSvcUrl string) *packageWatcher { builderNamespace string, storageSvcUrl string, podInformer *k8sCache.SharedIndexInformer,
lw := k8sCache.NewListWatchFromClient(k8sClientSet.CoreV1().RESTClient(), "pods", metav1.NamespaceAll, fields.Everything()) pkgInformer *k8sCache.SharedIndexInformer) *packageWatcher {
store, controller := k8sCache.NewInformer(lw, &apiv1.Pod{}, 30*time.Second, k8sCache.ResourceEventHandlerFuncs{})
go controller.Run(make(chan struct{}))
pkgw := &packageWatcher{ pkgw := &packageWatcher{
logger: logger.Named("package_watcher"), logger: logger.Named("package_watcher"),
fissionClient: fissionClient, fissionClient: fissionClient,
k8sClient: k8sClientSet, k8sClient: k8sClientSet,
podStore: store, podInformer: podInformer,
pkgInformer: pkgInformer,
builderNamespace: builderNamespace, builderNamespace: builderNamespace,
storageSvcUrl: storageSvcUrl, storageSvcUrl: storageSvcUrl,
buildCache: cache.MakeCache(0, 0),
} }
return pkgw return pkgw
} }
@@ -74,15 +73,15 @@ func makePackageWatcher(logger *zap.Logger, fissionClient *crd.FissionClient, k8
// 5. Update package resource in package ref of functions that share the same package // 5. Update package resource in package ref of functions that share the same package
// 6. Update package status to succeed state // 6. Update package status to succeed state
// *. Update package status to failed state,if any one of steps above failed/time out // *. Update package status to failed state,if any one of steps above failed/time out
func (pkgw *packageWatcher) build(buildCache *cache.Cache, srcpkg *fv1.Package) { func (pkgw *packageWatcher) build(srcpkg *fv1.Package) {
// Ignore duplicate build requests // Ignore duplicate build requests
key := fmt.Sprintf("%v-%v", srcpkg.ObjectMeta.Name, srcpkg.ObjectMeta.ResourceVersion) key := fmt.Sprintf("%v-%v", srcpkg.ObjectMeta.Name, srcpkg.ObjectMeta.ResourceVersion)
_, err := buildCache.Set(key, srcpkg) _, err := pkgw.buildCache.Set(key, srcpkg)
if err != nil { if err != nil {
return return
} }
defer func() { defer func() {
err := buildCache.Delete(key) err := pkgw.buildCache.Delete(key)
if err != nil { if err != nil {
pkgw.logger.Error("error deleting key from cache", zap.String("key", key), zap.Error(err)) pkgw.logger.Error("error deleting key from cache", zap.String("key", key), zap.Error(err))
} }
@@ -122,7 +121,7 @@ func (pkgw *packageWatcher) build(buildCache *cache.Cache, srcpkg *fv1.Package)
for healthCheckBackOff.NextExists() { for healthCheckBackOff.NextExists() {
// Informer store is not able to use label to find the pod, // Informer store is not able to use label to find the pod,
// iterate all available environment builders. // iterate all available environment builders.
items := pkgw.podStore.List() items := (*pkgw.podInformer).GetStore().List()
if err != nil { if err != nil {
pkgw.logger.Error("error retrieving pod information for environment", zap.Error(err), zap.String("environment", env.ObjectMeta.Name)) pkgw.logger.Error("error retrieving pod information for environment", zap.Error(err), zap.String("environment", env.ObjectMeta.Name))
return return
@@ -281,10 +280,7 @@ func (pkgw *packageWatcher) build(buildCache *cache.Cache, srcpkg *fv1.Package)
zap.String("package", fmt.Sprintf("%s.%s", pkg.ObjectMeta.Name, pkg.ObjectMeta.Namespace))) zap.String("package", fmt.Sprintf("%s.%s", pkg.ObjectMeta.Name, pkg.ObjectMeta.Namespace)))
} }
func (pkgw *packageWatcher) watchPackages() { func (pkgw *packageWatcher) packageInformerHandler() k8sCache.ResourceEventHandlerFuncs {
buildCache := cache.MakeCache(0, 0)
lw := k8sCache.NewListWatchFromClient(pkgw.fissionClient.CoreV1().RESTClient(), "packages", apiv1.NamespaceAll, fields.Everything())
processPkg := func(pkg *fv1.Package) { processPkg := func(pkg *fv1.Package) {
var err error var err error
@@ -298,14 +294,12 @@ func (pkgw *packageWatcher) watchPackages() {
// don't need to build the package at this moment. // don't need to build the package at this moment.
return return
} }
// Only build pending state packages. // Only build pending state packages.
if pkg.Status.BuildStatus == fv1.BuildStatusPending { if pkg.Status.BuildStatus == fv1.BuildStatusPending {
go pkgw.build(buildCache, pkg) go pkgw.build(pkg)
} }
} }
return k8sCache.ResourceEventHandlerFuncs{
pkgStore, controller := k8sCache.NewInformer(lw, &fv1.Package{}, 60*time.Minute, k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { AddFunc: func(obj interface{}) {
pkg := obj.(*fv1.Package) pkg := obj.(*fv1.Package)
processPkg(pkg) processPkg(pkg)
@@ -325,10 +319,14 @@ func (pkgw *packageWatcher) watchPackages() {
} }
processPkg(pkg) processPkg(pkg)
}, },
}) }
}
pkgw.pkgStore = pkgStore func (pkgw *packageWatcher) Run() {
controller.Run(make(chan struct{})) context := context.Background()
go (*pkgw.podInformer).Run(context.Done())
(*pkgw.pkgInformer).AddEventHandler(pkgw.packageInformerHandler())
(*pkgw.pkgInformer).Run(context.Done())
} }
// setInitialBuildStatus sets initial build status to a package if it is empty. // setInitialBuildStatus sets initial build status to a package if it is empty.
+35 -44
View File
@@ -28,13 +28,12 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
k8serrors "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
k8sCache "k8s.io/client-go/tools/cache" k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1" fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/crd" "github.com/fission/fission/pkg/crd"
genInformer "github.com/fission/fission/pkg/generated/informers/externalversions"
) )
const ( const (
@@ -45,14 +44,12 @@ type canaryConfigMgr struct {
logger *zap.Logger logger *zap.Logger
fissionClient *crd.FissionClient fissionClient *crd.FissionClient
kubeClient *kubernetes.Clientset kubeClient *kubernetes.Clientset
canaryConfigStore k8sCache.Store canaryConfigInformer *k8sCache.SharedIndexInformer
canaryConfigController k8sCache.Controller
promClient *PrometheusApiClient promClient *PrometheusApiClient
crdClient rest.Interface
canaryCfgCancelFuncMap *canaryConfigCancelFuncMap canaryCfgCancelFuncMap *canaryConfigCancelFuncMap
} }
func MakeCanaryConfigMgr(logger *zap.Logger, fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset, crdClient rest.Interface, prometheusSvc string) (*canaryConfigMgr, error) { func MakeCanaryConfigMgr(logger *zap.Logger, fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset, prometheusSvc string) (*canaryConfigMgr, error) {
if prometheusSvc == "" { if prometheusSvc == "" {
logger.Info("try to retrieve prometheus server information from environment variables") logger.Info("try to retrieve prometheus server information from environment variables")
@@ -92,54 +89,48 @@ func MakeCanaryConfigMgr(logger *zap.Logger, fissionClient *crd.FissionClient, k
logger: logger.Named("canary_config_manager"), logger: logger.Named("canary_config_manager"),
fissionClient: fissionClient, fissionClient: fissionClient,
kubeClient: kubeClient, kubeClient: kubeClient,
crdClient: crdClient,
promClient: promClient, promClient: promClient,
canaryCfgCancelFuncMap: makecanaryConfigCancelFuncMap(), canaryCfgCancelFuncMap: makecanaryConfigCancelFuncMap(),
} }
store, controller := configMgr.initCanaryConfigController() informerFactory := genInformer.NewSharedInformerFactory(fissionClient, time.Second*30)
configMgr.canaryConfigStore = store informer := informerFactory.Core().V1().CanaryConfigs().Informer()
configMgr.canaryConfigController = controller configMgr.canaryConfigInformer = &informer
configMgr.CanaryConfigEventHandlers()
return configMgr, nil return configMgr, nil
} }
func (canaryCfgMgr *canaryConfigMgr) initCanaryConfigController() (k8sCache.Store, k8sCache.Controller) { func (canaryCfgMgr *canaryConfigMgr) CanaryConfigEventHandlers() {
resyncPeriod := 30 * time.Second (*canaryCfgMgr.canaryConfigInformer).AddEventHandler(k8sCache.ResourceEventHandlerFuncs{
listWatch := k8sCache.NewListWatchFromClient(canaryCfgMgr.crdClient, "canaryconfigs", metav1.NamespaceAll, fields.Everything()) AddFunc: func(obj interface{}) {
store, controller := k8sCache.NewInformer(listWatch, &fv1.CanaryConfig{}, resyncPeriod, canaryConfig := obj.(*fv1.CanaryConfig)
k8sCache.ResourceEventHandlerFuncs{ if canaryConfig.Status.Status == fv1.CanaryConfigStatusPending {
AddFunc: func(obj interface{}) { go canaryCfgMgr.addCanaryConfig(canaryConfig)
canaryConfig := obj.(*fv1.CanaryConfig) }
if canaryConfig.Status.Status == fv1.CanaryConfigStatusPending { },
go canaryCfgMgr.addCanaryConfig(canaryConfig) DeleteFunc: func(obj interface{}) {
} canaryConfig := obj.(*fv1.CanaryConfig)
}, go canaryCfgMgr.deleteCanaryConfig(canaryConfig)
DeleteFunc: func(obj interface{}) { },
canaryConfig := obj.(*fv1.CanaryConfig) UpdateFunc: func(oldObj interface{}, newObj interface{}) {
go canaryCfgMgr.deleteCanaryConfig(canaryConfig) oldConfig := oldObj.(*fv1.CanaryConfig)
}, newConfig := newObj.(*fv1.CanaryConfig)
UpdateFunc: func(oldObj interface{}, newObj interface{}) { if oldConfig.ObjectMeta.ResourceVersion != newConfig.ObjectMeta.ResourceVersion &&
oldConfig := oldObj.(*fv1.CanaryConfig) newConfig.Status.Status == fv1.CanaryConfigStatusPending {
newConfig := newObj.(*fv1.CanaryConfig) canaryCfgMgr.logger.Info("update canary config invoked",
if oldConfig.ObjectMeta.ResourceVersion != newConfig.ObjectMeta.ResourceVersion && zap.String("name", newConfig.ObjectMeta.Name),
newConfig.Status.Status == fv1.CanaryConfigStatusPending { zap.String("namespace", newConfig.ObjectMeta.Namespace),
canaryCfgMgr.logger.Info("update canary config invoked", zap.String("version", newConfig.ObjectMeta.ResourceVersion))
zap.String("name", newConfig.ObjectMeta.Name), go canaryCfgMgr.updateCanaryConfig(oldConfig, newConfig)
zap.String("namespace", newConfig.ObjectMeta.Namespace), }
zap.String("version", newConfig.ObjectMeta.ResourceVersion)) go canaryCfgMgr.reSyncCanaryConfigs()
go canaryCfgMgr.updateCanaryConfig(oldConfig, newConfig)
}
go canaryCfgMgr.reSyncCanaryConfigs()
}, },
}) })
return store, controller
} }
func (canaryCfgMgr *canaryConfigMgr) Run(ctx context.Context) { func (canaryCfgMgr *canaryConfigMgr) Run(ctx context.Context) {
go canaryCfgMgr.canaryConfigController.Run(ctx.Done()) go (*canaryCfgMgr.canaryConfigInformer).Run(ctx.Done())
canaryCfgMgr.logger.Info("started canary configmgr controller") canaryCfgMgr.logger.Info("started canary configmgr controller")
} }
@@ -501,7 +492,7 @@ func (canaryCfgMgr *canaryConfigMgr) rollForward(canaryConfig *fv1.CanaryConfig,
} }
func (canaryCfgMgr *canaryConfigMgr) reSyncCanaryConfigs() { func (canaryCfgMgr *canaryConfigMgr) reSyncCanaryConfigs() {
for _, obj := range canaryCfgMgr.canaryConfigStore.List() { for _, obj := range (*canaryCfgMgr.canaryConfigInformer).GetStore().List() {
canaryConfig := obj.(*fv1.CanaryConfig) canaryConfig := obj.(*fv1.CanaryConfig)
_, err := canaryCfgMgr.canaryCfgCancelFuncMap.lookup(&canaryConfig.ObjectMeta) _, err := canaryCfgMgr.canaryCfgCancelFuncMap.lookup(&canaryConfig.ObjectMeta)
if err != nil && canaryConfig.Status.Status == fv1.CanaryConfigStatusPending { if err != nil && canaryConfig.Status.Status == fv1.CanaryConfigStatusPending {
+1
View File
@@ -19,6 +19,7 @@ package v1
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/fission/fission/pkg/controller/client/rest" "github.com/fission/fission/pkg/controller/client/rest"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+1
View File
@@ -19,6 +19,7 @@ package v1
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/fission/fission/pkg/controller/client/rest" "github.com/fission/fission/pkg/controller/client/rest"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -18,6 +18,7 @@ package fake
import ( import (
"io" "io"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/fission/fission/pkg/controller/client/v1" v1 "github.com/fission/fission/pkg/controller/client/v1"
+1
View File
@@ -19,6 +19,7 @@ package v1
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/fission/fission/pkg/controller/client/rest" "github.com/fission/fission/pkg/controller/client/rest"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+1
View File
@@ -19,6 +19,7 @@ package v1
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/fission/fission/pkg/controller/client/rest" "github.com/fission/fission/pkg/controller/client/rest"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+1
View File
@@ -19,6 +19,7 @@ package v1
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/fission/fission/pkg/controller/client/rest" "github.com/fission/fission/pkg/controller/client/rest"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+1
View File
@@ -19,6 +19,7 @@ package v1
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/fission/fission/pkg/controller/client/rest" "github.com/fission/fission/pkg/controller/client/rest"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+1 -2
View File
@@ -31,8 +31,7 @@ import (
func ConfigCanaryFeature(context context.Context, logger *zap.Logger, fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset, featureConfig *config.FeatureConfig, featureStatus map[string]string) error { func ConfigCanaryFeature(context context.Context, logger *zap.Logger, fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset, featureConfig *config.FeatureConfig, featureStatus map[string]string) error {
// start the appropriate controller // start the appropriate controller
if featureConfig.CanaryConfig.IsEnabled { if featureConfig.CanaryConfig.IsEnabled {
canaryCfgMgr, err := canaryconfigmgr.MakeCanaryConfigMgr(logger, fissionClient, kubeClient, fissionClient.CoreV1().RESTClient(), canaryCfgMgr, err := canaryconfigmgr.MakeCanaryConfigMgr(logger, fissionClient, kubeClient, featureConfig.CanaryConfig.PrometheusSvc)
featureConfig.CanaryConfig.PrometheusSvc)
if err != nil { if err != nil {
featureStatus[config.CanaryFeature] = err.Error() featureStatus[config.CanaryFeature] = err.Error()
return errors.Wrap(err, "failed to start canary config manager") return errors.Wrap(err, "failed to start canary config manager")
+1 -1
View File
@@ -189,7 +189,7 @@ func (a *API) PackageApiGet(w http.ResponseWriter, r *http.Request) {
var resp []byte var resp []byte
if raw != "" { if raw != "" {
resp = []byte(f.Spec.Deployment.Literal) resp = f.Spec.Deployment.Literal
} else { } else {
resp, err = json.Marshal(f) resp, err = json.Marshal(f)
if err != nil { if err != nil {
+1 -1
View File
@@ -31,7 +31,7 @@ import (
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
metricsclient "k8s.io/metrics/pkg/client/clientset/versioned" metricsclient "k8s.io/metrics/pkg/client/clientset/versioned"
genClientset "github.com/fission/fission/pkg/apis/genclient/clientset/versioned" genClientset "github.com/fission/fission/pkg/generated/clientset/versioned"
) )
type ( type (
+4 -3
View File
@@ -19,6 +19,7 @@ package executor
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"html"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
@@ -87,10 +88,10 @@ func (executor *Executor) getServiceForFunctionAPI(w http.ResponseWriter, r *htt
if active >= concurrency { if active >= concurrency {
errMsg := fmt.Sprintf("max concurrency reached for %v. All %v instance are active", fn.ObjectMeta.Name, concurrency) errMsg := fmt.Sprintf("max concurrency reached for %v. All %v instance are active", fn.ObjectMeta.Name, concurrency)
executor.logger.Error("error occurred", zap.String("error", errMsg)) executor.logger.Error("error occurred", zap.String("error", errMsg))
http.Error(w, errMsg, http.StatusTooManyRequests) http.Error(w, html.EscapeString(errMsg), http.StatusTooManyRequests)
return return
} }
} else if t == fv1.ExecutorTypeNewdeploy { } else if t == fv1.ExecutorTypeNewdeploy || t == fv1.ExecutorTypeContainer {
fsvc, err := et.GetFuncSvcFromCache(fn) fsvc, err := et.GetFuncSvcFromCache(fn)
if err == nil { if err == nil {
if et.IsValid(fsvc) { if et.IsValid(fsvc) {
@@ -227,7 +228,7 @@ func (executor *Executor) unTapService(w http.ResponseWriter, r *http.Request) {
t := tapSvcReq.FnExecutorType t := tapSvcReq.FnExecutorType
if t != fv1.ExecutorTypePoolmgr { if t != fv1.ExecutorTypePoolmgr {
msg := fmt.Sprintf("Unknown executor type '%v'", t) msg := fmt.Sprintf("Unknown executor type '%v'", t)
http.Error(w, msg, http.StatusBadRequest) http.Error(w, html.EscapeString(msg), http.StatusBadRequest)
return return
} }
+73
View File
@@ -0,0 +1,73 @@
/*
Copyright 2021 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cms
import (
"context"
"go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/crd"
"github.com/fission/fission/pkg/executor/executortype"
)
func getConfigmapRelatedFuncs(logger *zap.Logger, m *metav1.ObjectMeta, fissionClient *crd.FissionClient) ([]fv1.Function, error) {
funcList, err := fissionClient.CoreV1().Functions(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
// In future a cache that populates at start and is updated on changes might be better solution
relatedFunctions := make([]fv1.Function, 0)
for _, f := range funcList.Items {
for _, cm := range f.Spec.ConfigMaps {
if (cm.Name == m.Name) && (cm.Namespace == m.Namespace) {
relatedFunctions = append(relatedFunctions, f)
break
}
}
}
return relatedFunctions, nil
}
func ConfigMapEventHandlers(logger *zap.Logger, fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, types map[fv1.ExecutorType]executortype.ExecutorType) k8sCache.ResourceEventHandlerFuncs {
return k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
DeleteFunc: func(obj interface{}) {},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
oldCm := oldObj.(*apiv1.ConfigMap)
newCm := newObj.(*apiv1.ConfigMap)
if oldCm.ObjectMeta.ResourceVersion != newCm.ObjectMeta.ResourceVersion {
if newCm.ObjectMeta.Namespace != "kube-system" {
logger.Debug("Configmap changed",
zap.String("configmap_name", newCm.ObjectMeta.Name),
zap.String("configmap_namespace", newCm.ObjectMeta.Namespace))
}
funcs, err := getConfigmapRelatedFuncs(logger, &newCm.ObjectMeta, fissionClient)
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))
}
refreshPods(logger, funcs, types)
}
},
}
}
+14 -115
View File
@@ -17,16 +17,10 @@ limitations under the License.
package cms package cms
import ( import (
"context"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache" k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1" fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/crd" "github.com/fission/fission/pkg/crd"
@@ -38,126 +32,31 @@ type (
ConfigSecretController struct { ConfigSecretController struct {
logger *zap.Logger logger *zap.Logger
configmapController cache.Controller configmapInformer *k8sCache.SharedIndexInformer
secretController cache.Controller secretInformer *k8sCache.SharedIndexInformer
fissionClient *crd.FissionClient fissionClient *crd.FissionClient
} }
) )
//MakeConfigSecretController makes a controller for configmaps and secrets which changes related functions // MakeConfigSecretController makes a controller for configmaps and secrets which changes related functions
func MakeConfigSecretController(logger *zap.Logger, fissionClient *crd.FissionClient, func MakeConfigSecretController(logger *zap.Logger, fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, types map[fv1.ExecutorType]executortype.ExecutorType) *ConfigSecretController { kubernetesClient *kubernetes.Clientset, types map[fv1.ExecutorType]executortype.ExecutorType,
configmapInformer *k8sCache.SharedIndexInformer,
secretInformer *k8sCache.SharedIndexInformer) *ConfigSecretController {
logger.Debug("Creating ConfigMap & Secret Controller") logger.Debug("Creating ConfigMap & Secret Controller")
_, cmcontroller := initConfigmapController(logger, fissionClient, kubernetesClient, types)
_, scontroller := initSecretController(logger, fissionClient, kubernetesClient, types)
cmsController := &ConfigSecretController{ cmsController := &ConfigSecretController{
logger: logger, logger: logger,
configmapController: cmcontroller, configmapInformer: configmapInformer,
secretController: scontroller, secretInformer: secretInformer,
fissionClient: fissionClient, fissionClient: fissionClient,
} }
(*configmapInformer).AddEventHandler(ConfigMapEventHandlers(logger, fissionClient, kubernetesClient, types))
(*secretInformer).AddEventHandler(SecretEventHandlers(logger, fissionClient, kubernetesClient, types))
return cmsController return cmsController
} }
//Run runs the controllers for configmaps and secrets
func (csController *ConfigSecretController) Run(ctx context.Context) {
go csController.configmapController.Run(ctx.Done())
go csController.secretController.Run(ctx.Done())
}
func initConfigmapController(logger *zap.Logger, fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, types map[fv1.ExecutorType]executortype.ExecutorType) (cache.Store, cache.Controller) {
resyncPeriod := 30 * time.Second
listWatch := cache.NewListWatchFromClient(kubernetesClient.CoreV1().RESTClient(), "configmaps", metav1.NamespaceAll, fields.Everything())
store, controller := cache.NewInformer(listWatch, &apiv1.ConfigMap{}, resyncPeriod, cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
DeleteFunc: func(obj interface{}) {},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
oldCm := oldObj.(*apiv1.ConfigMap)
newCm := newObj.(*apiv1.ConfigMap)
if oldCm.ObjectMeta.ResourceVersion != newCm.ObjectMeta.ResourceVersion {
if newCm.ObjectMeta.Namespace != "kube-system" {
logger.Debug("Configmap changed",
zap.String("configmap_name", newCm.ObjectMeta.Name),
zap.String("configmap_namespace", newCm.ObjectMeta.Namespace))
}
funcs, err := getConfigmapRelatedFuncs(logger, &newCm.ObjectMeta, fissionClient)
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))
}
refreshPods(logger, funcs, types)
}
},
})
return store, controller
}
func getConfigmapRelatedFuncs(logger *zap.Logger, m *metav1.ObjectMeta, fissionClient *crd.FissionClient) ([]fv1.Function, error) {
funcList, err := fissionClient.CoreV1().Functions(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
// In future a cache that populates at start and is updated on changes might be better solution
relatedFunctions := make([]fv1.Function, 0)
for _, f := range funcList.Items {
for _, cm := range f.Spec.ConfigMaps {
if (cm.Name == m.Name) && (cm.Namespace == m.Namespace) {
relatedFunctions = append(relatedFunctions, f)
break
}
}
}
return relatedFunctions, nil
}
func initSecretController(logger *zap.Logger, fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, types map[fv1.ExecutorType]executortype.ExecutorType) (cache.Store, cache.Controller) {
resyncPeriod := 30 * time.Second
listWatch := cache.NewListWatchFromClient(kubernetesClient.CoreV1().RESTClient(), "secrets", metav1.NamespaceAll, fields.Everything())
store, controller := cache.NewInformer(listWatch, &apiv1.Secret{}, resyncPeriod, cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
DeleteFunc: func(obj interface{}) {},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
oldS := oldObj.(*apiv1.Secret)
newS := newObj.(*apiv1.Secret)
if oldS.ObjectMeta.ResourceVersion != newS.ObjectMeta.ResourceVersion {
if newS.ObjectMeta.Namespace != "kube-system" {
logger.Debug("Secret changed",
zap.String("configmap_name", newS.ObjectMeta.Name),
zap.String("configmap_namespace", newS.ObjectMeta.Namespace))
}
funcs, err := getSecretRelatedFuncs(logger, &newS.ObjectMeta, 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))
}
refreshPods(logger, funcs, types)
}
},
})
return store, controller
}
func getSecretRelatedFuncs(logger *zap.Logger, m *metav1.ObjectMeta, fissionClient *crd.FissionClient) ([]fv1.Function, error) {
funcList, err := fissionClient.CoreV1().Functions(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
// In future a cache that populates at start and is updated on changes might be better solution
relatedFunctions := make([]fv1.Function, 0)
for _, f := range funcList.Items {
for _, secret := range f.Spec.Secrets {
if (secret.Name == m.Name) && (secret.Namespace == m.Namespace) {
relatedFunctions = append(relatedFunctions, f)
break
}
}
}
return relatedFunctions, nil
}
func refreshPods(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 { for _, f := range funcs {
var err error var err error
+72
View File
@@ -0,0 +1,72 @@
/*
Copyright 2021 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cms
import (
"context"
"go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/crd"
"github.com/fission/fission/pkg/executor/executortype"
)
func getSecretRelatedFuncs(logger *zap.Logger, m *metav1.ObjectMeta, fissionClient *crd.FissionClient) ([]fv1.Function, error) {
funcList, err := fissionClient.CoreV1().Functions(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
// In future a cache that populates at start and is updated on changes might be better solution
relatedFunctions := make([]fv1.Function, 0)
for _, f := range funcList.Items {
for _, secret := range f.Spec.Secrets {
if (secret.Name == m.Name) && (secret.Namespace == m.Namespace) {
relatedFunctions = append(relatedFunctions, f)
break
}
}
}
return relatedFunctions, nil
}
func SecretEventHandlers(logger *zap.Logger, fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, types map[fv1.ExecutorType]executortype.ExecutorType) k8sCache.ResourceEventHandlerFuncs {
return k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
DeleteFunc: func(obj interface{}) {},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
oldS := oldObj.(*apiv1.Secret)
newS := newObj.(*apiv1.Secret)
if oldS.ObjectMeta.ResourceVersion != newS.ObjectMeta.ResourceVersion {
if newS.ObjectMeta.Namespace != "kube-system" {
logger.Debug("Secret changed",
zap.String("configmap_name", newS.ObjectMeta.Name),
zap.String("configmap_namespace", newS.ObjectMeta.Namespace))
}
funcs, err := getSecretRelatedFuncs(logger, &newS.ObjectMeta, 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))
}
refreshPods(logger, funcs, types)
}
},
}
}
+57 -14
View File
@@ -30,17 +30,21 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap" "go.uber.org/zap"
k8sInformers "k8s.io/client-go/informers"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1" fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/crd" "github.com/fission/fission/pkg/crd"
"github.com/fission/fission/pkg/executor/cms" "github.com/fission/fission/pkg/executor/cms"
"github.com/fission/fission/pkg/executor/executortype" "github.com/fission/fission/pkg/executor/executortype"
"github.com/fission/fission/pkg/executor/executortype/container"
"github.com/fission/fission/pkg/executor/executortype/newdeploy" "github.com/fission/fission/pkg/executor/executortype/newdeploy"
"github.com/fission/fission/pkg/executor/executortype/poolmgr" "github.com/fission/fission/pkg/executor/executortype/poolmgr"
"github.com/fission/fission/pkg/executor/fscache" "github.com/fission/fission/pkg/executor/fscache"
"github.com/fission/fission/pkg/executor/reaper" "github.com/fission/fission/pkg/executor/reaper"
"github.com/fission/fission/pkg/executor/util" "github.com/fission/fission/pkg/executor/util"
fetcherConfig "github.com/fission/fission/pkg/fetcher/config" fetcherConfig "github.com/fission/fission/pkg/fetcher/config"
genInformer "github.com/fission/fission/pkg/generated/informers/externalversions"
) )
type ( type (
@@ -54,7 +58,7 @@ type (
fissionClient *crd.FissionClient fissionClient *crd.FissionClient
requestChan chan *createFuncServiceRequest requestChan chan *createFuncServiceRequest
fsCreateWg map[string]*sync.WaitGroup fsCreateWg sync.Map
} }
createFuncServiceRequest struct { createFuncServiceRequest struct {
@@ -69,8 +73,9 @@ type (
) )
// MakeExecutor returns an Executor for given ExecutorType(s). // MakeExecutor returns an Executor for given ExecutorType(s).
func MakeExecutor(logger *zap.Logger, cms *cms.ConfigSecretController, func MakeExecutor(ctx context.Context, logger *zap.Logger, cms *cms.ConfigSecretController,
fissionClient *crd.FissionClient, types map[fv1.ExecutorType]executortype.ExecutorType) (*Executor, error) { fissionClient *crd.FissionClient, types map[fv1.ExecutorType]executortype.ExecutorType,
informers []k8sCache.SharedIndexInformer) (*Executor, error) {
executor := &Executor{ executor := &Executor{
logger: logger.Named("executor"), logger: logger.Named("executor"),
cms: cms, cms: cms,
@@ -78,14 +83,19 @@ func MakeExecutor(logger *zap.Logger, cms *cms.ConfigSecretController,
executorTypes: types, executorTypes: types,
requestChan: make(chan *createFuncServiceRequest), requestChan: make(chan *createFuncServiceRequest),
fsCreateWg: make(map[string]*sync.WaitGroup),
} }
// Run all informers
for _, informer := range informers {
go informer.Run(ctx.Done())
}
for _, et := range types { for _, et := range types {
go func(et executortype.ExecutorType) { go func(et executortype.ExecutorType) {
et.Run(context.Background()) et.Run(ctx)
}(et) }(et)
} }
go cms.Run(context.Background())
go executor.serveCreateFuncServices() go executor.serveCreateFuncServices()
return executor, nil return executor, nil
@@ -128,13 +138,13 @@ func (executor *Executor) serveCreateFuncServices() {
} }
// Cache miss -- is this first one to request the func? // Cache miss -- is this first one to request the func?
wg, found := executor.fsCreateWg[crd.CacheKey(fnMetadata)] wg, found := executor.fsCreateWg.Load(crd.CacheKey(fnMetadata))
if !found { if !found {
// create a waitgroup for other requests for // create a waitgroup for other requests for
// the same function to wait on // the same function to wait on
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
wg.Add(1) wg.Add(1)
executor.fsCreateWg[crd.CacheKey(fnMetadata)] = wg executor.fsCreateWg.Store(crd.CacheKey(fnMetadata), wg)
// launch a goroutine for each request, to parallelize // launch a goroutine for each request, to parallelize
// the specialization of different functions // the specialization of different functions
@@ -166,7 +176,7 @@ func (executor *Executor) serveCreateFuncServices() {
funcSvc: fsvc, funcSvc: fsvc,
err: err, err: err,
} }
delete(executor.fsCreateWg, crd.CacheKey(fnMetadata)) executor.fsCreateWg.Delete(crd.CacheKey(fnMetadata))
wg.Done() wg.Done()
}() }()
} else { } else {
@@ -174,6 +184,14 @@ func (executor *Executor) serveCreateFuncServices() {
go func() { go func() {
executor.logger.Debug("waiting for concurrent request for the same function", executor.logger.Debug("waiting for concurrent request for the same function",
zap.Any("function", fnMetadata)) zap.Any("function", fnMetadata))
wg, ok := wg.(*sync.WaitGroup)
if !ok {
err := fmt.Errorf("could not convert value to workgroup for function %v in namespace %v", fnMetadata.Name, fnMetadata.Namespace)
req.respChan <- &createFuncServiceResponse{
funcSvc: nil,
err: err,
}
}
wg.Wait() wg.Wait()
// get the function service from the cache // get the function service from the cache
@@ -257,25 +275,43 @@ func StartExecutor(logger *zap.Logger, functionNamespace string, envBuilderNames
logger.Info("Starting executor", zap.String("instanceID", executorInstanceID)) logger.Info("Starting executor", zap.String("instanceID", executorInstanceID))
informerFactory := genInformer.NewSharedInformerFactory(fissionClient, time.Second*30)
funcInformer := informerFactory.Core().V1().Functions().Informer()
pkgInformer := informerFactory.Core().V1().Packages().Informer()
envInformer := informerFactory.Core().V1().Environments().Informer()
gpm, err := poolmgr.MakeGenericPoolManager( gpm, err := poolmgr.MakeGenericPoolManager(
logger, logger,
fissionClient, kubernetesClient, metricsClient, fissionClient, kubernetesClient, metricsClient,
functionNamespace, fetcherConfig, executorInstanceID) functionNamespace, fetcherConfig, executorInstanceID,
&funcInformer, &pkgInformer,
)
if err != nil { if err != nil {
return errors.Wrap(err, "pool manager creation faied") return errors.Wrap(err, "pool manager creation faied")
} }
ndm, err := newdeploy.MakeNewDeploy( ndm, err := newdeploy.MakeNewDeploy(
logger, logger,
fissionClient, kubernetesClient, fissionClient.CoreV1().RESTClient(), fissionClient, kubernetesClient,
functionNamespace, fetcherConfig, executorInstanceID) functionNamespace, fetcherConfig, executorInstanceID,
&funcInformer, &envInformer,
)
if err != nil { if err != nil {
return errors.Wrap(err, "new deploy manager creation faied") return errors.Wrap(err, "new deploy manager creation faied")
} }
cnm, err := container.MakeContainer(
logger,
fissionClient, kubernetesClient,
functionNamespace, executorInstanceID, &funcInformer)
if err != nil {
return errors.Wrap(err, "container manager creation faied")
}
executorTypes := make(map[fv1.ExecutorType]executortype.ExecutorType) executorTypes := make(map[fv1.ExecutorType]executortype.ExecutorType)
executorTypes[gpm.GetTypeName()] = gpm executorTypes[gpm.GetTypeName()] = gpm
executorTypes[ndm.GetTypeName()] = ndm executorTypes[ndm.GetTypeName()] = ndm
executorTypes[cnm.GetTypeName()] = cnm
adoptExistingResources, _ := strconv.ParseBool(os.Getenv("ADOPT_EXISTING_RESOURCES")) adoptExistingResources, _ := strconv.ParseBool(os.Getenv("ADOPT_EXISTING_RESOURCES"))
@@ -294,9 +330,16 @@ func StartExecutor(logger *zap.Logger, functionNamespace string, envBuilderNames
// TODO: use context to control the waiting time once kubernetes client supports it. // TODO: use context to control the waiting time once kubernetes client supports it.
util.WaitTimeout(wg, 30*time.Second) util.WaitTimeout(wg, 30*time.Second)
cms := cms.MakeConfigSecretController(logger, fissionClient, kubernetesClient, executorTypes) k8sInformerFactory := k8sInformers.NewSharedInformerFactory(kubernetesClient, time.Second*30)
configmapInformer := k8sInformerFactory.Core().V1().ConfigMaps().Informer()
secretInformer := k8sInformerFactory.Core().V1().Secrets().Informer()
api, err := MakeExecutor(logger, cms, fissionClient, executorTypes) cms := cms.MakeConfigSecretController(logger, fissionClient, kubernetesClient, executorTypes, &configmapInformer, &secretInformer)
ctx := context.Background()
api, err := MakeExecutor(ctx, logger, cms, fissionClient, executorTypes, []k8sCache.SharedIndexInformer{
funcInformer, pkgInformer, envInformer, configmapInformer, secretInformer,
})
if err != nil { if err != nil {
return err return err
} }
@@ -0,0 +1,153 @@
/*
Copyright 2020 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package container
import (
"context"
"strconv"
multierror "github.com/hashicorp/go-multierror"
"go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
k8s_err "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
)
// getResources gets the resources(CPU, memory) set for the function
func (cn *Container) getResources(fn *fv1.Function) apiv1.ResourceRequirements {
resources := fn.Spec.Resources
if resources.Requests == nil {
resources.Requests = make(map[apiv1.ResourceName]resource.Quantity)
}
if resources.Limits == nil {
resources.Limits = make(map[apiv1.ResourceName]resource.Quantity)
}
val, ok := fn.Spec.Resources.Requests[apiv1.ResourceCPU]
if ok && !val.IsZero() {
resources.Requests[apiv1.ResourceCPU] = fn.Spec.Resources.Requests[apiv1.ResourceCPU]
}
val, ok = fn.Spec.Resources.Requests[apiv1.ResourceMemory]
if ok && !val.IsZero() {
resources.Requests[apiv1.ResourceMemory] = fn.Spec.Resources.Requests[apiv1.ResourceMemory]
}
val, ok = fn.Spec.Resources.Limits[apiv1.ResourceCPU]
if ok && !val.IsZero() {
resources.Limits[apiv1.ResourceCPU] = fn.Spec.Resources.Limits[apiv1.ResourceCPU]
}
val, ok = fn.Spec.Resources.Limits[apiv1.ResourceMemory]
if ok && !val.IsZero() {
resources.Limits[apiv1.ResourceMemory] = fn.Spec.Resources.Limits[apiv1.ResourceMemory]
}
return resources
}
// cleanupContainer cleans all kubernetes objects related to function
func (cn *Container) cleanupContainer(ns string, name string) error {
result := &multierror.Error{}
err := cn.deleteSvc(ns, name)
if err != nil && !k8s_err.IsNotFound(err) {
cn.logger.Error("error deleting service for Container function",
zap.Error(err),
zap.String("function_name", name),
zap.String("function_namespace", ns))
result = multierror.Append(result, err)
}
err = cn.deleteHpa(ns, name)
if err != nil && !k8s_err.IsNotFound(err) {
cn.logger.Error("error deleting HPA for Container function",
zap.Error(err),
zap.String("function_name", name),
zap.String("function_namespace", ns))
result = multierror.Append(result, err)
}
err = cn.deleteDeployment(ns, name)
if err != nil && !k8s_err.IsNotFound(err) {
cn.logger.Error("error deleting deployment for Container function",
zap.Error(err),
zap.String("function_name", name),
zap.String("function_namespace", ns))
result = multierror.Append(result, err)
}
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(context.TODO(), 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(context.TODO(), 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
}
@@ -0,0 +1,796 @@
/*
Copyright 2020 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package container
import (
"context"
"fmt"
"os"
"reflect"
"strconv"
"strings"
"sync"
"time"
multierror "github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
k8sErrs "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
k8sTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"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"
"github.com/fission/fission/pkg/throttler"
"github.com/fission/fission/pkg/utils"
"github.com/fission/fission/pkg/utils/maps"
)
var _ executortype.ExecutorType = &Container{}
type (
// Container represents an executor type
Container struct {
logger *zap.Logger
kubernetesClient *kubernetes.Clientset
fissionClient *crd.FissionClient
instanceID string
// fetcherConfig *fetcherConfig.Config
runtimeImagePullPolicy apiv1.PullPolicy
namespace string
useIstio bool
fsCache *fscache.FunctionServiceCache // cache funcSvc's by function, address and pod name
throttler *throttler.Throttler
funcInformer *k8sCache.SharedIndexInformer
serviceInformer k8sCache.SharedIndexInformer
deploymentInformer k8sCache.SharedIndexInformer
defaultIdlePodReapTime time.Duration
}
)
// MakeContainer initializes and returns an instance of CaaF
func MakeContainer(
logger *zap.Logger,
fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset,
namespace string,
instanceID string,
funcInformer *k8sCache.SharedIndexInformer) (executortype.ExecutorType, error) {
enableIstio := false
if len(os.Getenv("ENABLE_ISTIO")) > 0 {
istio, err := strconv.ParseBool(os.Getenv("ENABLE_ISTIO"))
if err != nil {
logger.Error("failed to parse 'ENABLE_ISTIO', set to false", zap.Error(err))
}
enableIstio = istio
}
caaf := &Container{
logger: logger.Named("CaaF"),
fissionClient: fissionClient,
kubernetesClient: kubernetesClient,
instanceID: instanceID,
namespace: namespace,
fsCache: fscache.MakeFunctionServiceCache(logger),
throttler: throttler.MakeThrottler(1 * time.Minute),
funcInformer: funcInformer,
runtimeImagePullPolicy: utils.GetImagePullPolicy(os.Getenv("RUNTIME_IMAGE_PULL_POLICY")),
useIstio: enableIstio,
// Time is set slightly higher than NewDeploy as cold starts are longer for CaaF
defaultIdlePodReapTime: 1 * time.Minute,
}
(*caaf.funcInformer).AddEventHandler(caaf.FuncInformerHandler())
informerFactory, err := utils.GetInformerFactoryByExecutor(caaf.kubernetesClient, fv1.ExecutorTypeContainer)
if err != nil {
return nil, err
}
caaf.serviceInformer = informerFactory.Core().V1().Services().Informer()
caaf.deploymentInformer = informerFactory.Apps().V1().Deployments().Informer()
return caaf, nil
}
// Run start the function along with an object reaper.
func (caaf *Container) Run(ctx context.Context) {
go caaf.idleObjectReaper()
}
// GetTypeName returns the executor type name.
func (caaf *Container) GetTypeName() fv1.ExecutorType {
return fv1.ExecutorTypeContainer
}
// GetTotalAvailable has not been implemented for CaaF.
func (caaf *Container) GetTotalAvailable(fn *fv1.Function) int {
// Not Implemented for CaaF.
return 0
}
// UnTapService has not been implemented for CaaF.
func (caaf *Container) UnTapService(key string, svcHost string) {
// Not Implemented for CaaF.
}
// GetFuncSvc returns a function service; error otherwise.
func (caaf *Container) GetFuncSvc(ctx context.Context, fn *fv1.Function) (*fscache.FuncSvc, error) {
return caaf.createFunction(fn)
}
// GetFuncSvcFromCache returns a function service from cache; error otherwise.
func (caaf *Container) GetFuncSvcFromCache(fn *fv1.Function) (*fscache.FuncSvc, error) {
return caaf.fsCache.GetByFunction(&fn.ObjectMeta)
}
// DeleteFuncSvcFromCache deletes a function service from cache.
func (caaf *Container) DeleteFuncSvcFromCache(fsvc *fscache.FuncSvc) {
caaf.fsCache.DeleteEntry(fsvc)
}
// GetFuncSvcFromPoolCache has not been implemented for Container Functions
func (caaf *Container) GetFuncSvcFromPoolCache(fn *fv1.Function, requestsPerPod int) (*fscache.FuncSvc, int, error) {
// Not Implemented for NewDeployment. Will be used when support of concurrent specialization of same function is added.
return nil, 0, nil
}
// TapService makes a TouchByAddress request to the cache.
func (caaf *Container) TapService(svcHost string) error {
err := caaf.fsCache.TouchByAddress(svcHost)
if err != nil {
return err
}
return nil
}
func (caaf *Container) getServiceInfo(obj apiv1.ObjectReference) (*apiv1.Service, error) {
item, exists, err := utils.GetCachedItem(obj, caaf.serviceInformer)
if err != nil || !exists {
caaf.logger.Debug(
"Falling back to getting service info from k8s API -- this may cause performance issues for your function.",
zap.Bool("exists", exists),
zap.Error(err),
)
service, err := caaf.kubernetesClient.CoreV1().Services(obj.Namespace).Get(context.TODO(), obj.Name, metav1.GetOptions{})
return service, err
}
service := item.(*apiv1.Service)
return service, nil
}
func (caaf *Container) getDeploymentInfo(obj apiv1.ObjectReference) (*appsv1.Deployment, error) {
item, exists, err := utils.GetCachedItem(obj, caaf.deploymentInformer)
if err != nil || !exists {
caaf.logger.Debug(
"Falling back to getting deployment info from k8s API -- this may cause performance issues for your function.",
zap.Bool("exists", exists),
zap.Error(err),
)
deployment, err := caaf.kubernetesClient.AppsV1().Deployments(obj.Namespace).Get(context.TODO(), obj.Name, metav1.GetOptions{})
return deployment, err
}
deployment := item.(*appsv1.Deployment)
return deployment, nil
}
// IsValid does a get on the service address to ensure it's a valid service, then
// scale deployment to 1 replica if there are no available replicas for function.
// Return true if no error occurs, return false otherwise.
func (caaf *Container) IsValid(fsvc *fscache.FuncSvc) bool {
if len(strings.Split(fsvc.Address, ".")) == 0 {
caaf.logger.Error("address not found in function service")
return false
}
if len(fsvc.KubernetesObjects) == 0 {
caaf.logger.Error("no kubernetes object related to function", zap.String("function", fsvc.Function.Name))
return false
}
for _, obj := range fsvc.KubernetesObjects {
if strings.ToLower(obj.Kind) == "service" {
_, err := caaf.getServiceInfo(obj)
if err != nil {
if !k8sErrs.IsNotFound(err) {
caaf.logger.Error("error validating function service", zap.String("function", fsvc.Function.Name), zap.Error(err))
}
return false
}
} else if strings.ToLower(obj.Kind) == "deployment" {
currentDeploy, err := caaf.getDeploymentInfo(obj)
if err != nil {
if !k8sErrs.IsNotFound(err) {
caaf.logger.Error("error validating function deployment", zap.String("function", fsvc.Function.Name), zap.Error(err))
}
return false
}
if currentDeploy.Status.AvailableReplicas < 1 {
return false
}
}
}
return true
}
// RefreshFuncPods deletes pods related to the function so that new pods are replenished
func (caaf *Container) RefreshFuncPods(logger *zap.Logger, f fv1.Function) error {
funcLabels := caaf.getDeployLabels(f.ObjectMeta)
dep, err := caaf.kubernetesClient.AppsV1().Deployments(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{
LabelSelector: labels.Set(funcLabels).AsSelector().String(),
})
if err != nil {
return err
}
// Ideally there should be only one deployment but for now we rely on label/selector to ensure that condition
for _, deployment := range dep.Items {
rvCount, err := referencedResourcesRVSum(caaf.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.ObjectMeta.Name, fv1.ResourceVersionCount, rvCount)
_, err = caaf.kubernetesClient.AppsV1().Deployments(deployment.ObjectMeta.Namespace).Patch(context.TODO(), deployment.ObjectMeta.Name,
k8sTypes.StrategicMergePatchType,
[]byte(patch), metav1.PatchOptions{})
if err != nil {
return err
}
}
return nil
}
// AdoptExistingResources attempts to adopt resources for functions in all namespaces.
func (caaf *Container) AdoptExistingResources() {
fnList, err := caaf.fissionClient.CoreV1().Functions(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil {
caaf.logger.Error("error getting function list", zap.Error(err))
return
}
wg := &sync.WaitGroup{}
for i := range fnList.Items {
fn := &fnList.Items[i]
if fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fv1.ExecutorTypeContainer {
wg.Add(1)
go func() {
defer wg.Done()
_, err = caaf.fnCreate(fn)
if err != nil {
caaf.logger.Warn("failed to adopt resources for function", zap.Error(err))
return
}
caaf.logger.Info("adopt resources for function", zap.String("function", fn.ObjectMeta.Name))
}()
}
}
wg.Wait()
}
// CleanupOldExecutorObjects cleans orphaned resources.
func (caaf *Container) CleanupOldExecutorObjects() {
caaf.logger.Info("CaaF starts to clean orphaned resources", zap.String("instanceID", caaf.instanceID))
errs := &multierror.Error{}
listOpts := metav1.ListOptions{
LabelSelector: labels.Set(map[string]string{fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeContainer)}).AsSelector().String(),
}
err := reaper.CleanupHpa(caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts)
if err != nil {
errs = multierror.Append(errs, err)
}
err = reaper.CleanupDeployments(caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts)
if err != nil {
errs = multierror.Append(errs, err)
}
err = reaper.CleanupServices(caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts)
if err != nil {
errs = multierror.Append(errs, err)
}
if errs.ErrorOrNil() != nil {
// TODO retry reaper; logged and ignored for now
caaf.logger.Error("Failed to cleanup old executor objects", zap.Error(err))
}
}
func (caaf *Container) createFunction(fn *fv1.Function) (*fscache.FuncSvc, error) {
if fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypeContainer {
return nil, nil
}
fsvcObj, err := caaf.throttler.RunOnce(string(fn.ObjectMeta.UID), func(ableToCreate bool) (interface{}, error) {
if ableToCreate {
return caaf.fnCreate(fn)
}
return caaf.fsCache.GetByFunctionUID(fn.ObjectMeta.UID)
})
if err != nil {
e := "error creating k8s resources for function"
caaf.logger.Error(e,
zap.Error(err),
zap.String("function_name", fn.ObjectMeta.Name),
zap.String("function_namespace", fn.ObjectMeta.Namespace))
return nil, errors.Wrapf(err, "%s %s_%s", e, fn.ObjectMeta.Name, fn.ObjectMeta.Namespace)
}
fsvc, ok := fsvcObj.(*fscache.FuncSvc)
if !ok {
caaf.logger.Panic("receive unknown object while creating function - expected pointer of function service object")
}
return fsvc, err
}
func (caaf *Container) deleteFunction(fn *fv1.Function) error {
if fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypeContainer {
return nil
}
err := caaf.fnDelete(fn)
if err != nil {
err = errors.Wrapf(err, "error deleting kubernetes objects of function %v", fn.ObjectMeta)
}
return err
}
func (caaf *Container) fnCreate(fn *fv1.Function) (*fscache.FuncSvc, error) {
cleanupFunc := func(ns string, name string) {
err := caaf.cleanupContainer(ns, name)
if err != nil {
caaf.logger.Error("received error while cleaning function resources",
zap.String("namespace", ns), zap.String("name", name))
}
}
objName := caaf.getObjName(fn)
deployLabels := caaf.getDeployLabels(fn.ObjectMeta)
deployAnnotations := caaf.getDeployAnnotations(fn.ObjectMeta)
// to support backward compatibility, if the function was created in default ns, we fall back to creating the
// deployment of the function in fission-function ns
ns := caaf.namespace
if fn.ObjectMeta.Namespace != metav1.NamespaceDefault {
ns = fn.ObjectMeta.Namespace
}
// Envoy(istio-proxy) returns 404 directly before istio pilot
// propagates latest Envoy-specific configuration.
// Since Container waits for pods of deployment to be ready,
// change the order of kubeObject creation (create service first,
// then deployment) to take advantage of waiting time.
svc, err := caaf.createOrGetSvc(fn, deployLabels, deployAnnotations, objName, ns)
if err != nil {
caaf.logger.Error("error creating service", zap.Error(err), zap.String("service", objName))
go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating service %v", objName)
}
svcAddress := fmt.Sprintf("%v.%v", svc.Name, svc.Namespace)
depl, err := caaf.createOrGetDeployment(fn, objName, deployLabels, deployAnnotations, ns)
if err != nil {
caaf.logger.Error("error creating deployment", zap.Error(err), zap.String("deployment", objName))
go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating deployment %v", objName)
}
hpa, err := caaf.createOrGetHpa(objName, &fn.Spec.InvokeStrategy.ExecutionStrategy, depl, deployLabels, deployAnnotations)
if err != nil {
caaf.logger.Error("error creating HPA", zap.Error(err), zap.String("hpa", objName))
go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating the HPA %v", objName)
}
kubeObjRefs := []apiv1.ObjectReference{
{
//obj.TypeMeta.Kind does not work hence this, needs investigation and a fix
Kind: "deployment",
Name: depl.ObjectMeta.Name,
APIVersion: depl.TypeMeta.APIVersion,
Namespace: depl.ObjectMeta.Namespace,
ResourceVersion: depl.ObjectMeta.ResourceVersion,
UID: depl.ObjectMeta.UID,
},
{
Kind: "service",
Name: svc.ObjectMeta.Name,
APIVersion: svc.TypeMeta.APIVersion,
Namespace: svc.ObjectMeta.Namespace,
ResourceVersion: svc.ObjectMeta.ResourceVersion,
UID: svc.ObjectMeta.UID,
},
{
Kind: "horizontalpodautoscaler",
Name: hpa.ObjectMeta.Name,
APIVersion: hpa.TypeMeta.APIVersion,
Namespace: hpa.ObjectMeta.Namespace,
ResourceVersion: hpa.ObjectMeta.ResourceVersion,
UID: hpa.ObjectMeta.UID,
},
}
fsvc := &fscache.FuncSvc{
Name: objName,
Function: &fn.ObjectMeta,
Address: svcAddress,
KubernetesObjects: kubeObjRefs,
Executor: fv1.ExecutorTypeContainer,
}
_, err = caaf.fsCache.Add(*fsvc)
if err != nil {
caaf.logger.Error("error adding function to cache", zap.Error(err), zap.Any("function", fsvc.Function))
return fsvc, err
}
caaf.fsCache.IncreaseColdStarts(fn.ObjectMeta.Name, string(fn.ObjectMeta.UID))
return fsvc, nil
}
func (caaf *Container) updateFunction(oldFn *fv1.Function, newFn *fv1.Function) error {
if oldFn.ObjectMeta.ResourceVersion == newFn.ObjectMeta.ResourceVersion {
return nil
}
// Ignoring updates to functions which are not of Container type
if newFn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypeContainer &&
oldFn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypeContainer {
return nil
}
// Executor type is no longer Container
if newFn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypeContainer &&
oldFn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fv1.ExecutorTypeContainer {
caaf.logger.Info("function does not use new deployment executor anymore, deleting resources",
zap.Any("function", newFn))
// IMP - pass the oldFn, as the new/modified function is not in cache
return caaf.deleteFunction(oldFn)
}
// Executor type changed to Container from something else
if oldFn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypeContainer &&
newFn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fv1.ExecutorTypeContainer {
caaf.logger.Info("function type changed to Container, creating resources",
zap.Any("old_function", oldFn.ObjectMeta),
zap.Any("new_function", newFn.ObjectMeta))
_, err := caaf.createFunction(newFn)
if err != nil {
caaf.updateStatus(oldFn, err, "error changing the function's type to Container")
}
return err
}
if oldFn.Spec.InvokeStrategy != newFn.Spec.InvokeStrategy {
// to support backward compatibility, if the function was created in default ns, we fall back to creating the
// deployment of the function in fission-function ns, so cleaning up resources there
ns := caaf.namespace
if newFn.ObjectMeta.Namespace != metav1.NamespaceDefault {
ns = newFn.ObjectMeta.Namespace
}
fsvc, err := caaf.fsCache.GetByFunctionUID(newFn.ObjectMeta.UID)
if err != nil {
err = errors.Wrapf(err, "error updating function due to unable to find function service cache: %v", oldFn)
return err
}
hpa, err := caaf.getHpa(ns, fsvc.Name)
if err != nil {
caaf.updateStatus(oldFn, err, "error getting HPA while updating function")
return err
}
hpaChanged := false
if newFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale != oldFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale {
replicas := int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.MinScale)
hpa.Spec.MinReplicas = &replicas
hpaChanged = true
}
if newFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale != oldFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale {
hpa.Spec.MaxReplicas = int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.MaxScale)
hpaChanged = true
}
if newFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent != oldFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent {
targetCpupercent := int32(newFn.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent)
hpa.Spec.TargetCPUUtilizationPercentage = &targetCpupercent
hpaChanged = true
}
if hpaChanged {
err := caaf.updateHpa(hpa)
if err != nil {
caaf.updateStatus(oldFn, err, "error updating HPA while updating function")
return err
}
}
}
deployChanged := false
// If length of slice has changed then no need to check individual elements
if len(oldFn.Spec.Secrets) != len(newFn.Spec.Secrets) {
deployChanged = true
} else {
for i, newSecret := range newFn.Spec.Secrets {
if newSecret != oldFn.Spec.Secrets[i] {
deployChanged = true
break
}
}
}
if len(oldFn.Spec.ConfigMaps) != len(newFn.Spec.ConfigMaps) {
deployChanged = true
} else {
for i, newConfig := range newFn.Spec.ConfigMaps {
if newConfig != oldFn.Spec.ConfigMaps[i] {
deployChanged = true
break
}
}
}
if !reflect.DeepEqual(oldFn.Spec.PodSpec, newFn.Spec.PodSpec) {
deployChanged = true
}
if deployChanged {
return caaf.updateFuncDeployment(newFn)
}
return nil
}
func (caaf *Container) updateFuncDeployment(fn *fv1.Function) error {
fsvc, err := caaf.fsCache.GetByFunctionUID(fn.ObjectMeta.UID)
if err != nil {
err = errors.Wrapf(err, "error updating function due to unable to find function service cache: %v", fn)
return err
}
fnObjName := fsvc.Name
deployLabels := caaf.getDeployLabels(fn.ObjectMeta)
caaf.logger.Info("updating deployment due to function update",
zap.String("deployment", fnObjName), zap.Any("function", fn.ObjectMeta.Name))
// to support backward compatibility, if the function was created in default ns, we fall back to creating the
// deployment of the function in fission-function ns
ns := caaf.namespace
if fn.ObjectMeta.Namespace != metav1.NamespaceDefault {
ns = fn.ObjectMeta.Namespace
}
existingDepl, err := caaf.kubernetesClient.AppsV1().Deployments(ns).Get(context.TODO(), 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 := caaf.getDeploymentSpec(fn, existingDepl.Spec.Replicas, // use current replicas instead of minscale in the ExecutionStrategy.
fnObjName, ns, deployLabels, caaf.getDeployAnnotations(fn.ObjectMeta))
if err != nil {
caaf.updateStatus(fn, err, "failed to get new deployment spec while updating function")
return err
}
err = caaf.updateDeployment(newDeployment, ns)
if err != nil {
caaf.updateStatus(fn, err, "failed to update deployment while updating function")
return err
}
return nil
}
func (caaf *Container) fnDelete(fn *fv1.Function) error {
multierr := &multierror.Error{}
// GetByFunction uses resource version as part of cache key, however,
// the resource version in function metadata will be changed when a function
// is deleted and cause Container backend fails to delete the entry.
// Use GetByFunctionUID instead of GetByFunction here to find correct
// fsvc entry.
fsvc, err := caaf.fsCache.GetByFunctionUID(fn.ObjectMeta.UID)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("fsvc not found in cache: %v", fn.ObjectMeta))
return err
}
objName := fsvc.Name
_, err = caaf.fsCache.DeleteOld(fsvc, time.Second*0)
if err != nil {
multierr = multierror.Append(multierr,
errors.Wrapf(err, "error deleting the function from cache"))
}
// to support backward compatibility, if the function was created in default ns, we fall back to creating the
// deployment of the function in fission-function ns, so cleaning up resources there
ns := caaf.namespace
if fn.ObjectMeta.Namespace != metav1.NamespaceDefault {
ns = fn.ObjectMeta.Namespace
}
err = caaf.cleanupContainer(ns, objName)
multierr = multierror.Append(multierr, err)
return multierr.ErrorOrNil()
}
// getObjName returns a unique name for kubernetes objects of function
func (caaf *Container) getObjName(fn *fv1.Function) string {
// use meta uuid of function, this ensure we always get the same name for the same function.
uid := fn.ObjectMeta.UID[len(fn.ObjectMeta.UID)-17:]
var functionMetadata string
if len(fn.ObjectMeta.Name)+len(fn.ObjectMeta.Namespace) < 35 {
functionMetadata = fn.ObjectMeta.Name + "-" + fn.ObjectMeta.Namespace
} else {
if len(fn.ObjectMeta.Name) > 17 {
functionMetadata = fn.ObjectMeta.Name[:17]
} else {
functionMetadata = fn.ObjectMeta.Name
}
if len(fn.ObjectMeta.Namespace) > 17 {
functionMetadata = functionMetadata + "-" + fn.ObjectMeta.Namespace[:17]
} else {
functionMetadata = functionMetadata + "-" + fn.ObjectMeta.Namespace
}
}
// contructed name should be 63 characters long, as it is a valid k8s name
// functionMetadata should be 35 characters long, as we take 17 characters from functionUid
// with newdeploy 10 character prefix
return strings.ToLower(fmt.Sprintf("container-%s-%s", functionMetadata, uid))
}
func (caaf *Container) getDeployLabels(fnMeta metav1.ObjectMeta) map[string]string {
deployLabels := maps.CopyStringMap(fnMeta.Labels)
deployLabels[fv1.EXECUTOR_TYPE] = string(fv1.ExecutorTypeContainer)
deployLabels[fv1.FUNCTION_NAME] = fnMeta.Name
deployLabels[fv1.FUNCTION_NAMESPACE] = fnMeta.Namespace
deployLabels[fv1.FUNCTION_UID] = string(fnMeta.UID)
return deployLabels
}
func (caaf *Container) getDeployAnnotations(fnMeta metav1.ObjectMeta) map[string]string {
deployAnnotations := maps.CopyStringMap(fnMeta.Annotations)
deployAnnotations[fv1.EXECUTOR_INSTANCEID_LABEL] = caaf.instanceID
deployAnnotations[fv1.FUNCTION_RESOURCE_VERSION] = fnMeta.ResourceVersion
return deployAnnotations
}
// updateStatus is a function which updates status of update.
// Current implementation only logs messages, in future it will update function status
func (caaf *Container) updateStatus(fn *fv1.Function, err error, message string) {
caaf.logger.Error("function status update", zap.Error(err), zap.Any("function", fn), zap.String("message", message))
}
// idleObjectReaper reaps objects after certain idle time
func (caaf *Container) idleObjectReaper() {
pollSleep := 5 * time.Second
for {
time.Sleep(pollSleep)
funcSvcs, err := caaf.fsCache.ListOld(pollSleep)
if err != nil {
caaf.logger.Error("error reaping idle pods", zap.Error(err))
continue
}
for i := range funcSvcs {
fsvc := funcSvcs[i]
if fsvc.Executor != fv1.ExecutorTypeContainer {
continue
}
fn, err := caaf.fissionClient.CoreV1().Functions(fsvc.Function.Namespace).Get(context.TODO(), fsvc.Function.Name, metav1.GetOptions{})
if err != nil {
// CaaF manager handles the function delete event and clean cache/kubeobjs itself,
// so we ignore the not found error for functions with CaaF executor type here.
if k8sErrs.IsNotFound(err) && fsvc.Executor == fv1.ExecutorTypeContainer {
continue
}
caaf.logger.Error("error getting function", zap.Error(err), zap.String("function", fsvc.Function.Name))
continue
}
idlePodReapTime := caaf.defaultIdlePodReapTime
if fn.Spec.IdleTimeout != nil {
idlePodReapTime = time.Duration(*fn.Spec.IdleTimeout) * time.Second
}
if time.Since(fsvc.Atime) < idlePodReapTime {
continue
}
go func() {
deployObj := getDeploymentObj(fsvc.KubernetesObjects)
if deployObj == nil {
caaf.logger.Error("error finding function deployment", zap.Error(err), zap.String("function", fsvc.Function.Name))
return
}
currentDeploy, err := caaf.kubernetesClient.AppsV1().
Deployments(deployObj.Namespace).Get(context.TODO(), deployObj.Name, metav1.GetOptions{})
if err != nil {
caaf.logger.Error("error getting function deployment", zap.Error(err), zap.String("function", fsvc.Function.Name))
return
}
minScale := int32(fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale)
// do nothing if the current replicas is already lower than minScale
if *currentDeploy.Spec.Replicas <= minScale {
return
}
err = caaf.scaleDeployment(deployObj.Namespace, deployObj.Name, minScale)
if err != nil {
caaf.logger.Error("error scaling down function deployment", zap.Error(err), zap.String("function", fsvc.Function.Name))
}
}()
}
}
}
func getDeploymentObj(kubeobjs []apiv1.ObjectReference) *apiv1.ObjectReference {
for _, kubeobj := range kubeobjs {
switch strings.ToLower(kubeobj.Kind) {
case "deployment":
return &kubeobj
}
}
return nil
}
@@ -0,0 +1,295 @@
/*
Copyright 2020 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package container
import (
"context"
"fmt"
"time"
"go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
apiv1 "k8s.io/api/core/v1"
k8s_err "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/executor/util"
)
func (cn *Container) createOrGetDeployment(fn *fv1.Function, deployName string, deployLabels map[string]string, deployAnnotations map[string]string, deployNamespace string) (*appsv1.Deployment, error) {
// The specializationTimeout here refers to the creation of the pod and not the loading of function
// as in other executors.
specializationTimeout := fn.Spec.InvokeStrategy.ExecutionStrategy.SpecializationTimeout
minScale := int32(fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale)
// 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
}
deployment, err := cn.getDeploymentSpec(fn, &minScale, deployName, deployNamespace, deployLabels, deployAnnotations)
if err != nil {
return nil, err
}
existingDepl, err := cn.kubernetesClient.AppsV1().Deployments(deployNamespace).Get(context.TODO(), deployName, metav1.GetOptions{})
if err == nil {
// Try to adopt orphan deployment created by the old executor.
if existingDepl.Annotations[fv1.EXECUTOR_INSTANCEID_LABEL] != cn.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
// Update with the latest deployment spec. Kubernetes will trigger
// rolling update if spec is different from the one in the cluster.
existingDepl, err = cn.kubernetesClient.AppsV1().Deployments(deployNamespace).Update(context.TODO(), existingDepl, metav1.UpdateOptions{})
if err != nil {
cn.logger.Warn("error adopting cn", zap.Error(err),
zap.String("cn", 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 *existingDepl.Spec.Replicas < minScale {
err = cn.scaleDeployment(existingDepl.Namespace, existingDepl.Name, minScale)
if err != nil {
cn.logger.Error("error scaling up function deployment", zap.Error(err), zap.String("function", fn.ObjectMeta.Name))
return nil, err
}
}
if existingDepl.Status.AvailableReplicas < minScale {
existingDepl, err = cn.waitForDeploy(existingDepl, minScale, specializationTimeout)
}
return existingDepl, err
} else if k8s_err.IsNotFound(err) {
depl, err := cn.kubernetesClient.AppsV1().Deployments(deployNamespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
if err != nil {
if k8s_err.IsAlreadyExists(err) {
depl, err = cn.kubernetesClient.AppsV1().Deployments(deployNamespace).Get(context.TODO(), deployName, metav1.GetOptions{})
}
if err != nil {
cn.logger.Error("error while creating function deployment",
zap.Error(err),
zap.String("function", fn.ObjectMeta.Name),
zap.String("deployment_name", deployName),
zap.String("deployment_namespace", deployNamespace))
return nil, err
}
}
if minScale > 0 {
depl, err = cn.waitForDeploy(depl, minScale, specializationTimeout)
}
return depl, err
}
return nil, err
}
func (cn *Container) updateDeployment(deployment *appsv1.Deployment, ns string) error {
_, err := cn.kubernetesClient.AppsV1().Deployments(ns).Update(context.TODO(), deployment, metav1.UpdateOptions{})
return err
}
func (cn *Container) deleteDeployment(ns string, name string) error {
// DeletePropagationBackground deletes the object immediately and dependent are deleted later
// DeletePropagationForeground not advisable; it marks for deleteion and API can still serve those objects
deletePropagation := metav1.DeletePropagationBackground
return cn.kubernetesClient.AppsV1().Deployments(ns).Delete(context.TODO(), name, metav1.DeleteOptions{
PropagationPolicy: &deletePropagation,
})
}
func (cn *Container) waitForDeploy(depl *appsv1.Deployment, replicas int32, specializationTimeout int) (latestDepl *appsv1.Deployment, err error) {
oldStatus := depl.Status
// if no specializationTimeout is set, use default value
if specializationTimeout < fv1.DefaultSpecializationTimeOut {
specializationTimeout = fv1.DefaultSpecializationTimeOut
}
for i := 0; i < specializationTimeout; i++ {
latestDepl, err := cn.kubernetesClient.AppsV1().Deployments(depl.ObjectMeta.Namespace).Get(context.TODO(), depl.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
// TODO check for imagePullerror
// use AvailableReplicas here is better than ReadyReplicas
// since the pods may not be able to serve network traffic yet.
if latestDepl.Status.AvailableReplicas >= replicas {
return latestDepl, err
}
time.Sleep(time.Second)
}
cn.logger.Error("Deployment provision failed within timeout window",
zap.String("name", latestDepl.ObjectMeta.Name), zap.Any("old_status", oldStatus),
zap.Any("current_status", latestDepl.Status), zap.Int("timeout", specializationTimeout))
// this error appears in the executor pod logs
timeoutError := fmt.Errorf("failed to create deployment within the timeout window of %d seconds", specializationTimeout)
return nil, timeoutError
}
func (cn *Container) getDeploymentSpec(fn *fv1.Function, 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)
podAnnotations := make(map[string]string)
if cn.useIstio {
podAnnotations["sidecar.istio.io/inject"] = "false"
}
podLabels := make(map[string]string)
for k, v := range deployLabels {
podLabels[k] = v
}
// Set maxUnavailable and maxSurge to 20% is because we want
// fission to rollout newer function version gradually without
// affecting any online service. For example, if you set maxSurge
// to 100%, the new ReplicaSet scales up immediately and may
// consume all remaining compute resources which might be an
// issue if a cluster's resource is on a budget.
// TODO: add to ExecutionStrategy so that the user
// can do more fine control over different functions.
maxUnavailable := intstr.FromString("20%")
maxSurge := intstr.FromString("20%")
// Container updates the environment variable "LastUpdateTimestamp" of deployment
// whenever a configmap/secret gets an update, but it also leaves multiple ReplicaSets for
// rollback purpose. Since fission always update a deployment instead of performing a
// rollback, set RevisionHistoryLimit to 0 to disable this feature.
revisionHistoryLimit := int32(0)
resources := cn.getResources(fn)
// Other executor types rely on Environments to add configmaps and secrets
envFromSources, err := util.ConvertConfigSecrets(fn, cn.kubernetesClient)
if err != nil {
return nil, err
}
rvCount, err := referencedResourcesRVSum(cn.kubernetesClient, fn.ObjectMeta.Namespace, fn.Spec.Secrets, fn.Spec.ConfigMaps)
if err != nil {
return nil, err
}
if fn.Spec.PodSpec == nil {
return nil, fmt.Errorf("podSpec is not set for function %s", fn.ObjectMeta.Name)
}
container := &apiv1.Container{
Name: fn.ObjectMeta.Name,
ImagePullPolicy: cn.runtimeImagePullPolicy,
TerminationMessagePath: "/dev/termination-log",
Lifecycle: &apiv1.Lifecycle{
PreStop: &apiv1.Handler{
Exec: &apiv1.ExecAction{
Command: []string{
"/bin/sleep",
fmt.Sprintf("%v", gracePeriodSeconds),
},
},
},
},
Env: []apiv1.EnvVar{
{
Name: fv1.ResourceVersionCount,
Value: fmt.Sprintf("%v", rvCount),
},
},
EnvFrom: envFromSources,
// https://istio.io/docs/setup/kubernetes/additional-setup/requirements/
Resources: resources,
}
podSpec, err := util.MergePodSpec(&apiv1.PodSpec{
Containers: []apiv1.Container{*container},
TerminationGracePeriodSeconds: &gracePeriodSeconds,
}, fn.Spec.PodSpec)
if err != nil {
return nil, err
}
pod := apiv1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: podLabels,
Annotations: podAnnotations,
},
Spec: *podSpec,
}
pod.Spec = *(util.ApplyImagePullSecret("", pod.Spec))
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: deployName,
Labels: deployLabels,
Annotations: deployAnnotations,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: deployLabels,
},
Template: pod,
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
RollingUpdate: &appsv1.RollingUpdateDeployment{
MaxUnavailable: &maxUnavailable,
MaxSurge: &maxSurge,
},
},
RevisionHistoryLimit: &revisionHistoryLimit,
},
}
return deployment, nil
}
func (caaf *Container) scaleDeployment(deplNS string, deplName string, replicas int32) error {
caaf.logger.Info("scaling deployment",
zap.String("deployment", deplName),
zap.String("namespace", deplNS),
zap.Int32("replicas", replicas))
_, err := caaf.kubernetesClient.AppsV1().Deployments(deplNS).UpdateScale(context.TODO(), deplName, &autoscalingv1.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: deplName,
Namespace: deplNS,
},
Spec: autoscalingv1.ScaleSpec{
Replicas: replicas,
},
}, metav1.UpdateOptions{})
return err
}
@@ -0,0 +1,85 @@
/*
Copyright 2020 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package container
import (
"go.uber.org/zap"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
)
func (caaf *Container) FuncInformerHandler() k8sCache.ResourceEventHandlerFuncs {
return k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
fn := obj.(*fv1.Function)
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
if fnExecutorType != "" && fnExecutorType != fv1.ExecutorTypeContainer {
return
}
// TODO: A workaround to process items in parallel. We should use workqueue ("k8s.io/client-go/util/workqueue")
// and worker pattern to process items instead of moving process to another goroutine.
// example: https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/job/job_controller.go
go func() {
log := caaf.logger.With(zap.String("function_name", fn.ObjectMeta.Name), zap.String("function_namespace", fn.ObjectMeta.Namespace))
log.Debug("start function create handler")
_, err := caaf.createFunction(fn)
if err != nil {
log.Error("error eager creating function", zap.Error(err))
}
log.Debug("end function create handler")
}()
},
DeleteFunc: func(obj interface{}) {
fn := obj.(*fv1.Function)
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
if fnExecutorType != "" && fnExecutorType != fv1.ExecutorTypeContainer {
return
}
go func() {
log := caaf.logger.With(zap.String("function_name", fn.ObjectMeta.Name), zap.String("function_namespace", fn.ObjectMeta.Namespace))
log.Debug("start function delete handler")
err := caaf.deleteFunction(fn)
if err != nil {
log.Error("error deleting function", zap.Error(err))
}
log.Debug("end function delete handler")
}()
},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
oldFn := oldObj.(*fv1.Function)
newFn := newObj.(*fv1.Function)
fnExecutorType := oldFn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
if fnExecutorType != "" && fnExecutorType != fv1.ExecutorTypeContainer {
return
}
go func() {
log := caaf.logger.With(zap.String("function_name", newFn.ObjectMeta.Name),
zap.String("function_namespace", newFn.ObjectMeta.Namespace),
zap.String("old_function_name", oldFn.ObjectMeta.Name))
log.Debug("start function update handler")
err := caaf.updateFunction(oldFn, newFn)
if err != nil {
log.Error("error updating function",
zap.Error(err))
}
log.Debug("end function update handler")
}()
},
}
}
+113
View File
@@ -0,0 +1,113 @@
/*
Copyright 2020 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package container
import (
"context"
"github.com/pkg/errors"
"go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1"
asv1 "k8s.io/api/autoscaling/v1"
k8s_err "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
)
const (
DeploymentKind = "Deployment"
DeploymentVersion = "apps/v1"
)
func (cn *Container) 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")
}
minRepl := int32(execStrategy.MinScale)
if minRepl == 0 {
minRepl = 1
}
maxRepl := int32(execStrategy.MaxScale)
if maxRepl == 0 {
maxRepl = minRepl
}
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 := cn.getHpa(depl.ObjectMeta.Namespace, hpaName)
if err == nil {
// to adopt orphan service
if existingHpa.Annotations[fv1.EXECUTOR_INSTANCEID_LABEL] != cn.instanceID {
existingHpa.Annotations = hpa.Annotations
existingHpa.Labels = hpa.Labels
existingHpa.Spec = hpa.Spec
existingHpa, err = cn.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(depl.ObjectMeta.Namespace).Update(context.TODO(), existingHpa, metav1.UpdateOptions{})
if err != nil {
cn.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) {
cHpa, err := cn.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(depl.ObjectMeta.Namespace).Create(context.TODO(), hpa, metav1.CreateOptions{})
if err != nil {
if k8s_err.IsAlreadyExists(err) {
cHpa, err = cn.getHpa(depl.ObjectMeta.Namespace, hpaName)
}
if err != nil {
return nil, err
}
}
return cHpa, nil
}
return nil, err
}
func (cn *Container) getHpa(ns, name string) (*asv1.HorizontalPodAutoscaler, error) {
return cn.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(ns).Get(context.TODO(), name, metav1.GetOptions{})
}
func (cn *Container) updateHpa(hpa *asv1.HorizontalPodAutoscaler) error {
_, err := cn.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(hpa.ObjectMeta.Namespace).Update(context.TODO(), hpa, metav1.UpdateOptions{})
return err
}
func (cn *Container) deleteHpa(ns string, name string) error {
return cn.kubernetesClient.AutoscalingV1().HorizontalPodAutoscalers(ns).Delete(context.TODO(), name, metav1.DeleteOptions{})
}
+103
View File
@@ -0,0 +1,103 @@
/*
Copyright 2020 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package container
import (
"context"
"fmt"
"go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
k8s_err "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
)
func (cn *Container) getSvPort(fn *fv1.Function) (port int32, err error) {
if fn.Spec.PodSpec == nil {
return port, fmt.Errorf("podspec is empty for function %s", fn.ObjectMeta.Name)
}
if len(fn.Spec.PodSpec.Containers) != 1 {
return port, fmt.Errorf("podspec should have exactly one container %s", fn.ObjectMeta.Name)
}
if len(fn.Spec.PodSpec.Containers[0].Ports) != 1 {
return port, fmt.Errorf("container should have exactly one port %s", fn.ObjectMeta.Name)
}
return fn.Spec.PodSpec.Containers[0].Ports[0].ContainerPort, nil
}
func (cn *Container) createOrGetSvc(fn *fv1.Function, deployLabels map[string]string, deployAnnotations map[string]string, svcName string, svcNamespace string) (*apiv1.Service, error) {
targetPort, err := cn.getSvPort(fn)
if err != nil {
return nil, 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(int(targetPort)),
},
},
Selector: deployLabels,
Type: apiv1.ServiceTypeClusterIP,
},
}
existingSvc, err := cn.kubernetesClient.CoreV1().Services(svcNamespace).Get(context.TODO(), svcName, metav1.GetOptions{})
if err == nil {
// to adopt orphan service
if existingSvc.Annotations[fv1.EXECUTOR_INSTANCEID_LABEL] != cn.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 = cn.kubernetesClient.CoreV1().Services(svcNamespace).Update(context.TODO(), existingSvc, metav1.UpdateOptions{})
if err != nil {
cn.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) {
svc, err := cn.kubernetesClient.CoreV1().Services(svcNamespace).Create(context.TODO(), service, metav1.CreateOptions{})
if err != nil {
if k8s_err.IsAlreadyExists(err) {
svc, err = cn.kubernetesClient.CoreV1().Services(svcNamespace).Get(context.TODO(), svcName, metav1.GetOptions{})
}
if err != nil {
return nil, err
}
}
return svc, nil
}
return nil, err
}
func (cn *Container) deleteSvc(ns string, name string) error {
return cn.kubernetesClient.CoreV1().Services(ns).Delete(context.TODO(), name, metav1.DeleteOptions{})
}
@@ -0,0 +1,54 @@
/*
Copyright 2021 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package newdeploy
import (
"context"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
)
func (deploy *NewDeploy) EnvEventHandlers() k8sCache.ResourceEventHandlerFuncs {
return k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
DeleteFunc: func(obj interface{}) {},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
newEnv := newObj.(*fv1.Environment)
oldEnv := oldObj.(*fv1.Environment)
// Currently only an image update in environment calls for function's deployment recreation. In future there might be more attributes which would want to do it
if oldEnv.Spec.Runtime.Image != newEnv.Spec.Runtime.Image {
deploy.logger.Debug("Updating all function of the environment that changed, old env:", zap.Any("environment", oldEnv))
funcs := deploy.getEnvFunctions(&newEnv.ObjectMeta)
for _, f := range funcs {
function, err := deploy.fissionClient.CoreV1().Functions(f.ObjectMeta.Namespace).Get(context.TODO(), f.ObjectMeta.Name, metav1.GetOptions{})
if err != nil {
deploy.logger.Error("Error getting function", zap.Error(err), zap.Any("function", function))
continue
}
err = deploy.updateFuncDeployment(function, newEnv)
if err != nil {
deploy.logger.Error("Error updating function", zap.Error(err), zap.Any("function", function))
continue
}
}
}
},
}
}
@@ -0,0 +1,68 @@
/*
Copyright 2021 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package newdeploy
import (
"go.uber.org/zap"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
)
func (deploy *NewDeploy) FunctionEventHandlers() k8sCache.ResourceEventHandlerFuncs {
return k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
// TODO: A workaround to process items in parallel. We should use workqueue ("k8s.io/client-go/util/workqueue")
// and worker pattern to process items instead of moving process to another goroutine.
// example: https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/job/job_controller.go
go func() {
fn := obj.(*fv1.Function)
deploy.logger.Debug("create deployment for function", zap.Any("fn", fn.ObjectMeta), zap.Any("fnspec", fn.Spec))
_, err := deploy.createFunction(fn)
if err != nil {
deploy.logger.Error("error eager creating function",
zap.Error(err),
zap.Any("function", fn))
}
deploy.logger.Debug("end create deployment for function", zap.Any("fn", fn.ObjectMeta), zap.Any("fnspec", fn.Spec))
}()
},
DeleteFunc: func(obj interface{}) {
fn := obj.(*fv1.Function)
go func() {
err := deploy.deleteFunction(fn)
if err != nil {
deploy.logger.Error("error deleting function",
zap.Error(err),
zap.Any("function", fn))
}
}()
},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
oldFn := oldObj.(*fv1.Function)
newFn := newObj.(*fv1.Function)
go func() {
err := deploy.updateFunction(oldFn, newFn)
if err != nil {
deploy.logger.Error("error updating function",
zap.Error(err),
zap.Any("old_function", oldFn),
zap.Any("new_function", newFn))
}
}()
},
}
}
@@ -48,7 +48,7 @@ const (
func (deploy *NewDeploy) createOrGetDeployment(fn *fv1.Function, env *fv1.Environment, func (deploy *NewDeploy) createOrGetDeployment(fn *fv1.Function, env *fv1.Environment,
deployName string, deployLabels map[string]string, deployAnnotations map[string]string, deployNamespace string) (*appsv1.Deployment, error) { deployName string, deployLabels map[string]string, deployAnnotations map[string]string, deployNamespace string) (*appsv1.Deployment, error) {
specializationTimeout := int(fn.Spec.InvokeStrategy.ExecutionStrategy.SpecializationTimeout) specializationTimeout := fn.Spec.InvokeStrategy.ExecutionStrategy.SpecializationTimeout
minScale := int32(fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale) minScale := int32(fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale)
// Always scale to at least one pod when createOrGetDeployment // Always scale to at least one pod when createOrGetDeployment
@@ -262,7 +262,8 @@ func (deploy *NewDeploy) getDeploymentSpec(fn *fv1.Function, env *fv1.Environmen
// https://istio.io/docs/setup/kubernetes/additional-setup/requirements/ // https://istio.io/docs/setup/kubernetes/additional-setup/requirements/
Ports: []apiv1.ContainerPort{ Ports: []apiv1.ContainerPort{
{ {
Name: "http-env", Name: "http-env",
// Now that we have added Port field in spec, should we make this configurable too?
ContainerPort: int32(8888), ContainerPort: int32(8888),
}, },
}, },
@@ -453,8 +454,9 @@ func (deploy *NewDeploy) createOrGetSvc(deployLabels map[string]string, deployAn
Spec: apiv1.ServiceSpec{ Spec: apiv1.ServiceSpec{
Ports: []apiv1.ServicePort{ Ports: []apiv1.ServicePort{
{ {
Name: "http-env", Name: "http-env",
Port: int32(80), Port: int32(80),
// Since Function spec now supports Port , should we make this configurable too?
TargetPort: intstr.FromInt(8888), TargetPort: intstr.FromInt(8888),
}, },
}, },
@@ -499,14 +501,16 @@ func (deploy *NewDeploy) deleteSvc(ns string, name string) error {
return deploy.kubernetesClient.CoreV1().Services(ns).Delete(context.TODO(), name, metav1.DeleteOptions{}) return deploy.kubernetesClient.CoreV1().Services(ns).Delete(context.TODO(), name, metav1.DeleteOptions{})
} }
func (deploy *NewDeploy) waitForDeploy(depl *appsv1.Deployment, replicas int32, specializationTimeout int) (*appsv1.Deployment, error) { func (deploy *NewDeploy) waitForDeploy(depl *appsv1.Deployment, replicas int32, specializationTimeout int) (latestDepl *appsv1.Deployment, err error) {
oldStatus := depl.Status
// if no specializationTimeout is set, use default value // if no specializationTimeout is set, use default value
if specializationTimeout < fv1.DefaultSpecializationTimeOut { if specializationTimeout < fv1.DefaultSpecializationTimeOut {
specializationTimeout = fv1.DefaultSpecializationTimeOut specializationTimeout = fv1.DefaultSpecializationTimeOut
} }
for i := 0; i < specializationTimeout; i++ { for i := 0; i < specializationTimeout; i++ {
latestDepl, err := deploy.kubernetesClient.AppsV1().Deployments(depl.ObjectMeta.Namespace).Get(context.TODO(), depl.Name, metav1.GetOptions{}) latestDepl, err = deploy.kubernetesClient.AppsV1().Deployments(depl.ObjectMeta.Namespace).Get(context.TODO(), depl.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -519,6 +523,10 @@ func (deploy *NewDeploy) waitForDeploy(depl *appsv1.Deployment, replicas int32,
time.Sleep(time.Second) time.Sleep(time.Second)
} }
deploy.logger.Error("Deployment provision failed within timeout window",
zap.String("name", latestDepl.ObjectMeta.Name), zap.Any("old_status", oldStatus),
zap.Any("current_status", latestDepl.Status), zap.Int("timeout", specializationTimeout))
// this error appears in the executor pod logs // this error appears in the executor pod logs
timeoutError := fmt.Errorf("failed to create deployment within the timeout window of %d seconds", specializationTimeout) timeoutError := fmt.Errorf("failed to create deployment within the timeout window of %d seconds", specializationTimeout)
return nil, timeoutError return nil, timeoutError
@@ -33,11 +33,9 @@ import (
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
k8sErrs "k8s.io/apimachinery/pkg/api/errors" k8sErrs "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
k8sTypes "k8s.io/apimachinery/pkg/types" k8sTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
k8sCache "k8s.io/client-go/tools/cache" k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1" fv1 "github.com/fission/fission/pkg/apis/core/v1"
@@ -48,6 +46,7 @@ import (
fetcherConfig "github.com/fission/fission/pkg/fetcher/config" fetcherConfig "github.com/fission/fission/pkg/fetcher/config"
"github.com/fission/fission/pkg/throttler" "github.com/fission/fission/pkg/throttler"
"github.com/fission/fission/pkg/utils" "github.com/fission/fission/pkg/utils"
"github.com/fission/fission/pkg/utils/maps"
) )
var _ executortype.ExecutorType = &NewDeploy{} var _ executortype.ExecutorType = &NewDeploy{}
@@ -59,7 +58,6 @@ type (
kubernetesClient *kubernetes.Clientset kubernetesClient *kubernetes.Clientset
fissionClient *crd.FissionClient fissionClient *crd.FissionClient
crdClient rest.Interface
instanceID string instanceID string
fetcherConfig *fetcherConfig.Config fetcherConfig *fetcherConfig.Config
@@ -69,12 +67,11 @@ type (
fsCache *fscache.FunctionServiceCache // cache funcSvc's by function, address and pod name fsCache *fscache.FunctionServiceCache // cache funcSvc's by function, address and pod name
throttler *throttler.Throttler throttler *throttler.Throttler
funcStore k8sCache.Store
funcController k8sCache.Controller funcInformer *k8sCache.SharedIndexInformer
envInformer *k8sCache.SharedIndexInformer
envStore k8sCache.Store
envController k8sCache.Controller
serviceInformer k8sCache.SharedIndexInformer serviceInformer k8sCache.SharedIndexInformer
deploymentInformer k8sCache.SharedIndexInformer deploymentInformer k8sCache.SharedIndexInformer
@@ -87,10 +84,11 @@ func MakeNewDeploy(
logger *zap.Logger, logger *zap.Logger,
fissionClient *crd.FissionClient, fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, kubernetesClient *kubernetes.Clientset,
crdClient rest.Interface,
namespace string, namespace string,
fetcherConfig *fetcherConfig.Config, fetcherConfig *fetcherConfig.Config,
instanceID string, instanceID string,
funcInformer *k8sCache.SharedIndexInformer,
envInformer *k8sCache.SharedIndexInformer,
) (executortype.ExecutorType, error) { ) (executortype.ExecutorType, error) {
enableIstio := false enableIstio := false
if len(os.Getenv("ENABLE_ISTIO")) > 0 { if len(os.Getenv("ENABLE_ISTIO")) > 0 {
@@ -106,7 +104,6 @@ func MakeNewDeploy(
fissionClient: fissionClient, fissionClient: fissionClient,
kubernetesClient: kubernetesClient, kubernetesClient: kubernetesClient,
crdClient: crdClient,
instanceID: instanceID, instanceID: instanceID,
namespace: namespace, namespace: namespace,
@@ -118,19 +115,14 @@ func MakeNewDeploy(
useIstio: enableIstio, useIstio: enableIstio,
defaultIdlePodReapTime: 2 * time.Minute, defaultIdlePodReapTime: 2 * time.Minute,
funcInformer: funcInformer,
envInformer: envInformer,
} }
if nd.crdClient != nil { (*nd.funcInformer).AddEventHandler(nd.FunctionEventHandlers())
fnStore, fnController := nd.initFuncController() (*nd.envInformer).AddEventHandler(nd.EnvEventHandlers())
nd.funcStore = fnStore
nd.funcController = fnController
envStore, envController := nd.initEnvController() informerFactory, err := utils.GetInformerFactoryByExecutor(nd.kubernetesClient, fv1.ExecutorTypePoolmgr)
nd.envStore = envStore
nd.envController = envController
}
informerFactory, err := utils.GetInformerFacoryByExecutor(nd.kubernetesClient, fv1.ExecutorTypePoolmgr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -141,8 +133,6 @@ func MakeNewDeploy(
// Run start the function and environment controller along with an object reaper. // Run start the function and environment controller along with an object reaper.
func (deploy *NewDeploy) Run(ctx context.Context) { func (deploy *NewDeploy) Run(ctx context.Context) {
go deploy.funcController.Run(ctx.Done())
go deploy.envController.Run(ctx.Done())
go deploy.serviceInformer.Run(ctx.Done()) go deploy.serviceInformer.Run(ctx.Done())
go deploy.deploymentInformer.Run(ctx.Done()) go deploy.deploymentInformer.Run(ctx.Done())
go deploy.idleObjectReaper() go deploy.idleObjectReaper()
@@ -191,23 +181,12 @@ func (deploy *NewDeploy) TapService(svcHost string) error {
return nil return nil
} }
func getCachedItem(obj apiv1.ObjectReference, informer k8sCache.SharedIndexInformer) (item interface{}, exists bool, err error) {
store := informer.GetStore()
item, exists, err = store.Get(obj)
if err != nil || !exists {
item, exists, err = store.GetByKey(fmt.Sprintf("%s/%s", obj.Namespace, obj.Name))
}
return item, exists, err
}
func (deploy *NewDeploy) getServiceInfo(obj apiv1.ObjectReference) (*apiv1.Service, error) { func (deploy *NewDeploy) getServiceInfo(obj apiv1.ObjectReference) (*apiv1.Service, error) {
item, exists, err := getCachedItem(obj, deploy.serviceInformer) item, exists, err := utils.GetCachedItem(obj, deploy.serviceInformer)
if err != nil || !exists { if err != nil || !exists {
deploy.logger.Debug( deploy.logger.Debug(
"Falling back to getting service info from k8s API -- this may cause performace issues for your function.", "Falling back to getting service info from k8s API -- this may cause performance issues for your function.",
zap.Bool("exists", exists), zap.Bool("exists", exists),
zap.Error(err), zap.Error(err),
) )
@@ -220,11 +199,11 @@ func (deploy *NewDeploy) getServiceInfo(obj apiv1.ObjectReference) (*apiv1.Servi
} }
func (deploy *NewDeploy) getDeploymentInfo(obj apiv1.ObjectReference) (*appsv1.Deployment, error) { func (deploy *NewDeploy) getDeploymentInfo(obj apiv1.ObjectReference) (*appsv1.Deployment, error) {
item, exists, err := getCachedItem(obj, deploy.deploymentInformer) item, exists, err := utils.GetCachedItem(obj, deploy.deploymentInformer)
if err != nil || !exists { if err != nil || !exists {
deploy.logger.Debug( deploy.logger.Debug(
"Falling back to getting deployment info from k8s API -- this may cause performace issues for your function.", "Falling back to getting deployment info from k8s API -- this may cause performance issues for your function.",
zap.Bool("exists", exists), zap.Bool("exists", exists),
zap.Error(err), zap.Error(err),
) )
@@ -241,9 +220,13 @@ func (deploy *NewDeploy) getDeploymentInfo(obj apiv1.ObjectReference) (*appsv1.D
// Return true if no error occurs, return false otherwise. // Return true if no error occurs, return false otherwise.
func (deploy *NewDeploy) IsValid(fsvc *fscache.FuncSvc) bool { func (deploy *NewDeploy) IsValid(fsvc *fscache.FuncSvc) bool {
if len(strings.Split(fsvc.Address, ".")) == 0 { if len(strings.Split(fsvc.Address, ".")) == 0 {
deploy.logger.Error("address not found in function service")
return false
}
if len(fsvc.KubernetesObjects) == 0 {
deploy.logger.Error("no kubernetes object related to function", zap.String("function", fsvc.Function.Name))
return false return false
} }
for _, obj := range fsvc.KubernetesObjects { for _, obj := range fsvc.KubernetesObjects {
if strings.ToLower(obj.Kind) == "service" { if strings.ToLower(obj.Kind) == "service" {
_, err := deploy.getServiceInfo(obj) _, err := deploy.getServiceInfo(obj)
@@ -254,11 +237,7 @@ func (deploy *NewDeploy) IsValid(fsvc *fscache.FuncSvc) bool {
return false return false
} }
} } else if strings.ToLower(obj.Kind) == "deployment" {
}
for _, obj := range fsvc.KubernetesObjects {
if strings.ToLower(obj.Kind) == "deployment" {
currentDeploy, err := deploy.getDeploymentInfo(obj) currentDeploy, err := deploy.getDeploymentInfo(obj)
if err != nil { if err != nil {
if !k8sErrs.IsNotFound(err) { if !k8sErrs.IsNotFound(err) {
@@ -266,17 +245,15 @@ func (deploy *NewDeploy) IsValid(fsvc *fscache.FuncSvc) bool {
} }
return false return false
} }
// return directly when available replicas > 0 if currentDeploy.Status.AvailableReplicas < 1 {
if currentDeploy.Status.AvailableReplicas > 0 { return false
return true
} }
} }
} }
return true
return false
} }
// RefreshFuncPods deleted pods related to the function so that new pods are replenished // RefreshFuncPods deletes pods related to the function so that new pods are replenished
func (deploy *NewDeploy) RefreshFuncPods(logger *zap.Logger, f fv1.Function) error { func (deploy *NewDeploy) RefreshFuncPods(logger *zap.Logger, f fv1.Function) error {
env, err := deploy.fissionClient.CoreV1().Environments(f.Spec.Environment.Namespace).Get(context.TODO(), f.Spec.Environment.Name, metav1.GetOptions{}) env, err := deploy.fissionClient.CoreV1().Environments(f.Spec.Environment.Namespace).Get(context.TODO(), f.Spec.Environment.Name, metav1.GetOptions{})
@@ -378,85 +355,6 @@ func (deploy *NewDeploy) CleanupOldExecutorObjects() {
} }
} }
func (deploy *NewDeploy) initFuncController() (k8sCache.Store, k8sCache.Controller) {
resyncPeriod := 30 * time.Second
listWatch := k8sCache.NewListWatchFromClient(deploy.crdClient, "functions", metav1.NamespaceAll, fields.Everything())
store, controller := k8sCache.NewInformer(listWatch, &fv1.Function{}, resyncPeriod, k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
// TODO: A workaround to process items in parallel. We should use workqueue ("k8s.io/client-go/util/workqueue")
// and worker pattern to process items instead of moving process to another goroutine.
// example: https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/job/job_controller.go
go func() {
fn := obj.(*fv1.Function)
deploy.logger.Debug("create deployment for function", zap.Any("fn", fn.ObjectMeta), zap.Any("fnspec", fn.Spec))
_, err := deploy.createFunction(fn)
if err != nil {
deploy.logger.Error("error eager creating function",
zap.Error(err),
zap.Any("function", fn))
}
deploy.logger.Debug("end create deployment for function", zap.Any("fn", fn.ObjectMeta), zap.Any("fnspec", fn.Spec))
}()
},
DeleteFunc: func(obj interface{}) {
fn := obj.(*fv1.Function)
go func() {
err := deploy.deleteFunction(fn)
if err != nil {
deploy.logger.Error("error deleting function",
zap.Error(err),
zap.Any("function", fn))
}
}()
},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
oldFn := oldObj.(*fv1.Function)
newFn := newObj.(*fv1.Function)
go func() {
err := deploy.updateFunction(oldFn, newFn)
if err != nil {
deploy.logger.Error("error updating function",
zap.Error(err),
zap.Any("old_function", oldFn),
zap.Any("new_function", newFn))
}
}()
},
})
return store, controller
}
func (deploy *NewDeploy) initEnvController() (k8sCache.Store, k8sCache.Controller) {
resyncPeriod := 30 * time.Second
listWatch := k8sCache.NewListWatchFromClient(deploy.crdClient, "environments", metav1.NamespaceAll, fields.Everything())
store, controller := k8sCache.NewInformer(listWatch, &fv1.Environment{}, resyncPeriod, k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
DeleteFunc: func(obj interface{}) {},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
newEnv := newObj.(*fv1.Environment)
oldEnv := oldObj.(*fv1.Environment)
// Currently only an image update in environment calls for function's deployment recreation. In future there might be more attributes which would want to do it
if oldEnv.Spec.Runtime.Image != newEnv.Spec.Runtime.Image {
deploy.logger.Debug("Updating all function of the environment that changed, old env:", zap.Any("environment", oldEnv))
funcs := deploy.getEnvFunctions(&newEnv.ObjectMeta)
for _, f := range funcs {
function, err := deploy.fissionClient.CoreV1().Functions(f.ObjectMeta.Namespace).Get(context.TODO(), f.ObjectMeta.Name, metav1.GetOptions{})
if err != nil {
deploy.logger.Error("Error getting function", zap.Error(err), zap.Any("function", function))
continue
}
err = deploy.updateFuncDeployment(function, newEnv)
if err != nil {
deploy.logger.Error("Error updating function", zap.Error(err), zap.Any("function", function))
continue
}
}
}
},
})
return store, controller
}
func (deploy *NewDeploy) getEnvFunctions(m *metav1.ObjectMeta) []fv1.Function { func (deploy *NewDeploy) getEnvFunctions(m *metav1.ObjectMeta) []fv1.Function {
funcList, err := deploy.fissionClient.CoreV1().Functions(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{}) funcList, err := deploy.fissionClient.CoreV1().Functions(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil { if err != nil {
@@ -512,6 +410,13 @@ func (deploy *NewDeploy) deleteFunction(fn *fv1.Function) error {
} }
func (deploy *NewDeploy) fnCreate(fn *fv1.Function) (*fscache.FuncSvc, error) { func (deploy *NewDeploy) fnCreate(fn *fv1.Function) (*fscache.FuncSvc, error) {
cleanupFunc := func(ns string, name string) {
err := deploy.cleanupNewdeploy(ns, name)
if err != nil {
deploy.logger.Error("received error while cleaning function resources",
zap.String("namespace", ns), zap.String("name", name))
}
}
env, err := deploy.fissionClient.CoreV1(). env, err := deploy.fissionClient.CoreV1().
Environments(fn.Spec.Environment.Namespace). Environments(fn.Spec.Environment.Namespace).
Get(context.TODO(), fn.Spec.Environment.Name, metav1.GetOptions{}) Get(context.TODO(), fn.Spec.Environment.Name, metav1.GetOptions{})
@@ -521,7 +426,7 @@ func (deploy *NewDeploy) fnCreate(fn *fv1.Function) (*fscache.FuncSvc, error) {
objName := deploy.getObjName(fn) objName := deploy.getObjName(fn)
deployLabels := deploy.getDeployLabels(fn.ObjectMeta, env.ObjectMeta) deployLabels := deploy.getDeployLabels(fn.ObjectMeta, env.ObjectMeta)
deployAnnotations := deploy.getDeployAnnotations(fn.ObjectMeta) deployAnnotations := deploy.getDeployAnnotations(fn.ObjectMeta, env.ObjectMeta)
// to support backward compatibility, if the function was created in default ns, we fall back to creating the // to support backward compatibility, if the function was created in default ns, we fall back to creating the
// deployment of the function in fission-function ns // deployment of the function in fission-function ns
@@ -538,7 +443,7 @@ func (deploy *NewDeploy) fnCreate(fn *fv1.Function) (*fscache.FuncSvc, error) {
svc, err := deploy.createOrGetSvc(deployLabels, deployAnnotations, objName, ns) svc, err := deploy.createOrGetSvc(deployLabels, deployAnnotations, objName, ns)
if err != nil { if err != nil {
deploy.logger.Error("error creating service", zap.Error(err), zap.String("service", objName)) deploy.logger.Error("error creating service", zap.Error(err), zap.String("service", objName))
go deploy.cleanupNewdeploy(ns, objName) //nolint: errcheck go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating service %v", objName) return nil, errors.Wrapf(err, "error creating service %v", objName)
} }
svcAddress := fmt.Sprintf("%v.%v", svc.Name, svc.Namespace) svcAddress := fmt.Sprintf("%v.%v", svc.Name, svc.Namespace)
@@ -546,14 +451,14 @@ func (deploy *NewDeploy) fnCreate(fn *fv1.Function) (*fscache.FuncSvc, error) {
depl, err := deploy.createOrGetDeployment(fn, env, objName, deployLabels, deployAnnotations, ns) depl, err := deploy.createOrGetDeployment(fn, env, objName, deployLabels, deployAnnotations, ns)
if err != nil { if err != nil {
deploy.logger.Error("error creating deployment", zap.Error(err), zap.String("deployment", objName)) deploy.logger.Error("error creating deployment", zap.Error(err), zap.String("deployment", objName))
go deploy.cleanupNewdeploy(ns, objName) //nolint: errcheck go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating deployment %v", objName) return nil, errors.Wrapf(err, "error creating deployment %v", objName)
} }
hpa, err := deploy.createOrGetHpa(objName, &fn.Spec.InvokeStrategy.ExecutionStrategy, depl, deployLabels, deployAnnotations) hpa, err := deploy.createOrGetHpa(objName, &fn.Spec.InvokeStrategy.ExecutionStrategy, depl, deployLabels, deployAnnotations)
if err != nil { if err != nil {
deploy.logger.Error("error creating HPA", zap.Error(err), zap.String("hpa", objName)) deploy.logger.Error("error creating HPA", zap.Error(err), zap.String("hpa", objName))
go deploy.cleanupNewdeploy(ns, objName) //nolint: errcheck go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating the HPA %v", objName) return nil, errors.Wrapf(err, "error creating the HPA %v", objName)
} }
@@ -761,7 +666,7 @@ func (deploy *NewDeploy) updateFuncDeployment(fn *fv1.Function, env *fv1.Environ
// Therefore, the deployment update will trigger a rolling update. // Therefore, the deployment update will trigger a rolling update.
newDeployment, err := deploy.getDeploymentSpec(fn, env, newDeployment, err := deploy.getDeploymentSpec(fn, env,
existingDepl.Spec.Replicas, // use current replicas instead of minscale in the ExecutionStrategy. existingDepl.Spec.Replicas, // use current replicas instead of minscale in the ExecutionStrategy.
fnObjName, ns, deployLabels, deploy.getDeployAnnotations(fn.ObjectMeta)) fnObjName, ns, deployLabels, deploy.getDeployAnnotations(fn.ObjectMeta, env.ObjectMeta))
if err != nil { if err != nil {
deploy.updateStatus(fn, err, "failed to get new deployment spec while updating function") deploy.updateStatus(fn, err, "failed to get new deployment spec while updating function")
return err return err
@@ -815,11 +720,29 @@ func (deploy *NewDeploy) fnDelete(fn *fv1.Function) error {
func (deploy *NewDeploy) getObjName(fn *fv1.Function) string { func (deploy *NewDeploy) getObjName(fn *fv1.Function) string {
// use meta uuid of function, this ensure we always get the same name for the same function. // use meta uuid of function, this ensure we always get the same name for the same function.
uid := fn.ObjectMeta.UID[len(fn.ObjectMeta.UID)-17:] uid := fn.ObjectMeta.UID[len(fn.ObjectMeta.UID)-17:]
return strings.ToLower(fmt.Sprintf("newdeploy-%v-%v-%v", fn.ObjectMeta.Name, fn.ObjectMeta.Namespace, uid)) var functionMetadata string
if len(fn.ObjectMeta.Name)+len(fn.ObjectMeta.Namespace) < 35 {
functionMetadata = fn.ObjectMeta.Name + "-" + fn.ObjectMeta.Namespace
} else {
if len(fn.ObjectMeta.Name) > 17 {
functionMetadata = fn.ObjectMeta.Name[:17]
} else {
functionMetadata = fn.ObjectMeta.Name
}
if len(fn.ObjectMeta.Namespace) > 17 {
functionMetadata = functionMetadata + "-" + fn.ObjectMeta.Namespace[:17]
} else {
functionMetadata = functionMetadata + "-" + fn.ObjectMeta.Namespace
}
}
// contructed name should be 63 characters long, as it is a valid k8s name
// functionMetadata should be 35 characters long, as we take 17 characters from functionUid
// with newdeploy 10 character prefix
return strings.ToLower(fmt.Sprintf("newdeploy-%s-%s", functionMetadata, uid))
} }
func (deploy *NewDeploy) getDeployLabels(fnMeta metav1.ObjectMeta, envMeta metav1.ObjectMeta) map[string]string { func (deploy *NewDeploy) getDeployLabels(fnMeta metav1.ObjectMeta, envMeta metav1.ObjectMeta) map[string]string {
return map[string]string{ deployLabels := map[string]string{
fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeNewdeploy), fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeNewdeploy),
fv1.ENVIRONMENT_NAME: envMeta.Name, fv1.ENVIRONMENT_NAME: envMeta.Name,
fv1.ENVIRONMENT_NAMESPACE: envMeta.Namespace, fv1.ENVIRONMENT_NAMESPACE: envMeta.Namespace,
@@ -828,13 +751,17 @@ func (deploy *NewDeploy) getDeployLabels(fnMeta metav1.ObjectMeta, envMeta metav
fv1.FUNCTION_NAMESPACE: fnMeta.Namespace, fv1.FUNCTION_NAMESPACE: fnMeta.Namespace,
fv1.FUNCTION_UID: string(fnMeta.UID), fv1.FUNCTION_UID: string(fnMeta.UID),
} }
for k, v := range envMeta.Labels {
deployLabels[k] = v
}
return deployLabels
} }
func (deploy *NewDeploy) getDeployAnnotations(fnMeta metav1.ObjectMeta) map[string]string { func (deploy *NewDeploy) getDeployAnnotations(fnMeta metav1.ObjectMeta, envMeta metav1.ObjectMeta) map[string]string {
return map[string]string{ deployAnnotations := maps.CopyStringMap(envMeta.Annotations)
fv1.EXECUTOR_INSTANCEID_LABEL: deploy.instanceID, deployAnnotations[fv1.EXECUTOR_INSTANCEID_LABEL] = deploy.instanceID
fv1.FUNCTION_RESOURCE_VERSION: fnMeta.ResourceVersion, deployAnnotations[fv1.FUNCTION_RESOURCE_VERSION] = fnMeta.ResourceVersion
} return deployAnnotations
} }
// updateStatus is a function which updates status of update. // updateStatus is a function which updates status of update.
@@ -876,7 +803,7 @@ func (deploy *NewDeploy) idleObjectReaper() {
// For function with the environment that no longer exists, executor // For function with the environment that no longer exists, executor
// scales down the deployment as usual and prints log to notify user. // scales down the deployment as usual and prints log to notify user.
if _, ok := envList[fsvc.Environment.ObjectMeta.UID]; !ok { if _, ok := envList[fsvc.Environment.ObjectMeta.UID]; !ok {
deploy.logger.Error("function environment no longer exists", deploy.logger.Warn("function environment no longer exists",
zap.String("environment", fsvc.Environment.ObjectMeta.Name), zap.String("environment", fsvc.Environment.ObjectMeta.Name),
zap.String("function", fsvc.Name)) zap.String("function", fsvc.Name))
} }
@@ -0,0 +1,198 @@
/*
Copyright 2018 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package poolmgr
import (
"context"
"go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/utils"
)
func getIstioServiceLabels(fnName string) map[string]string {
return map[string]string{
"functionName": fnName,
}
}
func (gpm *GenericPoolManager) FunctionEventHandlers(kubernetesClient *kubernetes.Clientset, fissionfnNamespace string, istioEnabled bool) k8sCache.ResourceEventHandlerFuncs {
return k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
fn := obj.(*fv1.Function)
// Since istio only allows accessing pod through k8s service,
// for the functions with executor type "poolmgr" we need to
// create a service for sending requests to pod in pool.
// Functions with executor type "Newdeploy" is specialized at
// pod starts. In this case, just ignore such functions.
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
// In some cases, user may not enter the executorType explicitly, for example in his spec.yaml.
// we assume it to be of type poolmgr
if fnExecutorType != "" && fnExecutorType != fv1.ExecutorTypePoolmgr {
return
}
// create or update role-binding
envNs := fissionfnNamespace
if fn.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = fn.Spec.Environment.Namespace
}
// TODO : Just bring to your attention during review :
// setup rolebinding is tried, if it fails, we don't return. we just log an error and move on, because :
// 1. not all functions have secrets and/or configmaps, so things will work without this rolebinding in that case.
// 2. on the contrary, when the route is tried, the env fetcher logs will show a 403 forbidden message and same will be relayed to executor.
err := utils.SetupRoleBinding(gpm.logger, kubernetesClient, fv1.SecretConfigMapGetterRB, fn.ObjectMeta.Namespace, fv1.SecretConfigMapGetterCR, fv1.ClusterRole, fv1.FissionFetcherSA, envNs)
if err != nil {
gpm.logger.Error("error creating rolebinding", zap.Error(err), zap.String("role_binding", fv1.SecretConfigMapGetterRB))
} else {
gpm.logger.Debug("successfully set up rolebinding for fetcher service account for function",
zap.String("service_account", fv1.FissionFetcherSA),
zap.String("service_account_namepsace", envNs),
zap.String("function_name", fn.ObjectMeta.Name),
zap.String("function_namespace", fn.ObjectMeta.Namespace))
}
if istioEnabled {
// create a same name service for function
// since istio only allows the traffic to service
sel := map[string]string{
"functionName": fn.ObjectMeta.Name,
"functionUid": string(fn.ObjectMeta.UID),
}
svcName := utils.GetFunctionIstioServiceName(fn.ObjectMeta.Name, fn.ObjectMeta.Namespace)
// service for accepting user traffic
svc := apiv1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: envNs,
Name: svcName,
Labels: getIstioServiceLabels(fn.ObjectMeta.Name),
},
Spec: apiv1.ServiceSpec{
Type: apiv1.ServiceTypeClusterIP,
Ports: []apiv1.ServicePort{
// Service port name should begin with a recognized prefix, or the traffic will be
// treated as TCP traffic. (https://istio.io/docs/setup/kubernetes/additional-setup/requirements/)
{
Name: "http-fetcher",
Protocol: apiv1.ProtocolTCP,
Port: 8000,
TargetPort: intstr.FromInt(8000),
},
{
Name: "http-env",
Protocol: apiv1.ProtocolTCP,
Port: 8888,
TargetPort: intstr.FromInt(8888),
},
},
Selector: sel,
},
}
// create function istio service if it does not exist
_, err = kubernetesClient.CoreV1().Services(envNs).Create(context.TODO(), &svc, metav1.CreateOptions{})
if err != nil && !kerrors.IsAlreadyExists(err) {
gpm.logger.Error("error creating istio service for function",
zap.Error(err),
zap.String("service_name", svcName),
zap.String("function_name", fn.ObjectMeta.Name),
zap.Any("selectors", sel))
}
}
},
DeleteFunc: func(obj interface{}) {
fn := obj.(*fv1.Function)
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
if fnExecutorType != "" && fnExecutorType != fv1.ExecutorTypePoolmgr {
return
}
envNs := fissionfnNamespace
if fn.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = fn.Spec.Environment.Namespace
}
if istioEnabled {
svcName := utils.GetFunctionIstioServiceName(fn.ObjectMeta.Name, fn.ObjectMeta.Namespace)
// delete function istio service
err := kubernetesClient.CoreV1().Services(envNs).Delete(context.TODO(), svcName, metav1.DeleteOptions{})
if err != nil && !kerrors.IsNotFound(err) {
gpm.logger.Error("error deleting istio service for function",
zap.Error(err),
zap.String("service_name", svcName),
zap.String("function_name", fn.ObjectMeta.Name))
}
}
},
UpdateFunc: func(oldObj, newObj interface{}) {
oldFunc := oldObj.(*fv1.Function)
newFunc := newObj.(*fv1.Function)
if oldFunc.ObjectMeta.ResourceVersion == newFunc.ObjectMeta.ResourceVersion {
return
}
envChanged := (oldFunc.Spec.Environment.Namespace != newFunc.Spec.Environment.Namespace)
executorTypeChangedToPM := (oldFunc.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypePoolmgr &&
newFunc.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fv1.ExecutorTypePoolmgr)
// if a func's env reference gets updated and the newly referenced env is in a different ns,
// we need to create a rolebinding in func's ns so that the fetcher-sa in env ns has access
// to fetch secrets and config maps from the func's ns.
// similarly if executorType changed to Pool Manager, we now need a rolebinding in the func ns for fetcher sa
// present in env ns because for newdeploy, the fetcher sa is in function namespace
if envChanged || executorTypeChangedToPM {
envNs := fissionfnNamespace
if newFunc.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = newFunc.Spec.Environment.Namespace
}
err := utils.SetupRoleBinding(gpm.logger, kubernetesClient, fv1.SecretConfigMapGetterRB,
newFunc.ObjectMeta.Namespace, fv1.SecretConfigMapGetterCR, fv1.ClusterRole,
fv1.FissionFetcherSA, envNs)
if err != nil {
gpm.logger.Error("error creating rolebinding", zap.Error(err), zap.String("role_binding", fv1.SecretConfigMapGetterRB))
} else {
gpm.logger.Debug("successfully set up rolebinding for fetcher service account for function",
zap.String("service_account", fv1.FissionFetcherSA),
zap.String("service_account_namepsace", envNs),
zap.String("function_name", newFunc.ObjectMeta.Name),
zap.String("function_namespace", newFunc.ObjectMeta.Namespace))
}
}
},
}
}
@@ -1,208 +0,0 @@
/*
Copyright 2018 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package poolmgr
import (
"context"
"time"
"go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/crd"
"github.com/fission/fission/pkg/utils"
)
func getIstioServiceLabels(fnName string) map[string]string {
return map[string]string{
"functionName": fnName,
}
}
func (gpm *GenericPoolManager) makeFuncController(fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, fissionfnNamespace string, istioEnabled bool) (k8sCache.Store, k8sCache.Controller) {
resyncPeriod := 30 * time.Second
lw := k8sCache.NewListWatchFromClient(fissionClient.CoreV1().RESTClient(), "functions", metav1.NamespaceAll, fields.Everything())
funcStore, controller := k8sCache.NewInformer(lw, &fv1.Function{}, resyncPeriod,
k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
fn := obj.(*fv1.Function)
// Since istio only allows accessing pod through k8s service,
// for the functions with executor type "poolmgr" we need to
// create a service for sending requests to pod in pool.
// Functions with executor type "Newdeploy" is specialized at
// pod starts. In this case, just ignore such functions.
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
// In some cases, user may not enter the executorType explicitly, for example in his spec.yaml.
// we assume it to be of type poolmgr
if fnExecutorType != "" && fnExecutorType != fv1.ExecutorTypePoolmgr {
return
}
// create or update role-binding
envNs := fissionfnNamespace
if fn.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = fn.Spec.Environment.Namespace
}
// TODO : Just bring to your attention during review :
// setup rolebinding is tried, if it fails, we don't return. we just log an error and move on, because :
// 1. not all functions have secrets and/or configmaps, so things will work without this rolebinding in that case.
// 2. on the contrary, when the route is tried, the env fetcher logs will show a 403 forbidden message and same will be relayed to executor.
err := utils.SetupRoleBinding(gpm.logger, kubernetesClient, fv1.SecretConfigMapGetterRB, fn.ObjectMeta.Namespace, fv1.SecretConfigMapGetterCR, fv1.ClusterRole, fv1.FissionFetcherSA, envNs)
if err != nil {
gpm.logger.Error("error creating rolebinding", zap.Error(err), zap.String("role_binding", fv1.SecretConfigMapGetterRB))
} else {
gpm.logger.Debug("successfully set up rolebinding for fetcher service account for function",
zap.String("service_account", fv1.FissionFetcherSA),
zap.String("service_account_namepsace", envNs),
zap.String("function_name", fn.ObjectMeta.Name),
zap.String("function_namespace", fn.ObjectMeta.Namespace))
}
if istioEnabled {
// create a same name service for function
// since istio only allows the traffic to service
sel := map[string]string{
"functionName": fn.ObjectMeta.Name,
"functionUid": string(fn.ObjectMeta.UID),
}
svcName := utils.GetFunctionIstioServiceName(fn.ObjectMeta.Name, fn.ObjectMeta.Namespace)
// service for accepting user traffic
svc := apiv1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: envNs,
Name: svcName,
Labels: getIstioServiceLabels(fn.ObjectMeta.Name),
},
Spec: apiv1.ServiceSpec{
Type: apiv1.ServiceTypeClusterIP,
Ports: []apiv1.ServicePort{
// Service port name should begin with a recognized prefix, or the traffic will be
// treated as TCP traffic. (https://istio.io/docs/setup/kubernetes/additional-setup/requirements/)
{
Name: "http-fetcher",
Protocol: apiv1.ProtocolTCP,
Port: 8000,
TargetPort: intstr.FromInt(8000),
},
{
Name: "http-env",
Protocol: apiv1.ProtocolTCP,
Port: 8888,
TargetPort: intstr.FromInt(8888),
},
},
Selector: sel,
},
}
// create function istio service if it does not exist
_, err = kubernetesClient.CoreV1().Services(envNs).Create(context.TODO(), &svc, metav1.CreateOptions{})
if err != nil && !kerrors.IsAlreadyExists(err) {
gpm.logger.Error("error creating istio service for function",
zap.Error(err),
zap.String("service_name", svcName),
zap.String("function_name", fn.ObjectMeta.Name),
zap.Any("selectors", sel))
}
}
},
DeleteFunc: func(obj interface{}) {
fn := obj.(*fv1.Function)
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
if fnExecutorType != "" && fnExecutorType != fv1.ExecutorTypePoolmgr {
return
}
envNs := fissionfnNamespace
if fn.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = fn.Spec.Environment.Namespace
}
if istioEnabled {
svcName := utils.GetFunctionIstioServiceName(fn.ObjectMeta.Name, fn.ObjectMeta.Namespace)
// delete function istio service
err := kubernetesClient.CoreV1().Services(envNs).Delete(context.TODO(), svcName, metav1.DeleteOptions{})
if err != nil && !kerrors.IsNotFound(err) {
gpm.logger.Error("error deleting istio service for function",
zap.Error(err),
zap.String("service_name", svcName),
zap.String("function_name", fn.ObjectMeta.Name))
}
}
},
UpdateFunc: func(oldObj, newObj interface{}) {
oldFunc := oldObj.(*fv1.Function)
newFunc := newObj.(*fv1.Function)
if oldFunc.ObjectMeta.ResourceVersion == newFunc.ObjectMeta.ResourceVersion {
return
}
envChanged := (oldFunc.Spec.Environment.Namespace != newFunc.Spec.Environment.Namespace)
executorTypeChangedToPM := (oldFunc.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fv1.ExecutorTypePoolmgr &&
newFunc.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fv1.ExecutorTypePoolmgr)
// if a func's env reference gets updated and the newly referenced env is in a different ns,
// we need to create a rolebinding in func's ns so that the fetcher-sa in env ns has access
// to fetch secrets and config maps from the func's ns.
// similarly if executorType changed to Pool Manager, we now need a rolebinding in the func ns for fetcher sa
// present in env ns because for newdeploy, the fetcher sa is in function namespace
if envChanged || executorTypeChangedToPM {
envNs := fissionfnNamespace
if newFunc.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = newFunc.Spec.Environment.Namespace
}
err := utils.SetupRoleBinding(gpm.logger, kubernetesClient, fv1.SecretConfigMapGetterRB,
newFunc.ObjectMeta.Namespace, fv1.SecretConfigMapGetterCR, fv1.ClusterRole,
fv1.FissionFetcherSA, envNs)
if err != nil {
gpm.logger.Error("error creating rolebinding", zap.Error(err), zap.String("role_binding", fv1.SecretConfigMapGetterRB))
} else {
gpm.logger.Debug("successfully set up rolebinding for fetcher service account for function",
zap.String("service_account", fv1.FissionFetcherSA),
zap.String("service_account_namepsace", envNs),
zap.String("function_name", newFunc.ObjectMeta.Name),
zap.String("function_namespace", newFunc.ObjectMeta.Namespace))
}
}
},
})
return funcStore, controller
}
+56 -34
View File
@@ -28,7 +28,6 @@ import (
"time" "time"
"github.com/dchest/uniuri" "github.com/dchest/uniuri"
"github.com/fission/fission/pkg/utils"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
@@ -50,6 +49,8 @@ import (
"github.com/fission/fission/pkg/executor/util" "github.com/fission/fission/pkg/executor/util"
fetcherClient "github.com/fission/fission/pkg/fetcher/client" fetcherClient "github.com/fission/fission/pkg/fetcher/client"
fetcherConfig "github.com/fission/fission/pkg/fetcher/config" fetcherConfig "github.com/fission/fission/pkg/fetcher/config"
"github.com/fission/fission/pkg/utils"
"github.com/fission/fission/pkg/utils/maps"
) )
type ( type (
@@ -71,8 +72,7 @@ type (
fissionClient *crd.FissionClient fissionClient *crd.FissionClient
fetcherConfig *fetcherConfig.Config fetcherConfig *fetcherConfig.Config
stopReadyPodControllerCh chan struct{} stopReadyPodControllerCh chan struct{}
readyPodController cache.Controller readyPodInformer cache.SharedIndexInformer
readyPodIndexer cache.Indexer
readyPodQueue workqueue.DelayingInterface readyPodQueue workqueue.DelayingInterface
poolInstanceID string // small random string to uniquify pod names poolInstanceID string // small random string to uniquify pod names
instanceID string // poolmgr instance id instanceID string // poolmgr instance id
@@ -156,47 +156,73 @@ func MakeGenericPool(
} }
func (gp *GenericPool) getEnvironmentPoolLabels() map[string]string { func (gp *GenericPool) getEnvironmentPoolLabels() map[string]string {
return map[string]string{ envLabels := maps.CopyStringMap(gp.env.ObjectMeta.Labels)
fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypePoolmgr), envLabels[fv1.EXECUTOR_TYPE] = string(fv1.ExecutorTypePoolmgr)
fv1.ENVIRONMENT_NAME: gp.env.ObjectMeta.Name, envLabels[fv1.ENVIRONMENT_NAME] = gp.env.ObjectMeta.Name
fv1.ENVIRONMENT_NAMESPACE: gp.env.ObjectMeta.Namespace, envLabels[fv1.ENVIRONMENT_NAMESPACE] = gp.env.ObjectMeta.Namespace
fv1.ENVIRONMENT_UID: string(gp.env.ObjectMeta.UID), envLabels[fv1.ENVIRONMENT_UID] = string(gp.env.ObjectMeta.UID)
"managed": "true", // this allows us to easily find pods managed by the deployment envLabels["managed"] = "true" // this allows us to easily find pods managed by the deployment
} return envLabels
} }
func (gp *GenericPool) getDeployAnnotations() map[string]string { func (gp *GenericPool) getDeployAnnotations() map[string]string {
return map[string]string{ deployAnnotations := maps.CopyStringMap(gp.env.Annotations)
fv1.EXECUTOR_INSTANCEID_LABEL: gp.instanceID, deployAnnotations[fv1.EXECUTOR_INSTANCEID_LABEL] = gp.instanceID
return deployAnnotations
}
func (gp *GenericPool) checkMetricsApi() bool {
apiGroups, err := gp.metricsClient.DiscoveryClient.ServerGroups()
if err != nil {
gp.logger.Error("faied to discover API groups", zap.Error(err))
return false
} }
return utils.SupportedMetricsAPIVersionAvailable(apiGroups)
} }
func (gp *GenericPool) updateCPUUtilizationSvc() { func (gp *GenericPool) updateCPUUtilizationSvc() {
for { var metricsApiAvailabe bool
checkDuration := 30
if !gp.checkMetricsApi() {
checkDuration = 180
gp.logger.Warn("Metrics API not available")
}
serviceFunc := func() {
podMetricsList, err := gp.metricsClient.MetricsV1beta1().PodMetricses(gp.namespace).List(context.TODO(), metav1.ListOptions{ podMetricsList, err := gp.metricsClient.MetricsV1beta1().PodMetricses(gp.namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: "managed=false", LabelSelector: "managed=false",
}) })
if err != nil { if err != nil {
gp.logger.Error("failed to fetch pod metrics list", zap.Error(err)) gp.logger.Error("failed to fetch pod metrics list", zap.Error(err))
} else { return
gp.logger.Debug("pods found", zap.Any("length", len(podMetricsList.Items))) }
for _, val := range podMetricsList.Items { gp.logger.Debug("pods found", zap.Any("length", len(podMetricsList.Items)))
p, _ := resource.ParseQuantity("0m") for _, val := range podMetricsList.Items {
for _, container := range val.Containers { p, _ := resource.ParseQuantity("0m")
p.Add(container.Usage["cpu"]) for _, container := range val.Containers {
} p.Add(container.Usage["cpu"])
if value, ok := gp.podFSVCMap.Load(val.ObjectMeta.Name); ok { }
if valArray, ok1 := value.([]interface{}); ok1 { if value, ok := gp.podFSVCMap.Load(val.ObjectMeta.Name); ok {
function, address := valArray[0], valArray[1] if valArray, ok1 := value.([]interface{}); ok1 {
gp.fsCache.SetCPUUtilizaton(function.(string), address.(string), p) function, address := valArray[0], valArray[1]
gp.logger.Info(fmt.Sprintf("updated function %s, address %s, cpuUsage %+v", function.(string), address.(string), p)) gp.fsCache.SetCPUUtilizaton(function.(string), address.(string), p)
} gp.logger.Info(fmt.Sprintf("updated function %s, address %s, cpuUsage %+v", function.(string), address.(string), p))
} }
} }
} }
}
time.Sleep(30 * time.Second) for {
if metricsApiAvailabe {
serviceFunc()
} else {
if gp.checkMetricsApi() {
metricsApiAvailabe = true
checkDuration = 30
}
}
time.Sleep(time.Duration(checkDuration) * time.Second)
} }
} }
@@ -223,7 +249,7 @@ func (gp *GenericPool) choosePod(newLabels map[string]string) (string, *apiv1.Po
key = item.(string) key = item.(string)
gp.logger.Debug("got key from the queue", zap.String("key", key)) gp.logger.Debug("got key from the queue", zap.String("key", key))
obj, exists, err := gp.readyPodIndexer.GetByKey(key) obj, exists, err := gp.readyPodInformer.GetIndexer().GetByKey(key)
if err != nil { if err != nil {
gp.logger.Error("fetching object from store failed", zap.String("key", key), zap.Error(err)) gp.logger.Error("fetching object from store failed", zap.String("key", key), zap.Error(err))
return "", nil, err return "", nil, err
@@ -688,11 +714,7 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
Atime: time.Now(), Atime: time.Now(),
} }
if gp.fsCache.PodToFsvc == nil { gp.fsCache.PodToFsvc.Store(pod.GetObjectMeta().GetName(), fsvc)
gp.fsCache.PodToFsvc = make(map[string]*fscache.FuncSvc)
}
gp.fsCache.PodToFsvc[pod.GetObjectMeta().GetName()] = fsvc
gp.podFSVCMap.Store(pod.ObjectMeta.Name, []interface{}{crd.CacheKey(fsvc.Function), fsvc.Address}) gp.podFSVCMap.Store(pod.ObjectMeta.Name, []interface{}{crd.CacheKey(fsvc.Function), fsvc.Address})
gp.fsCache.AddFunc(*fsvc) gp.fsCache.AddFunc(*fsvc)
+29 -20
View File
@@ -76,11 +76,10 @@ type (
enableIstio bool enableIstio bool
fetcherConfig *fetcherConfig.Config fetcherConfig *fetcherConfig.Config
funcStore k8sCache.Store funcInformer *k8sCache.SharedIndexInformer
funcController k8sCache.Controller pkgInformer *k8sCache.SharedIndexInformer
pkgStore k8sCache.Store
pkgController k8sCache.Controller podInformer k8sCache.SharedIndexInformer
podInformer k8sCache.SharedIndexInformer
defaultIdlePodReapTime time.Duration defaultIdlePodReapTime time.Duration
} }
@@ -103,7 +102,10 @@ func MakeGenericPoolManager(
metricsClient *metricsclient.Clientset, metricsClient *metricsclient.Clientset,
functionNamespace string, functionNamespace string,
fetcherConfig *fetcherConfig.Config, fetcherConfig *fetcherConfig.Config,
instanceID string) (executortype.ExecutorType, error) { instanceID string,
funcInformer *k8sCache.SharedIndexInformer,
pkgInformer *k8sCache.SharedIndexInformer,
) (executortype.ExecutorType, error) {
gpmLogger := logger.Named("generic_pool_manager") gpmLogger := logger.Named("generic_pool_manager")
@@ -120,6 +122,8 @@ func MakeGenericPoolManager(
requestChannel: make(chan *request), requestChannel: make(chan *request),
defaultIdlePodReapTime: 2 * time.Minute, defaultIdlePodReapTime: 2 * time.Minute,
fetcherConfig: fetcherConfig, fetcherConfig: fetcherConfig,
funcInformer: funcInformer,
pkgInformer: pkgInformer,
} }
go gpm.service() go gpm.service()
@@ -132,16 +136,14 @@ func MakeGenericPoolManager(
gpm.enableIstio = istio gpm.enableIstio = istio
} }
gpm.funcStore, gpm.funcController = gpm.makeFuncController( (*gpm.funcInformer).AddEventHandler(gpm.FunctionEventHandlers(gpm.kubernetesClient, gpm.namespace, gpm.enableIstio))
gpm.fissionClient, gpm.kubernetesClient, gpm.namespace, gpm.enableIstio) (*gpm.pkgInformer).AddEventHandler(gpm.PackageEventHandlers(gpm.kubernetesClient, gpm.namespace))
gpm.pkgStore, gpm.pkgController = gpm.makePkgController(gpm.fissionClient, gpm.kubernetesClient, gpm.namespace) kubeInformerFactory, err := utils.GetInformerFactoryByExecutor(gpm.kubernetesClient, fv1.ExecutorTypePoolmgr)
informerFactory, err := utils.GetInformerFacoryByExecutor(gpm.kubernetesClient, fv1.ExecutorTypePoolmgr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
gpm.podInformer = informerFactory.Core().V1().Pods().Informer() gpm.podInformer = kubeInformerFactory.Core().V1().Pods().Informer()
return gpm, nil return gpm, nil
} }
@@ -149,8 +151,6 @@ func (gpm *GenericPoolManager) Run(ctx context.Context) {
// eagerPoolCreator must run after CleanupOldExecutorObjects. // eagerPoolCreator must run after CleanupOldExecutorObjects.
// Otherwise, the poolmanager may wrongly delete the deployment. // Otherwise, the poolmanager may wrongly delete the deployment.
go gpm.eagerPoolCreator() go gpm.eagerPoolCreator()
go gpm.funcController.Run(ctx.Done())
go gpm.pkgController.Run(ctx.Done())
go gpm.podInformer.Run(ctx.Done()) go gpm.podInformer.Run(ctx.Done())
go gpm.idleObjectReaper() go gpm.idleObjectReaper()
} }
@@ -211,7 +211,7 @@ func (gpm *GenericPoolManager) getPodInfo(obj apiv1.ObjectReference) (*apiv1.Pod
} }
if err != nil || !exists { if err != nil || !exists {
gpm.logger.Debug("Falling back to getting pod info from k8s API -- this may cause performace issues for your function.") gpm.logger.Debug("Falling back to getting pod info from k8s API -- this may cause performance issues for your function.")
pod, err := gpm.kubernetesClient.CoreV1().Pods(obj.Namespace).Get(context.TODO(), obj.Name, metav1.GetOptions{}) pod, err := gpm.kubernetesClient.CoreV1().Pods(obj.Namespace).Get(context.TODO(), obj.Name, metav1.GetOptions{})
return pod, err return pod, err
} }
@@ -644,7 +644,7 @@ func (gpm *GenericPoolManager) idleObjectReaper() {
continue continue
} }
if _, ok := gpm.fsCache.WebsocketFsvc[fsvc.Name]; ok { if _, ok := gpm.fsCache.WebsocketFsvc.Load(fsvc.Name); ok {
continue continue
} }
// For function with the environment that no longer exists, executor // For function with the environment that no longer exists, executor
@@ -725,8 +725,13 @@ func (gpm *GenericPoolManager) WebsocketStartEventChecker(kubeClient *kubernetes
zap.String("Pod name", mObj.GetName())) zap.String("Pod name", mObj.GetName()))
podName := strings.SplitAfter(mObj.GetName(), ".") podName := strings.SplitAfter(mObj.GetName(), ".")
if fsvc, ok := gpm.fsCache.PodToFsvc[strings.TrimSuffix(podName[0], ".")]; ok { if fsvc, ok := gpm.fsCache.PodToFsvc.Load(strings.TrimSuffix(podName[0], ".")); ok {
gpm.fsCache.WebsocketFsvc[fsvc.Name] = true fsvc, ok := fsvc.(*fscache.FuncSvc)
if !ok {
gpm.logger.Error("could not covert item from PodToFsvc")
return
}
gpm.fsCache.WebsocketFsvc.Store(fsvc.Name, true)
} }
}, },
}) })
@@ -761,8 +766,12 @@ func (gpm *GenericPoolManager) NoActiveConnectionEventChecker(kubeClient *kubern
zap.String("Pod name", mObj.GetName())) zap.String("Pod name", mObj.GetName()))
podName := strings.SplitAfter(mObj.GetName(), ".") podName := strings.SplitAfter(mObj.GetName(), ".")
if fsvc, ok := gpm.fsCache.PodToFsvc[strings.TrimSuffix(podName[0], ".")]; ok { if fsvc, ok := gpm.fsCache.PodToFsvc.Load(strings.TrimSuffix(podName[0], ".")); ok {
fsvc, ok := fsvc.(*fscache.FuncSvc)
if !ok {
gpm.logger.Error("could not covert value from PodToFsvc")
return
}
gpm.fsCache.DeleteFunctionSvc(fsvc) gpm.fsCache.DeleteFunctionSvc(fsvc)
for i := range fsvc.KubernetesObjects { for i := range fsvc.KubernetesObjects {
gpm.logger.Info("release idle function resources due to inactivity", gpm.logger.Info("release idle function resources due to inactivity",
@@ -0,0 +1,99 @@
/*
Copyright 2018 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package poolmgr
import (
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/utils"
)
func (gpm *GenericPoolManager) PackageEventHandlers(kubernetesClient *kubernetes.Clientset, fissionfnNamespace string) k8sCache.ResourceEventHandlerFuncs {
return k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
pkg := obj.(*fv1.Package)
gpm.logger.Debug("list watch for package reported a new package addition",
zap.String("package_name", pkg.ObjectMeta.Name),
zap.String("package_namespace", pkg.ObjectMeta.Namespace))
// create or update role-binding for fetcher sa in env ns to be able to get the pkg contents from pkg namespace
envNs := fissionfnNamespace
if pkg.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = pkg.Spec.Environment.Namespace
}
// here, we return if we hit an error during rolebinding setup. this is because this rolebinding is mandatory for
// every function's package to be loaded into its env. without that, there's no point to move forward.
err := utils.SetupRoleBinding(gpm.logger, kubernetesClient, fv1.PackageGetterRB, pkg.ObjectMeta.Namespace, fv1.PackageGetterCR, fv1.ClusterRole, fv1.FissionFetcherSA, envNs)
if err != nil {
gpm.logger.Error("error creating rolebinding for package",
zap.Error(err),
zap.String("role_binding", fv1.PackageGetterRB),
zap.String("package_name", pkg.ObjectMeta.Name),
zap.String("package_namespace", pkg.ObjectMeta.Namespace))
return
}
gpm.logger.Debug("successfully set up rolebinding for fetcher service account",
zap.String("service_account", fv1.FissionFetcherSA),
zap.String("service_account_namespace", envNs),
zap.String("package_name", pkg.ObjectMeta.Name),
zap.String("package_namespace", pkg.ObjectMeta.Namespace))
},
UpdateFunc: func(oldObj, newObj interface{}) {
oldPkg := oldObj.(*fv1.Package)
newPkg := newObj.(*fv1.Package)
if oldPkg.ObjectMeta.ResourceVersion == newPkg.ObjectMeta.ResourceVersion {
return
}
// if a pkg's env reference gets updated and the newly referenced env is in a different ns,
// we need to update the role-binding in pkg ns to grant permissions to the fetcher-sa in env ns
// to do a get on pkg
if oldPkg.Spec.Environment.Namespace != newPkg.Spec.Environment.Namespace {
envNs := fissionfnNamespace
if newPkg.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = newPkg.Spec.Environment.Namespace
}
err := utils.SetupRoleBinding(gpm.logger, kubernetesClient, fv1.PackageGetterRB,
newPkg.ObjectMeta.Namespace, fv1.PackageGetterCR, fv1.ClusterRole,
fv1.FissionFetcherSA, envNs)
if err != nil {
gpm.logger.Error("error updating rolebinding for package",
zap.Error(err),
zap.String("role_binding", fv1.PackageGetterRB),
zap.String("package_name", newPkg.ObjectMeta.Name),
zap.String("package_namespace", newPkg.ObjectMeta.Namespace))
return
}
gpm.logger.Debug("successfully updated rolebinding for fetcher service account",
zap.String("service_account", fv1.FissionFetcherSA),
zap.String("service_account_namespace", envNs),
zap.String("package_name", newPkg.ObjectMeta.Name),
zap.String("package_namespace", newPkg.ObjectMeta.Namespace))
}
},
}
}
@@ -1,111 +0,0 @@
/*
Copyright 2018 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package poolmgr
import (
"time"
"github.com/fission/fission/pkg/utils"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/crd"
)
// TODO : It may make sense to make each of add, update, delete funcs run as separate go routines.
func (gpm *GenericPoolManager) makePkgController(fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset, fissionfnNamespace string) (k8sCache.Store, k8sCache.Controller) {
resyncPeriod := 30 * time.Second
lw := k8sCache.NewListWatchFromClient(fissionClient.CoreV1().RESTClient(), "packages", metav1.NamespaceAll, fields.Everything())
pkgStore, controller := k8sCache.NewInformer(lw, &fv1.Package{}, resyncPeriod,
k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
pkg := obj.(*fv1.Package)
gpm.logger.Debug("list watch for package reported a new package addition",
zap.String("package_name", pkg.ObjectMeta.Name),
zap.String("package_namespace", pkg.ObjectMeta.Namespace))
// create or update role-binding for fetcher sa in env ns to be able to get the pkg contents from pkg namespace
envNs := fissionfnNamespace
if pkg.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = pkg.Spec.Environment.Namespace
}
// here, we return if we hit an error during rolebinding setup. this is because this rolebinding is mandatory for
// every function's package to be loaded into its env. without that, there's no point to move forward.
err := utils.SetupRoleBinding(gpm.logger, kubernetesClient, fv1.PackageGetterRB, pkg.ObjectMeta.Namespace, fv1.PackageGetterCR, fv1.ClusterRole, fv1.FissionFetcherSA, envNs)
if err != nil {
gpm.logger.Error("error creating rolebinding for package",
zap.Error(err),
zap.String("role_binding", fv1.PackageGetterRB),
zap.String("package_name", pkg.ObjectMeta.Name),
zap.String("package_namespace", pkg.ObjectMeta.Namespace))
return
}
gpm.logger.Debug("successfully set up rolebinding for fetcher service account",
zap.String("service_account", fv1.FissionFetcherSA),
zap.String("service_account_namespace", envNs),
zap.String("package_name", pkg.ObjectMeta.Name),
zap.String("package_namespace", pkg.ObjectMeta.Namespace))
},
UpdateFunc: func(oldObj, newObj interface{}) {
oldPkg := oldObj.(*fv1.Package)
newPkg := newObj.(*fv1.Package)
if oldPkg.ObjectMeta.ResourceVersion == newPkg.ObjectMeta.ResourceVersion {
return
}
// if a pkg's env reference gets updated and the newly referenced env is in a different ns,
// we need to update the role-binding in pkg ns to grant permissions to the fetcher-sa in env ns
// to do a get on pkg
if oldPkg.Spec.Environment.Namespace != newPkg.Spec.Environment.Namespace {
envNs := fissionfnNamespace
if newPkg.Spec.Environment.Namespace != metav1.NamespaceDefault {
envNs = newPkg.Spec.Environment.Namespace
}
err := utils.SetupRoleBinding(gpm.logger, kubernetesClient, fv1.PackageGetterRB,
newPkg.ObjectMeta.Namespace, fv1.PackageGetterCR, fv1.ClusterRole,
fv1.FissionFetcherSA, envNs)
if err != nil {
gpm.logger.Error("error updating rolebinding for package",
zap.Error(err),
zap.String("role_binding", fv1.PackageGetterRB),
zap.String("package_name", newPkg.ObjectMeta.Name),
zap.String("package_namespace", newPkg.ObjectMeta.Namespace))
return
}
gpm.logger.Debug("successfully updated rolebinding for fetcher service account",
zap.String("service_account", fv1.FissionFetcherSA),
zap.String("service_account_namespace", envNs),
zap.String("package_name", newPkg.ObjectMeta.Name),
zap.String("package_namespace", newPkg.ObjectMeta.Namespace))
}
},
})
return pkgStore, controller
}
@@ -4,27 +4,31 @@ import (
"time" "time"
"go.uber.org/zap" "go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
) )
func (gp *GenericPool) newPodInformer() cache.SharedIndexInformer {
optionsModifier := func(options *metav1.ListOptions) {
options.LabelSelector = labels.Set(
gp.deployment.Spec.Selector.MatchLabels).AsSelector().String()
options.FieldSelector = "status.phase=Running"
}
return informers.NewFilteredPodInformer(gp.kubernetesClient, gp.namespace, 0, nil, optionsModifier)
}
func (gp *GenericPool) startReadyPodController() { func (gp *GenericPool) startReadyPodController() {
// create the pod watcher to filter by labels // create the pod watcher to filter by labels
// Filtering pod by phase=Running. In some cases the pod can be in // Filtering pod by phase=Running. In some cases the pod can be in
// different state than Running, for example Kubernetes sets a // different state than Running, for example Kubernetes sets a
// pod to Termination while k8s waits for the grace period of // pod to Termination while k8s waits for the grace period of
// the pod, even if all the containers are in Ready state. // the pod, even if all the containers are in Ready state.
optionsModifier := func(options *metav1.ListOptions) {
options.LabelSelector = labels.Set(
gp.deployment.Spec.Selector.MatchLabels).AsSelector().String()
options.FieldSelector = "status.phase=Running"
}
readyPodWatcher := cache.NewFilteredListWatchFromClient(gp.kubernetesClient.CoreV1().RESTClient(), "pods", gp.namespace, optionsModifier)
gp.readyPodQueue = workqueue.NewDelayingQueue() gp.readyPodQueue = workqueue.NewDelayingQueue()
gp.readyPodIndexer, gp.readyPodController = cache.NewIndexerInformer(readyPodWatcher, &apiv1.Pod{}, 0, cache.ResourceEventHandlerFuncs{ gp.readyPodInformer = gp.newPodInformer()
gp.readyPodInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { AddFunc: func(obj interface{}) {
key, err := cache.MetaNamespaceKeyFunc(obj) key, err := cache.MetaNamespaceKeyFunc(obj)
if err == nil { if err == nil {
@@ -39,7 +43,7 @@ func (gp *GenericPool) startReadyPodController() {
gp.logger.Debug("delete func called", zap.String("key", key)) gp.logger.Debug("delete func called", zap.String("key", key))
} }
}, },
}, cache.Indexers{}) })
go gp.readyPodController.Run(gp.stopReadyPodControllerCh) go gp.readyPodInformer.Run(gp.stopReadyPodControllerCh)
gp.logger.Info("readyPod controller started", zap.String("env", gp.env.ObjectMeta.Name), zap.String("envID", string(gp.env.ObjectMeta.UID))) gp.logger.Info("readyPod controller started", zap.String("env", gp.env.ObjectMeta.Name), zap.String("envID", string(gp.env.ObjectMeta.UID)))
} }
+3 -4
View File
@@ -18,6 +18,7 @@ package fscache
import ( import (
"fmt" "fmt"
"sync"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -68,8 +69,8 @@ type (
byAddress *cache.Cache // address -> function : map[string]metav1.ObjectMeta byAddress *cache.Cache // address -> function : map[string]metav1.ObjectMeta
byFunctionUID *cache.Cache // function uid -> function : map[string]metav1.ObjectMeta byFunctionUID *cache.Cache // function uid -> function : map[string]metav1.ObjectMeta
connFunctionCache *poolcache.Cache // function-key -> funcSvc : map[string]*funcSvc connFunctionCache *poolcache.Cache // function-key -> funcSvc : map[string]*funcSvc
PodToFsvc map[string]*FuncSvc PodToFsvc sync.Map // pod-name -> funcSvc: map[string]*FuncSvc
WebsocketFsvc map[string]bool WebsocketFsvc sync.Map // funcSvc-name -> bool: map[string]bool
requestChannel chan *fscRequest requestChannel chan *fscRequest
} }
@@ -111,8 +112,6 @@ func MakeFunctionServiceCache(logger *zap.Logger) *FunctionServiceCache {
byFunctionUID: cache.MakeCache(0, 0), byFunctionUID: cache.MakeCache(0, 0),
connFunctionCache: poolcache.NewPoolCache(), connFunctionCache: poolcache.NewPoolCache(),
requestChannel: make(chan *fscRequest), requestChannel: make(chan *fscRequest),
PodToFsvc: make(map[string]*FuncSvc),
WebsocketFsvc: make(map[string]bool),
} }
go fsc.service() go fsc.service()
return fsc return fsc
+1 -1
View File
@@ -88,7 +88,7 @@ func (fsc *FunctionServiceCache) setFuncAlive(funcname, funcuid string, isAlive
// ReapTime is the amount of time taken to reap a pod // ReapTime is the amount of time taken to reap a pod
func (fsc *FunctionServiceCache) ReapTime(funcName, funcAddress string, time float64) { func (fsc *FunctionServiceCache) ReapTime(funcName, funcAddress string, time float64) {
funcReapTime.WithLabelValues(funcName, funcAddress).Observe(float64(time)) funcReapTime.WithLabelValues(funcName, funcAddress).Observe(time)
} }
// IdleTime is the amount of time it took Reaper to find out the pod was idle // IdleTime is the amount of time it took Reaper to find out the pod was idle
+64
View File
@@ -17,10 +17,16 @@ limitations under the License.
package util package util
import ( import (
"context"
"errors"
"sync" "sync"
"time" "time"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
) )
// ApplyImagePullSecret applies image pull secret to the give pod spec. // ApplyImagePullSecret applies image pull secret to the give pod spec.
@@ -49,3 +55,61 @@ func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) {
case <-time.After(timeout): case <-time.After(timeout):
} }
} }
// ConvertConfigSecrets returns envFromSource which can be passed directly into the pod spec
func ConvertConfigSecrets(fn *fv1.Function, kc *kubernetes.Clientset) ([]apiv1.EnvFromSource, error) {
cmList := fn.Spec.ConfigMaps
secList := fn.Spec.Secrets
cmEnvSources := make([]*apiv1.ConfigMapEnvSource, 0)
secEnvSources := make([]*apiv1.SecretEnvSource, 0)
for _, cm := range cmList {
if cm.Namespace != fn.Namespace {
return nil, errors.New("Function should not reference config map of different namespace")
}
_, err := kc.CoreV1().ConfigMaps(cm.Namespace).Get(context.TODO(), cm.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
cmEnvSource := &apiv1.ConfigMapEnvSource{
LocalObjectReference: apiv1.LocalObjectReference{Name: cm.Name},
}
cmEnvSources = append(cmEnvSources, cmEnvSource)
}
for _, sec := range secList {
if sec.Namespace != fn.Namespace {
return nil, errors.New("Function should not reference secret of different namespace")
}
_, err := kc.CoreV1().Secrets(sec.Namespace).Get(context.TODO(), sec.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
secEnvSource := &apiv1.SecretEnvSource{
LocalObjectReference: apiv1.LocalObjectReference{Name: sec.Name},
}
secEnvSources = append(secEnvSources, secEnvSource)
}
envFromSources := make([]apiv1.EnvFromSource, 0)
for _, cmEnvSource := range cmEnvSources {
envFromSource := apiv1.EnvFromSource{
ConfigMapRef: cmEnvSource,
}
envFromSources = append(envFromSources, envFromSource)
}
for _, secEnvSource := range secEnvSources {
envFromSource := apiv1.EnvFromSource{
SecretRef: secEnvSource,
}
envFromSources = append(envFromSources, envFromSource)
}
return envFromSources, nil
}
+9 -4
View File
@@ -31,10 +31,13 @@ func Commands() *cobra.Command {
} }
wrapper.SetFlags(createCmd, flag.FlagSet{ wrapper.SetFlags(createCmd, flag.FlagSet{
Required: []flag.Flag{flag.EnvName, flag.EnvImage}, Required: []flag.Flag{flag.EnvName, flag.EnvImage},
Optional: []flag.Flag{flag.EnvPoolsize, flag.EnvBuilderImage, flag.EnvBuildCmd, Optional: []flag.Flag{
flag.EnvPoolsize, flag.EnvBuilderImage, flag.EnvBuildCmd,
flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory, flag.RunTimeMaxMemory, flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory, flag.RunTimeMaxMemory,
flag.EnvTerminationGracePeriod, flag.EnvVersion, flag.EnvImagePullSecret, flag.EnvTerminationGracePeriod, flag.EnvVersion, flag.EnvImagePullSecret, flag.EnvKeepArchive,
flag.EnvExternalNetwork, flag.EnvKeepArchive, flag.NamespaceEnvironment, flag.SpecSave, flag.SpecDry}, flag.NamespaceEnvironment, flag.EnvExternalNetwork,
flag.Labels, flag.Annotation,
flag.SpecSave, flag.SpecDry},
}) })
getCmd := &cobra.Command{ getCmd := &cobra.Command{
@@ -57,7 +60,9 @@ func Commands() *cobra.Command {
Optional: []flag.Flag{flag.EnvImage, flag.EnvPoolsize, Optional: []flag.Flag{flag.EnvImage, flag.EnvPoolsize,
flag.EnvBuilderImage, flag.EnvBuildCmd, flag.EnvImagePullSecret, flag.EnvBuilderImage, flag.EnvBuildCmd, flag.EnvImagePullSecret,
flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory, flag.RunTimeMaxMemory, flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory, flag.RunTimeMaxMemory,
flag.EnvTerminationGracePeriod, flag.EnvKeepArchive, flag.NamespaceEnvironment, flag.EnvExternalNetwork}, flag.EnvTerminationGracePeriod, flag.EnvKeepArchive,
flag.NamespaceEnvironment, flag.EnvExternalNetwork,
flag.Labels, flag.Annotation},
}) })
deleteCmd := &cobra.Command{ deleteCmd := &cobra.Command{
+5 -1
View File
@@ -129,7 +129,7 @@ func createEnvironmentFromCmd(input cli.Input) (*fv1.Environment, error) {
poolsize := input.Int(flagkey.EnvPoolsize) poolsize := input.Int(flagkey.EnvPoolsize)
if poolsize < 1 { if poolsize < 1 {
console.Warn("poolsize is not positive, if you are using pool manager please set postive value") console.Warn("poolsize is not positive, if you are using pool manager please set positive value")
} }
envBuilderImg := input.String(flagkey.EnvBuilderImage) envBuilderImg := input.String(flagkey.EnvBuilderImage)
@@ -179,6 +179,10 @@ func createEnvironmentFromCmd(input cli.Input) (*fv1.Environment, error) {
}, },
} }
err = util.ApplyLabelsAndAnnotations(input, &env.ObjectMeta)
if err != nil {
return nil, err
}
err = env.Validate() err = env.Validate()
if err != nil { if err != nil {
return nil, fv1.AggregateValidationErrors("Environment", err) return nil, fv1.AggregateValidationErrors("Environment", err)
+7 -1
View File
@@ -31,6 +31,7 @@ import (
"github.com/fission/fission/pkg/fission-cli/cmd" "github.com/fission/fission/pkg/fission-cli/cmd"
"github.com/fission/fission/pkg/fission-cli/console" "github.com/fission/fission/pkg/fission-cli/console"
flagkey "github.com/fission/fission/pkg/fission-cli/flag/key" flagkey "github.com/fission/fission/pkg/fission-cli/flag/key"
"github.com/fission/fission/pkg/fission-cli/util"
"github.com/fission/fission/pkg/utils" "github.com/fission/fission/pkg/utils"
) )
@@ -66,6 +67,11 @@ func (opts *UpdateSubCommand) complete(input cli.Input) error {
} }
opts.env = env opts.env = env
err = util.ApplyLabelsAndAnnotations(input, &opts.env.ObjectMeta)
if err != nil {
return err
}
return nil return nil
} }
@@ -105,7 +111,7 @@ func updateExistingEnvironmentWithCmd(env *fv1.Environment, input cli.Input) (*f
if input.IsSet(flagkey.EnvPoolsize) { if input.IsSet(flagkey.EnvPoolsize) {
env.Spec.Poolsize = input.Int(flagkey.EnvPoolsize) env.Spec.Poolsize = input.Int(flagkey.EnvPoolsize)
if env.Spec.Poolsize < 1 { if env.Spec.Poolsize < 1 {
console.Warn("poolsize is not positive, if you are using pool manager please set postive value") console.Warn("poolsize is not positive, if you are using pool manager please set positive value")
} }
} }
+51 -4
View File
@@ -36,7 +36,7 @@ func Commands() *cobra.Command {
flag.FnExecutorType, flag.FnCfgMap, flag.FnSecret, flag.FnExecutorType, flag.FnCfgMap, flag.FnSecret,
flag.FnSpecializationTimeout, flag.FnExecutionTimeout, flag.FnSpecializationTimeout, flag.FnExecutionTimeout,
flag.FnIdleTimeout, flag.FnConcurrency, flag.FnRequestsPerPod, flag.FnIdleTimeout, flag.FnConcurrency, flag.FnRequestsPerPod,
flag.FnOnceOnly, flag.FnOnceOnly, flag.Labels, flag.Annotation,
// TODO retired pkg & trigger related flags from function cmd // TODO retired pkg & trigger related flags from function cmd
flag.PkgCode, flag.PkgSrcArchive, flag.PkgDeployArchive, flag.PkgCode, flag.PkgSrcArchive, flag.PkgDeployArchive,
@@ -88,7 +88,7 @@ func Commands() *cobra.Command {
flag.FnExecutorType, flag.FnSecret, flag.FnCfgMap, flag.FnExecutorType, flag.FnSecret, flag.FnCfgMap,
flag.FnSpecializationTimeout, flag.FnExecutionTimeout, flag.FnSpecializationTimeout, flag.FnExecutionTimeout,
flag.FnIdleTimeout, flag.FnConcurrency, flag.FnRequestsPerPod, flag.FnIdleTimeout, flag.FnConcurrency, flag.FnRequestsPerPod,
flag.FnOnceOnly, flag.FnOnceOnly, flag.Labels, flag.Annotation,
flag.PkgCode, flag.PkgSrcArchive, flag.PkgDeployArchive, flag.PkgCode, flag.PkgSrcArchive, flag.PkgDeployArchive,
flag.PkgSrcChecksum, flag.PkgDeployChecksum, flag.PkgInsecure, flag.PkgSrcChecksum, flag.PkgDeployChecksum, flag.PkgInsecure,
@@ -154,13 +154,60 @@ func Commands() *cobra.Command {
}, },
}) })
runContainerCmd := &cobra.Command{
Use: "run-container",
Aliases: []string{"runc"},
Short: "Alpha: Run a container image as a function",
RunE: wrapper.Wrapper(RunContainer),
}
wrapper.SetFlags(runContainerCmd, flag.FlagSet{
Required: []flag.Flag{flag.FnName, flag.FnImageName},
Optional: []flag.Flag{
flag.FnPort, flag.FnCommand, flag.FnArgs,
flag.FnCfgMap, flag.FnSecret,
flag.FnExecutionTimeout,
flag.FnIdleTimeout,
flag.Labels, flag.Annotation,
// flag for newdeploy to use.
flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory,
flag.RunTimeMaxMemory, flag.ReplicasMin,
flag.ReplicasMax, flag.RunTimeTargetCPU,
flag.NamespaceFunction, flag.SpecSave, flag.SpecDry,
},
})
updateContainerCmd := &cobra.Command{
Use: "update-container",
Aliases: []string{"updatec"},
Short: "Alpha: Update a function running a container",
RunE: wrapper.Wrapper(UpdateContainer),
}
wrapper.SetFlags(updateContainerCmd, flag.FlagSet{
Required: []flag.Flag{flag.FnName},
Optional: []flag.Flag{
flag.FnImageName, flag.FnPort,
flag.FnCommand, flag.FnArgs,
flag.FnSecret, flag.FnCfgMap,
flag.FnExecutionTimeout, flag.FnIdleTimeout,
flag.Labels, flag.Annotation,
flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory,
flag.RunTimeMaxMemory, flag.ReplicasMin, flag.ReplicasMax,
flag.RunTimeTargetCPU,
flag.NamespaceFunction, flag.SpecSave,
},
})
command := &cobra.Command{ command := &cobra.Command{
Use: "function", Use: "function",
Aliases: []string{"fn"}, Aliases: []string{"fn"},
Short: "Create, update and manage functions", Short: "Create, update and manage functions",
} }
command.AddCommand(createCmd, getCmd, getmetaCmd, updateCmd, deleteCmd, listCmd, logsCmd, testCmd,
command.AddCommand(createCmd, getCmd, getmetaCmd, updateCmd, deleteCmd, listCmd, logsCmd, testCmd) runContainerCmd, updateContainerCmd)
return command return command
} }
+40 -24
View File
@@ -285,18 +285,6 @@ func (opts *CreateSubCommand) complete(input cli.Input) error {
Namespace: fnNamespace, Namespace: fnNamespace,
}, },
Spec: fv1.FunctionSpec{ Spec: fv1.FunctionSpec{
Environment: fv1.EnvironmentReference{
Name: envName,
Namespace: envNamespace,
},
Package: fv1.FunctionPackageRef{
FunctionName: entrypoint,
PackageRef: fv1.PackageRef{
Namespace: pkgMetadata.Namespace,
Name: pkgMetadata.Name,
ResourceVersion: pkgMetadata.ResourceVersion,
},
},
Secrets: secrets, Secrets: secrets,
ConfigMaps: cfgmaps, ConfigMaps: cfgmaps,
Resources: *resourceReq, Resources: *resourceReq,
@@ -309,6 +297,23 @@ func (opts *CreateSubCommand) complete(input cli.Input) error {
}, },
} }
err = util.ApplyLabelsAndAnnotations(input, &opts.function.ObjectMeta)
if err != nil {
return err
}
opts.function.Spec.Environment = fv1.EnvironmentReference{
Name: envName,
Namespace: envNamespace,
}
opts.function.Spec.Package = fv1.FunctionPackageRef{
FunctionName: entrypoint,
PackageRef: fv1.PackageRef{
Namespace: pkgMetadata.Namespace,
Name: pkgMetadata.Name,
ResourceVersion: pkgMetadata.ResourceVersion,
},
}
return nil return nil
} }
@@ -387,13 +392,19 @@ func getInvokeStrategy(input cli.Input, existingInvokeStrategy *fv1.InvokeStrate
var es *fv1.ExecutionStrategy var es *fv1.ExecutionStrategy
if existingInvokeStrategy == nil { if existingInvokeStrategy == nil {
es, err = getExecutionStrategy(input) executorType, err := getExecutorType(input)
if err != nil {
return nil, err
}
es, err = getExecutionStrategy(executorType, input)
if err != nil {
return nil, err
}
} else { } else {
es, err = updateExecutionStrategy(input, &existingInvokeStrategy.ExecutionStrategy) es, err = updateExecutionStrategy(input, &existingInvokeStrategy.ExecutionStrategy)
} if err != nil {
return nil, err
if err != nil { }
return nil, err
} }
return &fv1.InvokeStrategy{ return &fv1.InvokeStrategy{
@@ -402,20 +413,23 @@ func getInvokeStrategy(input cli.Input, existingInvokeStrategy *fv1.InvokeStrate
}, nil }, nil
} }
func getExecutionStrategy(input cli.Input) (strategy *fv1.ExecutionStrategy, err error) { func getExecutorType(input cli.Input) (executorType fv1.ExecutorType, err error) {
var fnExecutor fv1.ExecutorType
switch input.String(flagkey.FnExecutorType) { switch input.String(flagkey.FnExecutorType) {
case "": case "":
fallthrough fallthrough
case string(fv1.ExecutorTypePoolmgr): case string(fv1.ExecutorTypePoolmgr):
fnExecutor = fv1.ExecutorTypePoolmgr executorType = fv1.ExecutorTypePoolmgr
case string(fv1.ExecutorTypeNewdeploy): case string(fv1.ExecutorTypeNewdeploy):
fnExecutor = fv1.ExecutorTypeNewdeploy executorType = fv1.ExecutorTypeNewdeploy
case string(fv1.ExecutorTypeContainer):
executorType = fv1.ExecutorTypeContainer
default: default:
return nil, errors.Errorf("executor type must be one of '%v' or '%v'", fv1.ExecutorTypePoolmgr, fv1.ExecutorTypeNewdeploy) err = errors.Errorf("executor type must be one of '%v', '%v' or '%v'", fv1.ExecutorTypePoolmgr, fv1.ExecutorTypeNewdeploy, fv1.ExecutorTypeContainer)
} }
return executorType, err
}
func getExecutionStrategy(fnExecutor fv1.ExecutorType, input cli.Input) (strategy *fv1.ExecutionStrategy, err error) {
specializationTimeout := fv1.DefaultSpecializationTimeOut specializationTimeout := fv1.DefaultSpecializationTimeOut
if input.IsSet(flagkey.FnSpecializationTimeout) { if input.IsSet(flagkey.FnSpecializationTimeout) {
@@ -490,8 +504,10 @@ func updateExecutionStrategy(input cli.Input, existingExecutionStrategy *fv1.Exe
fnExecutor = fv1.ExecutorTypePoolmgr fnExecutor = fv1.ExecutorTypePoolmgr
case string(fv1.ExecutorTypeNewdeploy): case string(fv1.ExecutorTypeNewdeploy):
fnExecutor = fv1.ExecutorTypeNewdeploy fnExecutor = fv1.ExecutorTypeNewdeploy
case string(fv1.ExecutorTypeContainer):
fnExecutor = fv1.ExecutorTypeContainer
default: default:
return nil, errors.Errorf("executor type must be one of '%v' or '%v'", fv1.ExecutorTypePoolmgr, fv1.ExecutorTypeNewdeploy) return nil, errors.Errorf("executor type must be one of '%v', %v or '%v'", fv1.ExecutorTypePoolmgr, fv1.ExecutorTypeNewdeploy, fv1.ExecutorTypeContainer)
} }
} }
@@ -0,0 +1,238 @@
/*
Copyright 2019 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package function
import (
"fmt"
"strings"
"github.com/pkg/errors"
apiv1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
ferror "github.com/fission/fission/pkg/error"
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
"github.com/fission/fission/pkg/fission-cli/cmd"
"github.com/fission/fission/pkg/fission-cli/cmd/spec"
"github.com/fission/fission/pkg/fission-cli/console"
flagkey "github.com/fission/fission/pkg/fission-cli/flag/key"
"github.com/fission/fission/pkg/fission-cli/util"
)
type RunContainerSubCommand struct {
cmd.CommandActioner
function *fv1.Function
specFile string
}
func RunContainer(input cli.Input) error {
return (&RunContainerSubCommand{}).do(input)
}
func (opts *RunContainerSubCommand) do(input cli.Input) error {
err := opts.complete(input)
if err != nil {
return err
}
return opts.run(input)
}
func (opts *RunContainerSubCommand) complete(input cli.Input) error {
fnName := input.String(flagkey.FnName)
fnNamespace := input.String(flagkey.NamespaceFunction)
// user wants a spec, create a yaml file with package and function
toSpec := false
if input.Bool(flagkey.SpecSave) {
toSpec = true
opts.specFile = fmt.Sprintf("function-%v.yaml", fnName)
}
if !toSpec {
// check for unique function names within a namespace
fn, err := opts.Client().V1().Function().Get(&metav1.ObjectMeta{
Name: input.String(flagkey.FnName),
Namespace: input.String(flagkey.NamespaceFunction),
})
if err != nil && !ferror.IsNotFound(err) {
return err
} else if fn != nil {
return errors.New("a function with the same name already exists")
}
}
fnTimeout := input.Int(flagkey.FnExecutionTimeout)
if fnTimeout <= 0 {
return errors.Errorf("--%v must be greater than 0", flagkey.FnExecutionTimeout)
}
fnIdleTimeout := input.Int(flagkey.FnIdleTimeout)
secretNames := input.StringSlice(flagkey.FnSecret)
cfgMapNames := input.StringSlice(flagkey.FnCfgMap)
es, err := getExecutionStrategy(fv1.ExecutorTypeContainer, input)
if err != nil {
return err
}
invokeStrategy := &fv1.InvokeStrategy{
ExecutionStrategy: *es,
StrategyType: fv1.StrategyTypeExecution,
}
resourceReq, err := util.GetResourceReqs(input, &apiv1.ResourceRequirements{})
if err != nil {
return err
}
var imageName string
var port int
var command, args string
imageName = input.String(flagkey.FnImageName)
if imageName == "" {
return errors.New("need --image argument")
}
port = input.Int(flagkey.FnPort)
command = input.String(flagkey.FnCommand)
args = input.String(flagkey.FnArgs)
var secrets []fv1.SecretReference
var cfgmaps []fv1.ConfigMapReference
if len(secretNames) > 0 {
// check the referenced secret is in the same ns as the function, if not give a warning.
if !toSpec { // TODO: workaround in order not to block users from creating function spec, remove it.
for _, secretName := range secretNames {
err := opts.Client().V1().Misc().SecretExists(&metav1.ObjectMeta{
Namespace: fnNamespace,
Name: secretName,
})
if err != nil {
if k8serrors.IsNotFound(err) {
console.Warn(fmt.Sprintf("Secret %s not found in Namespace: %s. Secret needs to be present in the same namespace as function", secretName, fnNamespace))
} else {
return errors.Wrapf(err, "error checking secret %s", secretName)
}
}
}
}
for _, secretName := range secretNames {
newSecret := fv1.SecretReference{
Name: secretName,
Namespace: fnNamespace,
}
secrets = append(secrets, newSecret)
}
}
if len(cfgMapNames) > 0 {
// check the referenced cfgmap is in the same ns as the function, if not give a warning.
if !toSpec {
for _, cfgMapName := range cfgMapNames {
err := opts.Client().V1().Misc().ConfigMapExists(&metav1.ObjectMeta{
Namespace: fnNamespace,
Name: cfgMapName,
})
if err != nil {
if k8serrors.IsNotFound(err) {
console.Warn(fmt.Sprintf("ConfigMap %s not found in Namespace: %s. ConfigMap needs to be present in the same namespace as function", cfgMapName, fnNamespace))
} else {
return errors.Wrapf(err, "error checking configmap %s", cfgMapName)
}
}
}
}
for _, cfgMapName := range cfgMapNames {
newCfgMap := fv1.ConfigMapReference{
Name: cfgMapName,
Namespace: fnNamespace,
}
cfgmaps = append(cfgmaps, newCfgMap)
}
}
opts.function = &fv1.Function{
ObjectMeta: metav1.ObjectMeta{
Name: fnName,
Namespace: fnNamespace,
},
Spec: fv1.FunctionSpec{
Secrets: secrets,
ConfigMaps: cfgmaps,
Resources: *resourceReq,
InvokeStrategy: *invokeStrategy,
FunctionTimeout: fnTimeout,
IdleTimeout: &fnIdleTimeout,
},
}
err = util.ApplyLabelsAndAnnotations(input, &opts.function.ObjectMeta)
if err != nil {
return err
}
container := &apiv1.Container{
Name: fnName,
Image: imageName,
Ports: []apiv1.ContainerPort{
{
Name: "http-env",
ContainerPort: int32(port),
},
},
}
if command != "" {
container.Command = strings.Split(command, " ")
}
if args != "" {
container.Args = strings.Split(args, " ")
}
opts.function.Spec.PodSpec = &apiv1.PodSpec{
Containers: []apiv1.Container{*container},
}
return nil
}
// run write the resource to a spec file or create a fission CRD with remote fission server.
// It also prints warning/error if necessary.
func (opts *RunContainerSubCommand) run(input cli.Input) error {
// if we're writing a spec, don't create the function
// save to spec file or display the spec to console
if input.Bool(flagkey.SpecDry) {
return spec.SpecDry(*opts.function)
}
if input.Bool(flagkey.SpecSave) {
err := spec.SpecSave(*opts.function, opts.specFile)
if err != nil {
return errors.Wrap(err, "error saving function spec")
}
return nil
}
_, err := opts.Client().V1().Function().Create(opts.function)
if err != nil {
return errors.Wrap(err, "error creating function")
}
fmt.Printf("function '%v' created\n", opts.function.ObjectMeta.Name)
return nil
}
+5
View File
@@ -243,6 +243,11 @@ func (opts *UpdateSubCommand) complete(input cli.Input) error {
opts.function = function opts.function = function
err = util.ApplyLabelsAndAnnotations(input, &opts.function.ObjectMeta)
if err != nil {
return err
}
return nil return nil
} }
@@ -0,0 +1,197 @@
/*
Copyright 2019 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package function
import (
"fmt"
"strings"
"github.com/pkg/errors"
apiv1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
"github.com/fission/fission/pkg/fission-cli/cmd"
"github.com/fission/fission/pkg/fission-cli/console"
flagkey "github.com/fission/fission/pkg/fission-cli/flag/key"
"github.com/fission/fission/pkg/fission-cli/util"
)
type UpdateContainerSubCommand struct {
cmd.CommandActioner
function *fv1.Function
}
func UpdateContainer(input cli.Input) error {
return (&UpdateContainerSubCommand{}).do(input)
}
func (opts *UpdateContainerSubCommand) do(input cli.Input) error {
err := opts.complete(input)
if err != nil {
return err
}
return opts.run(input)
}
func (opts *UpdateContainerSubCommand) complete(input cli.Input) error {
fnName := input.String(flagkey.FnName)
fnNamespace := input.String(flagkey.NamespaceFunction)
function, err := opts.Client().V1().Function().Get(&metav1.ObjectMeta{
Name: input.String(flagkey.FnName),
Namespace: input.String(flagkey.NamespaceFunction),
})
if err != nil {
return errors.Wrap(err, fmt.Sprintf("read function '%v'", fnName))
}
if fv1.ExecutorTypeContainer != function.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType {
return fmt.Errorf("executor type for function is not %s", fv1.ExecutorTypeContainer)
}
imageName := input.String(flagkey.FnImageName)
port := input.Int(flagkey.FnPort)
command := input.String(flagkey.FnCommand)
args := input.String(flagkey.FnArgs)
secretNames := input.StringSlice(flagkey.FnSecret)
cfgMapNames := input.StringSlice(flagkey.FnCfgMap)
var secrets []fv1.SecretReference
var configMaps []fv1.ConfigMapReference
if len(secretNames) > 0 {
// check that the referenced secret is in the same ns as the function, if not give a warning.
for _, secretName := range secretNames {
err := opts.Client().V1().Misc().SecretExists(&metav1.ObjectMeta{
Namespace: fnNamespace,
Name: secretName,
})
if k8serrors.IsNotFound(err) {
console.Warn(fmt.Sprintf("secret %s not found in Namespace: %s. Secret needs to be present in the same namespace as function", secretName, fnNamespace))
}
}
for _, secretName := range secretNames {
newSecret := fv1.SecretReference{
Name: secretName,
Namespace: fnNamespace,
}
secrets = append(secrets, newSecret)
}
function.Spec.Secrets = secrets
}
if len(cfgMapNames) > 0 {
// check that the referenced cfgmap is in the same ns as the function, if not give a warning.
for _, cfgMapName := range cfgMapNames {
err := opts.Client().V1().Misc().ConfigMapExists(&metav1.ObjectMeta{
Namespace: fnNamespace,
Name: cfgMapName,
})
if k8serrors.IsNotFound(err) {
console.Warn(fmt.Sprintf("ConfigMap %s not found in Namespace: %s. ConfigMap needs to be present in the same namespace as the function", cfgMapName, fnNamespace))
}
}
for _, cfgMapName := range cfgMapNames {
newCfgMap := fv1.ConfigMapReference{
Name: cfgMapName,
Namespace: fnNamespace,
}
configMaps = append(configMaps, newCfgMap)
}
function.Spec.ConfigMaps = configMaps
}
if input.IsSet(flagkey.FnExecutionTimeout) {
fnTimeout := input.Int(flagkey.FnExecutionTimeout)
if fnTimeout <= 0 {
return errors.Errorf("--%v must be greater than 0", flagkey.FnExecutionTimeout)
}
function.Spec.FunctionTimeout = fnTimeout
}
if input.IsSet(flagkey.FnIdleTimeout) {
fnTimeout := input.Int(flagkey.FnIdleTimeout)
function.Spec.IdleTimeout = &fnTimeout
}
strategy, err := getInvokeStrategy(input, &function.Spec.InvokeStrategy)
if err != nil {
return err
}
function.Spec.InvokeStrategy = *strategy
resReqs, err := util.GetResourceReqs(input, &function.Spec.Resources)
if err != nil {
return err
}
function.Spec.Resources = *resReqs
if len(function.Spec.PodSpec.Containers) > 1 {
return errors.Errorf("function %s has more than one container, only one container is supported", fnName)
}
container := &function.Spec.PodSpec.Containers[0]
if imageName != "" {
container.Image = imageName
}
if port != 0 {
if len(container.Ports) > 1 {
return errors.Errorf("function %s has more than one port, only one port is supported", fnName)
}
container.Ports = []apiv1.ContainerPort{
{
Name: "http-env",
ContainerPort: int32(port),
},
}
}
if command != "" {
container.Command = strings.Split(command, " ")
}
if args != "" {
container.Args = strings.Split(args, " ")
}
function.Spec.Environment = fv1.EnvironmentReference{}
function.Spec.Package = fv1.FunctionPackageRef{}
opts.function = function
err = util.ApplyLabelsAndAnnotations(input, &opts.function.ObjectMeta)
if err != nil {
return err
}
return nil
}
func (opts *UpdateContainerSubCommand) run(input cli.Input) error {
_, err := opts.Client().V1().Function().Update(opts.function)
if err != nil {
return errors.Wrap(err, "error updating function")
}
return nil
}

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