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>
This commit is contained in:
@@ -108,6 +108,7 @@ jobs:
|
|||||||
run: ./test/kind_CI.sh
|
run: ./test/kind_CI.sh
|
||||||
|
|
||||||
- name: Collect Fission Dump
|
- name: Collect Fission Dump
|
||||||
|
if: ${{ always() }}
|
||||||
run: |
|
run: |
|
||||||
command -v fission && fission support dump
|
command -v fission && fission support dump
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ jobs:
|
|||||||
source ./test/upgrade_test/fission_objects.sh test_fission_objects
|
source ./test/upgrade_test/fission_objects.sh test_fission_objects
|
||||||
|
|
||||||
- name: Collect Fission Dump
|
- name: Collect Fission Dump
|
||||||
|
if: ${{ always() }}
|
||||||
run: |
|
run: |
|
||||||
command -v fission && fission support dump
|
command -v fission && fission support dump
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ 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"
|
"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,8 +45,7 @@ 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
|
crdClient rest.Interface
|
||||||
canaryCfgCancelFuncMap *canaryConfigCancelFuncMap
|
canaryCfgCancelFuncMap *canaryConfigCancelFuncMap
|
||||||
@@ -97,49 +96,44 @@ func MakeCanaryConfigMgr(logger *zap.Logger, fissionClient *crd.FissionClient, k
|
|||||||
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 +495,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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ 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"
|
||||||
@@ -41,6 +43,7 @@ import (
|
|||||||
"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 (
|
||||||
@@ -69,8 +72,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,
|
||||||
@@ -80,12 +84,18 @@ func MakeExecutor(logger *zap.Logger, cms *cms.ConfigSecretController,
|
|||||||
requestChan: make(chan *createFuncServiceRequest),
|
requestChan: make(chan *createFuncServiceRequest),
|
||||||
fsCreateWg: make(map[string]*sync.WaitGroup),
|
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
|
||||||
@@ -257,10 +267,17 @@ 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")
|
||||||
}
|
}
|
||||||
@@ -268,7 +285,9 @@ func StartExecutor(logger *zap.Logger, functionNamespace string, envBuilderNames
|
|||||||
ndm, err := newdeploy.MakeNewDeploy(
|
ndm, err := newdeploy.MakeNewDeploy(
|
||||||
logger,
|
logger,
|
||||||
fissionClient, kubernetesClient, fissionClient.CoreV1().RESTClient(),
|
fissionClient, kubernetesClient, fissionClient.CoreV1().RESTClient(),
|
||||||
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")
|
||||||
}
|
}
|
||||||
@@ -294,9 +313,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,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))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,6 @@ 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"
|
||||||
@@ -69,12 +68,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
|
||||||
|
|
||||||
@@ -91,6 +89,8 @@ func MakeNewDeploy(
|
|||||||
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 {
|
||||||
@@ -118,19 +118,16 @@ func MakeNewDeploy(
|
|||||||
useIstio: enableIstio,
|
useIstio: enableIstio,
|
||||||
|
|
||||||
defaultIdlePodReapTime: 2 * time.Minute,
|
defaultIdlePodReapTime: 2 * time.Minute,
|
||||||
|
funcInformer: funcInformer,
|
||||||
|
envInformer: envInformer,
|
||||||
}
|
}
|
||||||
|
|
||||||
if nd.crdClient != nil {
|
if nd.crdClient != nil {
|
||||||
fnStore, fnController := nd.initFuncController()
|
(*nd.funcInformer).AddEventHandler(nd.FunctionEventHandlers())
|
||||||
nd.funcStore = fnStore
|
(*nd.envInformer).AddEventHandler(nd.EnvEventHandlers())
|
||||||
nd.funcController = fnController
|
|
||||||
|
|
||||||
envStore, envController := nd.initEnvController()
|
|
||||||
nd.envStore = envStore
|
|
||||||
nd.envController = envController
|
|
||||||
}
|
}
|
||||||
|
|
||||||
informerFactory, err := utils.GetInformerFacoryByExecutor(nd.kubernetesClient, fv1.ExecutorTypePoolmgr)
|
informerFactory, err := utils.GetInformerFactoryByExecutor(nd.kubernetesClient, fv1.ExecutorTypePoolmgr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -141,8 +138,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,19 +186,8 @@ 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(
|
||||||
@@ -220,7 +204,7 @@ 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(
|
||||||
@@ -378,85 +362,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 {
|
||||||
@@ -876,7 +781,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
|
|
||||||
}
|
|
||||||
@@ -71,8 +71,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
|
||||||
@@ -249,7 +248,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
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
|
||||||
|
|
||||||
"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"
|
|
||||||
"github.com/fission/fission/pkg/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ type (
|
|||||||
// reference into a resolveResult
|
// reference into a resolveResult
|
||||||
functionReferenceResolver struct {
|
functionReferenceResolver struct {
|
||||||
// FunctionReference -> function metadata
|
// FunctionReference -> function metadata
|
||||||
refCache *cache.Cache
|
refCache *cache.Cache
|
||||||
store k8sCache.Store
|
funcInformer *k8sCache.SharedIndexInformer
|
||||||
|
// store k8sCache.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveResultType int
|
resolveResultType int
|
||||||
@@ -68,10 +69,10 @@ const (
|
|||||||
resolveResultMultipleFunctions
|
resolveResultMultipleFunctions
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeFunctionReferenceResolver(store k8sCache.Store) *functionReferenceResolver {
|
func makeFunctionReferenceResolver(funcInformer *k8sCache.SharedIndexInformer) *functionReferenceResolver {
|
||||||
frr := &functionReferenceResolver{
|
frr := &functionReferenceResolver{
|
||||||
refCache: cache.MakeCache(time.Minute, 0),
|
refCache: cache.MakeCache(time.Minute, 0),
|
||||||
store: store,
|
funcInformer: funcInformer,
|
||||||
}
|
}
|
||||||
return frr
|
return frr
|
||||||
}
|
}
|
||||||
@@ -120,7 +121,7 @@ func (frr *functionReferenceResolver) resolve(trigger fv1.HTTPTrigger) (*resolve
|
|||||||
// resolveByName simply looks up function by name in a namespace.
|
// resolveByName simply looks up function by name in a namespace.
|
||||||
func (frr *functionReferenceResolver) resolveByName(namespace, name string) (*resolveResult, error) {
|
func (frr *functionReferenceResolver) resolveByName(namespace, name string) (*resolveResult, error) {
|
||||||
// get function from cache
|
// get function from cache
|
||||||
obj, isExist, err := frr.store.Get(&fv1.Function{
|
obj, isExist, err := (*frr.funcInformer).GetStore().Get(&fv1.Function{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -154,7 +155,7 @@ func (frr *functionReferenceResolver) resolveByFunctionWeights(namespace string,
|
|||||||
|
|
||||||
for functionName, functionWeight := range fr.FunctionWeights {
|
for functionName, functionWeight := range fr.FunctionWeights {
|
||||||
// get function from cache
|
// get function from cache
|
||||||
obj, isExist, err := frr.store.Get(&fv1.Function{
|
obj, isExist, err := (*frr.funcInformer).GetStore().Get(&fv1.Function{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Name: functionName,
|
Name: functionName,
|
||||||
|
|||||||
+70
-83
@@ -23,8 +23,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
@@ -33,6 +31,7 @@ import (
|
|||||||
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"
|
||||||
executorClient "github.com/fission/fission/pkg/executor/client"
|
executorClient "github.com/fission/fission/pkg/executor/client"
|
||||||
|
genInformer "github.com/fission/fission/pkg/generated/informers/externalversions"
|
||||||
"github.com/fission/fission/pkg/throttler"
|
"github.com/fission/fission/pkg/throttler"
|
||||||
"github.com/fission/fission/pkg/utils"
|
"github.com/fission/fission/pkg/utils"
|
||||||
)
|
)
|
||||||
@@ -49,11 +48,9 @@ type HTTPTriggerSet struct {
|
|||||||
resolver *functionReferenceResolver
|
resolver *functionReferenceResolver
|
||||||
crdClient rest.Interface
|
crdClient rest.Interface
|
||||||
triggers []fv1.HTTPTrigger
|
triggers []fv1.HTTPTrigger
|
||||||
triggerStore k8sCache.Store
|
triggerInformer k8sCache.SharedIndexInformer
|
||||||
triggerController k8sCache.Controller
|
|
||||||
functions []fv1.Function
|
functions []fv1.Function
|
||||||
funcStore k8sCache.Store
|
funcInformer k8sCache.SharedIndexInformer
|
||||||
funcController k8sCache.Controller
|
|
||||||
updateRouterRequestChannel chan struct{}
|
updateRouterRequestChannel chan struct{}
|
||||||
tsRoundTripperParams *tsRoundTripperParams
|
tsRoundTripperParams *tsRoundTripperParams
|
||||||
isDebugEnv bool
|
isDebugEnv bool
|
||||||
@@ -62,7 +59,7 @@ type HTTPTriggerSet struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeHTTPTriggerSet(logger *zap.Logger, fmap *functionServiceMap, fissionClient *crd.FissionClient,
|
func makeHTTPTriggerSet(logger *zap.Logger, fmap *functionServiceMap, fissionClient *crd.FissionClient,
|
||||||
kubeClient *kubernetes.Clientset, executor *executorClient.Client, crdClient rest.Interface, params *tsRoundTripperParams, isDebugEnv bool, unTapServiceTimeout time.Duration, actionThrottler *throttler.Throttler) (*HTTPTriggerSet, k8sCache.Store, k8sCache.Store) {
|
kubeClient *kubernetes.Clientset, executor *executorClient.Client, crdClient rest.Interface, params *tsRoundTripperParams, isDebugEnv bool, unTapServiceTimeout time.Duration, actionThrottler *throttler.Throttler) *HTTPTriggerSet {
|
||||||
|
|
||||||
httpTriggerSet := &HTTPTriggerSet{
|
httpTriggerSet := &HTTPTriggerSet{
|
||||||
logger: logger.Named("http_trigger_set"),
|
logger: logger.Named("http_trigger_set"),
|
||||||
@@ -78,21 +75,19 @@ func makeHTTPTriggerSet(logger *zap.Logger, fmap *functionServiceMap, fissionCli
|
|||||||
svcAddrUpdateThrottler: actionThrottler,
|
svcAddrUpdateThrottler: actionThrottler,
|
||||||
unTapServiceTimeout: unTapServiceTimeout,
|
unTapServiceTimeout: unTapServiceTimeout,
|
||||||
}
|
}
|
||||||
var tStore, fnStore k8sCache.Store
|
|
||||||
var tController, fnController k8sCache.Controller
|
|
||||||
|
|
||||||
if httpTriggerSet.crdClient != nil {
|
if httpTriggerSet.crdClient != nil {
|
||||||
tStore, tController = httpTriggerSet.initTriggerController()
|
informerFactory := genInformer.NewSharedInformerFactory(fissionClient, time.Second*30)
|
||||||
httpTriggerSet.triggerStore = tStore
|
httpTriggerSet.triggerInformer = informerFactory.Core().V1().HTTPTriggers().Informer()
|
||||||
httpTriggerSet.triggerController = tController
|
httpTriggerSet.funcInformer = informerFactory.Core().V1().Functions().Informer()
|
||||||
fnStore, fnController = httpTriggerSet.initFunctionController()
|
|
||||||
httpTriggerSet.funcStore = fnStore
|
httpTriggerSet.addTriggerHandlers()
|
||||||
httpTriggerSet.funcController = fnController
|
httpTriggerSet.addFunctionHandlers()
|
||||||
}
|
}
|
||||||
return httpTriggerSet, tStore, fnStore
|
return httpTriggerSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *HTTPTriggerSet) subscribeRouter(ctx context.Context, mr *mutableRouter, resolver *functionReferenceResolver) {
|
func (ts *HTTPTriggerSet) subscribeRouter(ctx context.Context, mr *mutableRouter) {
|
||||||
|
resolver := makeFunctionReferenceResolver(&ts.funcInformer)
|
||||||
ts.resolver = resolver
|
ts.resolver = resolver
|
||||||
ts.mutableRouter = mr
|
ts.mutableRouter = mr
|
||||||
|
|
||||||
@@ -104,8 +99,8 @@ func (ts *HTTPTriggerSet) subscribeRouter(ctx context.Context, mr *mutableRouter
|
|||||||
}
|
}
|
||||||
go ts.updateRouter()
|
go ts.updateRouter()
|
||||||
go ts.syncTriggers()
|
go ts.syncTriggers()
|
||||||
go ts.runWatcher(ctx, ts.funcController)
|
go ts.runInformer(ctx, ts.funcInformer)
|
||||||
go ts.runWatcher(ctx, ts.triggerController)
|
go ts.runInformer(ctx, ts.triggerInformer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultHomeHandler(w http.ResponseWriter, r *http.Request) {
|
func defaultHomeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -234,78 +229,70 @@ func (ts *HTTPTriggerSet) updateTriggerStatusFailed(ht *fv1.HTTPTrigger, err err
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *HTTPTriggerSet) initTriggerController() (k8sCache.Store, k8sCache.Controller) {
|
func (ts *HTTPTriggerSet) addTriggerHandlers() {
|
||||||
resyncPeriod := 30 * time.Second
|
ts.triggerInformer.AddEventHandler(k8sCache.ResourceEventHandlerFuncs{
|
||||||
listWatch := k8sCache.NewListWatchFromClient(ts.crdClient, "httptriggers", metav1.NamespaceAll, fields.Everything())
|
AddFunc: func(obj interface{}) {
|
||||||
store, controller := k8sCache.NewInformer(listWatch, &fv1.HTTPTrigger{}, resyncPeriod,
|
trigger := obj.(*fv1.HTTPTrigger)
|
||||||
k8sCache.ResourceEventHandlerFuncs{
|
go createIngress(ts.logger, trigger, ts.kubeClient)
|
||||||
AddFunc: func(obj interface{}) {
|
ts.syncTriggers()
|
||||||
trigger := obj.(*fv1.HTTPTrigger)
|
},
|
||||||
go createIngress(ts.logger, trigger, ts.kubeClient)
|
DeleteFunc: func(obj interface{}) {
|
||||||
ts.syncTriggers()
|
ts.syncTriggers()
|
||||||
},
|
trigger := obj.(*fv1.HTTPTrigger)
|
||||||
DeleteFunc: func(obj interface{}) {
|
go deleteIngress(ts.logger, trigger, ts.kubeClient)
|
||||||
ts.syncTriggers()
|
},
|
||||||
trigger := obj.(*fv1.HTTPTrigger)
|
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
|
||||||
go deleteIngress(ts.logger, trigger, ts.kubeClient)
|
oldTrigger := oldObj.(*fv1.HTTPTrigger)
|
||||||
},
|
newTrigger := newObj.(*fv1.HTTPTrigger)
|
||||||
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
|
|
||||||
oldTrigger := oldObj.(*fv1.HTTPTrigger)
|
|
||||||
newTrigger := newObj.(*fv1.HTTPTrigger)
|
|
||||||
|
|
||||||
if oldTrigger.ObjectMeta.ResourceVersion == newTrigger.ObjectMeta.ResourceVersion {
|
if oldTrigger.ObjectMeta.ResourceVersion == newTrigger.ObjectMeta.ResourceVersion {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go updateIngress(ts.logger, oldTrigger, newTrigger, ts.kubeClient)
|
go updateIngress(ts.logger, oldTrigger, newTrigger, ts.kubeClient)
|
||||||
ts.syncTriggers()
|
ts.syncTriggers()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return store, controller
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *HTTPTriggerSet) initFunctionController() (k8sCache.Store, k8sCache.Controller) {
|
func (ts *HTTPTriggerSet) addFunctionHandlers() {
|
||||||
resyncPeriod := 30 * time.Second
|
ts.funcInformer.AddEventHandler(k8sCache.ResourceEventHandlerFuncs{
|
||||||
listWatch := k8sCache.NewListWatchFromClient(ts.crdClient, "functions", metav1.NamespaceAll, fields.Everything())
|
AddFunc: func(obj interface{}) {
|
||||||
store, controller := k8sCache.NewInformer(listWatch, &fv1.Function{}, resyncPeriod,
|
ts.syncTriggers()
|
||||||
k8sCache.ResourceEventHandlerFuncs{
|
},
|
||||||
AddFunc: func(obj interface{}) {
|
DeleteFunc: func(obj interface{}) {
|
||||||
ts.syncTriggers()
|
ts.syncTriggers()
|
||||||
},
|
},
|
||||||
DeleteFunc: func(obj interface{}) {
|
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
|
||||||
ts.syncTriggers()
|
oldFn := oldObj.(*fv1.Function)
|
||||||
},
|
fn := newObj.(*fv1.Function)
|
||||||
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
|
|
||||||
oldFn := oldObj.(*fv1.Function)
|
|
||||||
fn := newObj.(*fv1.Function)
|
|
||||||
|
|
||||||
if oldFn.ObjectMeta.ResourceVersion == fn.ObjectMeta.ResourceVersion {
|
if oldFn.ObjectMeta.ResourceVersion == fn.ObjectMeta.ResourceVersion {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// update resolver function reference cache
|
// update resolver function reference cache
|
||||||
for key, rr := range ts.resolver.copy() {
|
for key, rr := range ts.resolver.copy() {
|
||||||
if key.namespace == fn.ObjectMeta.Namespace &&
|
if key.namespace == fn.ObjectMeta.Namespace &&
|
||||||
rr.functionMap[fn.ObjectMeta.Name] != nil &&
|
rr.functionMap[fn.ObjectMeta.Name] != nil &&
|
||||||
rr.functionMap[fn.ObjectMeta.Name].ObjectMeta.ResourceVersion != fn.ObjectMeta.ResourceVersion {
|
rr.functionMap[fn.ObjectMeta.Name].ObjectMeta.ResourceVersion != fn.ObjectMeta.ResourceVersion {
|
||||||
// invalidate resolver cache
|
// invalidate resolver cache
|
||||||
ts.logger.Debug("invalidating resolver cache")
|
ts.logger.Debug("invalidating resolver cache")
|
||||||
err := ts.resolver.delete(key.namespace, key.triggerName, key.triggerResourceVersion)
|
err := ts.resolver.delete(key.namespace, key.triggerName, key.triggerResourceVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ts.logger.Error("error deleting functionReferenceResolver cache", zap.Error(err))
|
ts.logger.Error("error deleting functionReferenceResolver cache", zap.Error(err))
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
ts.syncTriggers()
|
}
|
||||||
},
|
ts.syncTriggers()
|
||||||
})
|
},
|
||||||
return store, controller
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *HTTPTriggerSet) runWatcher(ctx context.Context, controller k8sCache.Controller) {
|
func (ts *HTTPTriggerSet) runInformer(ctx context.Context, informer k8sCache.SharedIndexInformer) {
|
||||||
go func() {
|
go func() {
|
||||||
controller.Run(ctx.Done())
|
informer.Run(ctx.Done())
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +303,7 @@ func (ts *HTTPTriggerSet) syncTriggers() {
|
|||||||
func (ts *HTTPTriggerSet) updateRouter() {
|
func (ts *HTTPTriggerSet) updateRouter() {
|
||||||
for range ts.updateRouterRequestChannel {
|
for range ts.updateRouterRequestChannel {
|
||||||
// get triggers
|
// get triggers
|
||||||
latestTriggers := ts.triggerStore.List()
|
latestTriggers := ts.triggerInformer.GetStore().List()
|
||||||
triggers := make([]fv1.HTTPTrigger, len(latestTriggers))
|
triggers := make([]fv1.HTTPTrigger, len(latestTriggers))
|
||||||
for _, t := range latestTriggers {
|
for _, t := range latestTriggers {
|
||||||
triggers = append(triggers, *t.(*fv1.HTTPTrigger))
|
triggers = append(triggers, *t.(*fv1.HTTPTrigger))
|
||||||
@@ -324,7 +311,7 @@ func (ts *HTTPTriggerSet) updateRouter() {
|
|||||||
ts.triggers = triggers
|
ts.triggers = triggers
|
||||||
|
|
||||||
// get functions
|
// get functions
|
||||||
latestFunctions := ts.funcStore.List()
|
latestFunctions := ts.funcInformer.GetStore().List()
|
||||||
functionTimeout := make(map[types.UID]int, len(latestFunctions))
|
functionTimeout := make(map[types.UID]int, len(latestFunctions))
|
||||||
functions := make([]fv1.Function, len(latestFunctions))
|
functions := make([]fv1.Function, len(latestFunctions))
|
||||||
for _, f := range latestFunctions {
|
for _, f := range latestFunctions {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ import (
|
|||||||
|
|
||||||
// request url ---[trigger]---> Function(name, deployment) ----[deployment]----> Function(name, uid) ----[pool mgr]---> k8s service url
|
// request url ---[trigger]---> Function(name, deployment) ----[deployment]----> Function(name, uid) ----[pool mgr]---> k8s service url
|
||||||
|
|
||||||
func router(ctx context.Context, logger *zap.Logger, httpTriggerSet *HTTPTriggerSet, resolver *functionReferenceResolver) *mutableRouter {
|
func router(ctx context.Context, logger *zap.Logger, httpTriggerSet *HTTPTriggerSet) *mutableRouter {
|
||||||
var mr *mutableRouter
|
var mr *mutableRouter
|
||||||
|
|
||||||
// see issue https://github.com/fission/fission/issues/1317
|
// see issue https://github.com/fission/fission/issues/1317
|
||||||
@@ -74,13 +74,13 @@ func router(ctx context.Context, logger *zap.Logger, httpTriggerSet *HTTPTrigger
|
|||||||
mr = newMutableRouter(logger, mux.NewRouter())
|
mr = newMutableRouter(logger, mux.NewRouter())
|
||||||
}
|
}
|
||||||
|
|
||||||
httpTriggerSet.subscribeRouter(ctx, mr, resolver)
|
httpTriggerSet.subscribeRouter(ctx, mr)
|
||||||
return mr
|
return mr
|
||||||
}
|
}
|
||||||
|
|
||||||
func serve(ctx context.Context, logger *zap.Logger, port int, tracingSamplingRate float64,
|
func serve(ctx context.Context, logger *zap.Logger, port int, tracingSamplingRate float64,
|
||||||
httpTriggerSet *HTTPTriggerSet, resolver *functionReferenceResolver, displayAccessLog bool) {
|
httpTriggerSet *HTTPTriggerSet, displayAccessLog bool) {
|
||||||
mr := router(ctx, logger, httpTriggerSet, resolver)
|
mr := router(ctx, logger, httpTriggerSet)
|
||||||
url := fmt.Sprintf(":%v", port)
|
url := fmt.Sprintf(":%v", port)
|
||||||
|
|
||||||
err := http.ListenAndServe(url, &ochttp.Handler{
|
err := http.ListenAndServe(url, &ochttp.Handler{
|
||||||
@@ -236,7 +236,7 @@ func Start(logger *zap.Logger, port int, executorURL string) {
|
|||||||
zap.Bool("default", displayAccessLog))
|
zap.Bool("default", displayAccessLog))
|
||||||
}
|
}
|
||||||
|
|
||||||
triggers, _, fnStore := makeHTTPTriggerSet(logger.Named("triggerset"), fmap, fissionClient, kubeClient, executor, fissionClient.CoreV1().RESTClient(), &tsRoundTripperParams{
|
triggers := makeHTTPTriggerSet(logger.Named("triggerset"), fmap, fissionClient, kubeClient, executor, fissionClient.CoreV1().RESTClient(), &tsRoundTripperParams{
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
timeoutExponent: timeoutExponent,
|
timeoutExponent: timeoutExponent,
|
||||||
disableKeepAlive: disableKeepAlive,
|
disableKeepAlive: disableKeepAlive,
|
||||||
@@ -245,12 +245,10 @@ func Start(logger *zap.Logger, port int, executorURL string) {
|
|||||||
svcAddrRetryCount: svcAddrRetryCount,
|
svcAddrRetryCount: svcAddrRetryCount,
|
||||||
}, isDebugEnv, unTapServiceTimeout, throttler.MakeThrottler(svcAddrUpdateTimeout))
|
}, isDebugEnv, unTapServiceTimeout, throttler.MakeThrottler(svcAddrUpdateTimeout))
|
||||||
|
|
||||||
resolver := makeFunctionReferenceResolver(fnStore)
|
|
||||||
|
|
||||||
go serveMetric(logger)
|
go serveMetric(logger)
|
||||||
|
|
||||||
logger.Info("starting router", zap.Int("port", port))
|
logger.Info("starting router", zap.Int("port", port))
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
serve(ctx, logger, port, tracingSamplingRate, triggers, resolver, displayAccessLog)
|
serve(ctx, logger, port, tracingSamplingRate, triggers, displayAccessLog)
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-1
@@ -1,17 +1,21 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
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"
|
||||||
"k8s.io/apimachinery/pkg/selection"
|
"k8s.io/apimachinery/pkg/selection"
|
||||||
k8sInformers "k8s.io/client-go/informers"
|
k8sInformers "k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
|
k8sCache "k8s.io/client-go/tools/cache"
|
||||||
metricsapi "k8s.io/metrics/pkg/apis/metrics"
|
metricsapi "k8s.io/metrics/pkg/apis/metrics"
|
||||||
|
|
||||||
v1 "github.com/fission/fission/pkg/apis/core/v1"
|
v1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetInformerFacoryByExecutor(client *kubernetes.Clientset, executorType v1.ExecutorType) (k8sInformers.SharedInformerFactory, error) {
|
func GetInformerFactoryByExecutor(client *kubernetes.Clientset, executorType v1.ExecutorType) (k8sInformers.SharedInformerFactory, error) {
|
||||||
executorLabel, err := labels.NewRequirement(v1.EXECUTOR_TYPE, selection.DoubleEquals, []string{string(executorType)})
|
executorLabel, err := labels.NewRequirement(v1.EXECUTOR_TYPE, selection.DoubleEquals, []string{string(executorType)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -43,3 +47,14 @@ func SupportedMetricsAPIVersionAvailable(discoveredAPIGroups *metav1.APIGroupLis
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -84,7 +84,7 @@ main() {
|
|||||||
$ROOT/test/tests/mqtrigger/nats/test_mqtrigger.sh \
|
$ROOT/test/tests/mqtrigger/nats/test_mqtrigger.sh \
|
||||||
$ROOT/test/tests/mqtrigger/nats/test_mqtrigger_error.sh \
|
$ROOT/test/tests/mqtrigger/nats/test_mqtrigger_error.sh \
|
||||||
$ROOT/test/tests/test_huge_response/test_huge_response.sh \
|
$ROOT/test/tests/test_huge_response/test_huge_response.sh \
|
||||||
$ROOT/test/tests/test_kubectl/test_kubectl.sh
|
$ROOT/test/tests/test_kubectl/test_kubectl.sh \
|
||||||
$ROOT/test/tests/websocket/test_ws.sh
|
$ROOT/test/tests/websocket/test_ws.sh
|
||||||
|
|
||||||
export JOBS=3
|
export JOBS=3
|
||||||
|
|||||||
Reference in New Issue
Block a user