Test for httptrigger and functions container/newdeploy (#2861)

* Test for httptrigger and functions
* Fixes with multierror
* review changes

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
This commit is contained in:
Sanket Sudake
2023-10-27 15:05:20 +05:30
committed by GitHub
parent 2eb2eba88c
commit 2223081c80
24 changed files with 292 additions and 166 deletions
+11 -10
View File
@@ -18,11 +18,10 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
multierror "github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
@@ -52,17 +51,18 @@ const (
) )
func makePreUpgradeTaskClient(clientGen crd.ClientGeneratorInterface, logger *zap.Logger) (*PreUpgradeTaskClient, error) { func makePreUpgradeTaskClient(clientGen crd.ClientGeneratorInterface, logger *zap.Logger) (*PreUpgradeTaskClient, error) {
var err error
fissionClient, err := clientGen.GetFissionClient() fissionClient, err := clientGen.GetFissionClient()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to get fission client") return nil, fmt.Errorf("failed to get fission client: %w", err)
} }
k8sClient, err := clientGen.GetKubernetesClient() k8sClient, err := clientGen.GetKubernetesClient()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to get kubernetes client") return nil, fmt.Errorf("failed to get kubernetes client: %w", err)
} }
apiExtClient, err := clientGen.GetApiExtensionsClient() apiExtClient, err := clientGen.GetApiExtensionsClient()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to get apiextensions client") return nil, fmt.Errorf("failed to get apiextensions client: %w", err)
} }
return &PreUpgradeTaskClient{ return &PreUpgradeTaskClient{
@@ -129,7 +129,7 @@ func (client *PreUpgradeTaskClient) VerifyFunctionSpecReferences(ctx context.Con
var err error var err error
var fList *fv1.FunctionList var fList *fv1.FunctionList
errs := &multierror.Error{} var errs error
for _, namespace := range utils.DefaultNSResolver().FissionResourceNS { for _, namespace := range utils.DefaultNSResolver().FissionResourceNS {
for i := 0; i < maxRetries; i++ { for i := 0; i < maxRetries; i++ {
@@ -150,24 +150,25 @@ func (client *PreUpgradeTaskClient) VerifyFunctionSpecReferences(ctx context.Con
secrets := fn.Spec.Secrets secrets := fn.Spec.Secrets
for _, secret := range secrets { for _, secret := range secrets {
if secret.Namespace != "" && secret.Namespace != fn.ObjectMeta.Namespace { if secret.Namespace != "" && secret.Namespace != fn.ObjectMeta.Namespace {
errs = multierror.Append(errs, fmt.Errorf("function : %s.%s cannot reference a secret : %s in namespace : %s", fn.ObjectMeta.Name, fn.ObjectMeta.Namespace, secret.Name, secret.Namespace)) errs = errors.Join(errs, fmt.Errorf("function : %s.%s cannot reference a secret : %s in namespace : %s", fn.ObjectMeta.Name, fn.ObjectMeta.Namespace, secret.Name, secret.Namespace))
} }
} }
configmaps := fn.Spec.ConfigMaps configmaps := fn.Spec.ConfigMaps
for _, configmap := range configmaps { for _, configmap := range configmaps {
if configmap.Namespace != "" && configmap.Namespace != fn.ObjectMeta.Namespace { if configmap.Namespace != "" && configmap.Namespace != fn.ObjectMeta.Namespace {
errs = multierror.Append(errs, fmt.Errorf("function : %s.%s cannot reference a configmap : %s in namespace : %s", fn.ObjectMeta.Name, fn.ObjectMeta.Namespace, configmap.Name, configmap.Namespace)) errs = errors.Join(errs, fmt.Errorf("function : %s.%s cannot reference a configmap : %s in namespace : %s", fn.ObjectMeta.Name, fn.ObjectMeta.Namespace, configmap.Name, configmap.Namespace))
} }
} }
if fn.Spec.Package.PackageRef.Namespace != "" && fn.Spec.Package.PackageRef.Namespace != fn.ObjectMeta.Namespace { if fn.Spec.Package.PackageRef.Namespace != "" && fn.Spec.Package.PackageRef.Namespace != fn.ObjectMeta.Namespace {
errs = multierror.Append(errs, fmt.Errorf("function : %s.%s cannot reference a package : %s in namespace : %s", fn.ObjectMeta.Name, fn.ObjectMeta.Namespace, fn.Spec.Package.PackageRef.Name, fn.Spec.Package.PackageRef.Namespace)) errs = errors.Join(errs, fmt.Errorf("function : %s.%s cannot reference a package : %s in namespace : %s", fn.ObjectMeta.Name, fn.ObjectMeta.Namespace, fn.Spec.Package.PackageRef.Name, fn.Spec.Package.PackageRef.Namespace))
} }
} }
} }
if errs.ErrorOrNil() != nil { if errs != nil {
client.logger.Fatal("installation failed", client.logger.Fatal("installation failed",
zap.Error(errs), zap.Error(errs),
zap.String("summary", "a function cannot reference secrets, configmaps and packages outside it's own namespace")) zap.String("summary", "a function cannot reference secrets, configmaps and packages outside it's own namespace"))
+6 -7
View File
@@ -129,25 +129,24 @@ func ValidateKubeLabel(field string, labels map[string]string) error {
} }
func ValidateKubePort(field string, port int) error { func ValidateKubePort(field string, port int) error {
result := &multierror.Error{} var err error
e := validation.IsValidPortNum(port) e := validation.IsValidPortNum(port)
if len(e) > 0 { if len(e) > 0 {
result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, field, port, e...)) err = errors.Join(err, MakeValidationErr(ErrorInvalidValue, field, port, e...))
} }
return err
return result.ErrorOrNil()
} }
func ValidateKubeName(field string, val string) error { func ValidateKubeName(field string, val string) error {
result := &multierror.Error{} var err error
e := validation.IsDNS1123Label(val) e := validation.IsDNS1123Label(val)
if len(e) > 0 { if len(e) > 0 {
result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, field, val, e...)) err = errors.Join(err, MakeValidationErr(ErrorInvalidValue, field, val, e...))
} }
return result.ErrorOrNil() return err
} }
// validateNS is to match the k8s behaviour. Where it is not mandatory to provide a NS. And so we validate it if user has provided one. // validateNS is to match the k8s behaviour. Where it is not mandatory to provide a NS. And so we validate it if user has provided one.
+2 -1
View File
@@ -18,6 +18,7 @@ package buildermgr
import ( import (
"context" "context"
"os"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -54,7 +55,7 @@ func Start(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *
} }
podSpecPatch, err := util.GetSpecFromConfigMap(fv1.BuilderPodSpecPath) podSpecPatch, err := util.GetSpecFromConfigMap(fv1.BuilderPodSpecPath)
if err != nil { if err != nil && !os.IsNotExist(err) {
logger.Warn("error reading data for pod spec patch", zap.String("path", fv1.BuilderPodSpecPath), zap.Error(err)) logger.Warn("error reading data for pod spec patch", zap.String("path", fv1.BuilderPodSpecPath), zap.Error(err))
} }
+1 -1
View File
@@ -35,7 +35,7 @@ func ConfigureFeatures(ctx context.Context, logger *zap.Logger, unitTestMode boo
} }
// get the featureConfig from config map mounted onto the file system // get the featureConfig from config map mounted onto the file system
featureConfig, err := config.GetFeatureConfig() featureConfig, err := config.GetFeatureConfig(logger)
if err != nil { if err != nil {
logger.Error("error getting feature config", zap.Error(err)) logger.Error("error getting feature config", zap.Error(err))
return err return err
+7
View File
@@ -62,6 +62,13 @@ func CacheKeyURFromMeta(metadata *metav1.ObjectMeta) CacheKeyUR {
} }
} }
func CacheKeyURFromObject(obj metav1.Object) CacheKeyUR {
return CacheKeyUR{
UID: obj.GetUID(),
ResourceVersion: obj.GetResourceVersion(),
}
}
// CacheKeyURGFromMeta : Given metadata, create a key that uniquely identifies the contents // CacheKeyURGFromMeta : Given metadata, create a key that uniquely identifies the contents
// of the object. Since resourceVersion changes on every update and // of the object. Since resourceVersion changes on every update and
// UIDs are unique, uid+resourceVersion identifies the // UIDs are unique, uid+resourceVersion identifies the
+8 -10
View File
@@ -19,6 +19,7 @@ package executor
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"html" "html"
"io" "io"
@@ -26,8 +27,6 @@ import (
"strings" "strings"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
fv1 "github.com/fission/fission/pkg/apis/core/v1" fv1 "github.com/fission/fission/pkg/apis/core/v1"
@@ -201,27 +200,26 @@ func (executor *Executor) tapServices(w http.ResponseWriter, r *http.Request) {
return return
} }
errs := &multierror.Error{} var errs error
for _, req := range tapSvcReqs { for _, req := range tapSvcReqs {
svcHost := strings.TrimPrefix(req.ServiceURL, "http://") svcHost := strings.TrimPrefix(req.ServiceURL, "http://")
et, exists := executor.executorTypes[req.FnExecutorType] et, exists := executor.executorTypes[req.FnExecutorType]
if !exists { if !exists {
errs = multierror.Append(errs, errs = errors.Join(errs,
errors.Errorf("error tapping service due to unknown executor type '%v' found", fmt.Errorf("error tapping service due to unknown executor type '%s' found",
req.FnExecutorType)) req.FnExecutorType))
continue continue
} }
err = et.TapService(ctx, svcHost) err = et.TapService(ctx, svcHost)
if err != nil { if err != nil {
errs = multierror.Append(errs, errs = errors.Join(errs,
errors.Wrapf(err, "'%v' failed to tap function '%v' in '%v' with service url '%v'", fmt.Errorf("error tapping function '%s/%s' with executor '%s' and service url '%s': %w", req.FnMetadata.Namespace, req.FnMetadata.Name, req.FnExecutorType, req.ServiceURL, err))
req.FnMetadata.Name, req.FnMetadata.Namespace, req.ServiceURL, req.FnExecutorType))
} }
} }
if errs.ErrorOrNil() != nil { if errs != nil {
logger.Error("error tapping function service", zap.Error(errs)) logger.Error("error tapping function service", zap.Error(errs))
http.Error(w, "Not found", http.StatusNotFound) http.Error(w, "Not found", http.StatusNotFound)
return return
@@ -249,7 +247,7 @@ func (executor *Executor) unTapService(w http.ResponseWriter, r *http.Request) {
} }
t := tapSvcReq.FnExecutorType t := tapSvcReq.FnExecutorType
if t != fv1.ExecutorTypePoolmgr { if t != fv1.ExecutorTypePoolmgr {
msg := fmt.Sprintf("Unknown executor type '%v'", t) msg := fmt.Sprintf("Unknown executor type '%s'", t)
http.Error(w, html.EscapeString(msg), http.StatusBadRequest) http.Error(w, html.EscapeString(msg), http.StatusBadRequest)
return return
} }
+1 -1
View File
@@ -72,7 +72,7 @@ func refreshPods(ctx context.Context, logger *zap.Logger, funcs []fv1.Function,
if exists { if exists {
err = et.RefreshFuncPods(ctx, logger, f) err = et.RefreshFuncPods(ctx, logger, f)
} else { } else {
err = errors.Errorf("Unknown executor type '%v'", f.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType) err = errors.Errorf("Unknown executor type '%s'", f.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType)
} }
if err != nil { if err != nil {
+12 -10
View File
@@ -110,7 +110,9 @@ func MakeExecutor(ctx context.Context, logger *zap.Logger, cms *cms.ConfigSecret
func (executor *Executor) serveCreateFuncServices() { func (executor *Executor) serveCreateFuncServices() {
for { for {
req := <-executor.requestChan req := <-executor.requestChan
fnMetadata := &req.function.ObjectMeta function := req.function
fnName := k8sCache.MetaObjectToName(function)
fnkeyUR := crd.CacheKeyURFromObject(function)
if req.function.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fv1.ExecutorTypePoolmgr { if req.function.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType == fv1.ExecutorTypePoolmgr {
go func() { go func() {
@@ -138,13 +140,13 @@ func (executor *Executor) serveCreateFuncServices() {
} }
// Cache miss -- is this first one to request the func? // Cache miss -- is this first one to request the func?
wg, found := executor.fsCreateWg.Load(crd.CacheKeyURFromMeta(fnMetadata)) wg, found := executor.fsCreateWg.Load(fnkeyUR)
if !found { if !found {
// create a waitgroup for other requests for // create a waitgroup for other requests for
// the same function to wait on // the same function to wait on
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
wg.Add(1) wg.Add(1)
executor.fsCreateWg.Store(crd.CacheKeyURFromMeta(fnMetadata), wg) executor.fsCreateWg.Store(fnkeyUR, wg)
// launch a goroutine for each request, to parallelize // launch a goroutine for each request, to parallelize
// the specialization of different functions // the specialization of different functions
@@ -176,17 +178,17 @@ func (executor *Executor) serveCreateFuncServices() {
funcSvc: fsvc, funcSvc: fsvc,
err: err, err: err,
} }
executor.fsCreateWg.Delete(crd.CacheKeyURFromMeta(fnMetadata)) executor.fsCreateWg.Delete(fnkeyUR)
wg.Done() wg.Done()
}() }()
} else { } else {
// There's an existing request for this function, wait for it to finish // There's an existing request for this function, wait for it to finish
go func() { go func() {
executor.logger.Debug("waiting for concurrent request for the same function", executor.logger.Debug("waiting for concurrent request for the same function",
zap.Any("function", fnMetadata)) zap.String("function", fnName.String()))
wg, ok := wg.(*sync.WaitGroup) wg, ok := wg.(*sync.WaitGroup)
if !ok { if !ok {
err := fmt.Errorf("could not convert value to workgroup for function %v in namespace %v", fnMetadata.Name, fnMetadata.Namespace) err := fmt.Errorf("could not convert value to workgroup for function %s", fnName)
req.respChan <- &createFuncServiceResponse{ req.respChan <- &createFuncServiceResponse{
funcSvc: nil, funcSvc: nil,
err: err, err: err,
@@ -201,7 +203,7 @@ func (executor *Executor) serveCreateFuncServices() {
// It normally happened if there are multiple requests are // It normally happened if there are multiple requests are
// waiting for the same function and executor failed to cre- // waiting for the same function and executor failed to cre-
// ate service for function. // ate service for function.
err = errors.Wrapf(err, "error getting service for function %v in namespace %v", fnMetadata.Name, fnMetadata.Namespace) err = errors.Wrapf(err, "error getting service for function %s", fnName)
req.respChan <- &createFuncServiceResponse{ req.respChan <- &createFuncServiceResponse{
funcSvc: fsvc, funcSvc: fsvc,
err: err, err: err,
@@ -221,7 +223,7 @@ func (executor *Executor) createServiceForFunction(ctx context.Context, fn *fv1.
t := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType t := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
e, ok := executor.executorTypes[t] e, ok := executor.executorTypes[t]
if !ok { if !ok {
return nil, errors.Errorf("Unknown executor type '%v'", t) return nil, errors.Errorf("Unknown executor type '%s'", t)
} }
fsvc, fsvcErr := e.GetFuncSvc(ctx, fn) fsvc, fsvcErr := e.GetFuncSvc(ctx, fn)
@@ -242,7 +244,7 @@ func (executor *Executor) getFunctionServiceFromCache(ctx context.Context, fn *f
t := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType t := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
e, ok := executor.executorTypes[t] e, ok := executor.executorTypes[t]
if !ok { if !ok {
return nil, errors.Errorf("Unknown executor type '%v'", t) return nil, errors.Errorf("Unknown executor type '%s'", t)
} }
return e.GetFuncSvcFromCache(ctx, fn) return e.GetFuncSvcFromCache(ctx, fn)
} }
@@ -277,7 +279,7 @@ func StartExecutor(ctx context.Context, clientGen crd.ClientGeneratorInterface,
executorInstanceID := strings.ToLower(uniuri.NewLen(8)) executorInstanceID := strings.ToLower(uniuri.NewLen(8))
podSpecPatch, err := util.GetSpecFromConfigMap(fv1.RuntimePodSpecPath) podSpecPatch, err := util.GetSpecFromConfigMap(fv1.RuntimePodSpecPath)
if err != nil { if err != nil && !os.IsNotExist(err) {
logger.Warn("error reading data for pod spec patch", zap.String("path", fv1.RuntimePodSpecPath), zap.Error(err)) logger.Warn("error reading data for pod spec patch", zap.String("path", fv1.RuntimePodSpecPath), zap.Error(err))
} }
@@ -18,9 +18,9 @@ package container
import ( import (
"context" "context"
"errors"
"strconv" "strconv"
multierror "github.com/hashicorp/go-multierror"
"go.uber.org/zap" "go.uber.org/zap"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
k8s_err "k8s.io/apimachinery/pkg/api/errors" k8s_err "k8s.io/apimachinery/pkg/api/errors"
@@ -66,7 +66,7 @@ func (cn *Container) getResources(fn *fv1.Function) apiv1.ResourceRequirements {
// cleanupContainer cleans all kubernetes objects related to function // cleanupContainer cleans all kubernetes objects related to function
func (cn *Container) cleanupContainer(ctx context.Context, ns string, name string) error { func (cn *Container) cleanupContainer(ctx context.Context, ns string, name string) error {
result := &multierror.Error{} var result error
err := cn.deleteSvc(ctx, ns, name) err := cn.deleteSvc(ctx, ns, name)
if err != nil && !k8s_err.IsNotFound(err) { if err != nil && !k8s_err.IsNotFound(err) {
@@ -74,7 +74,7 @@ func (cn *Container) cleanupContainer(ctx context.Context, ns string, name strin
zap.Error(err), zap.Error(err),
zap.String("function_name", name), zap.String("function_name", name),
zap.String("function_namespace", ns)) zap.String("function_namespace", ns))
result = multierror.Append(result, err) result = errors.Join(result, err)
} }
err = cn.hpaops.DeleteHpa(ctx, ns, name) err = cn.hpaops.DeleteHpa(ctx, ns, name)
@@ -83,7 +83,7 @@ func (cn *Container) cleanupContainer(ctx context.Context, ns string, name strin
zap.Error(err), zap.Error(err),
zap.String("function_name", name), zap.String("function_name", name),
zap.String("function_namespace", ns)) zap.String("function_namespace", ns))
result = multierror.Append(result, err) result = errors.Join(result, err)
} }
err = cn.deleteDeployment(ctx, ns, name) err = cn.deleteDeployment(ctx, ns, name)
@@ -92,10 +92,10 @@ func (cn *Container) cleanupContainer(ctx context.Context, ns string, name strin
zap.Error(err), zap.Error(err),
zap.String("function_name", name), zap.String("function_name", name),
zap.String("function_namespace", ns)) zap.String("function_namespace", ns))
result = multierror.Append(result, err) result = errors.Join(result, err)
} }
return result.ErrorOrNil() return result
} }
// referencedResourcesRVSum returns the sum of resource version of all resources the function references to. // referencedResourcesRVSum returns the sum of resource version of all resources the function references to.
@@ -18,6 +18,7 @@ package container
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
@@ -26,8 +27,6 @@ import (
"sync" "sync"
"time" "time"
multierror "github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
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"
@@ -142,7 +141,7 @@ func MakeContainer(
for _, factory := range finformerFactory { for _, factory := range finformerFactory {
_, err := factory.Core().V1().Functions().Informer().AddEventHandler(caaf.FuncInformerHandler(ctx)) _, err := factory.Core().V1().Functions().Informer().AddEventHandler(caaf.FuncInformerHandler(ctx))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to add event handler for function informer") return nil, fmt.Errorf("failed to add event handler for function informer: %w", err)
} }
} }
return caaf, nil return caaf, nil
@@ -269,7 +268,7 @@ func (caaf *Container) RefreshFuncPods(ctx context.Context, logger *zap.Logger,
return err return err
} }
patch := fmt.Sprintf(`{"spec" : {"template": {"spec":{"containers":[{"name": "%s", "env":[{"name": "%s", "value": "%v"}]}]}}}}`, patch := fmt.Sprintf(`{"spec" : {"template": {"spec":{"containers":[{"name": "%s", "env":[{"name": "%s", "value": "%d"}]}]}}}}`,
f.ObjectMeta.Name, fv1.ResourceVersionCount, rvCount) f.ObjectMeta.Name, fv1.ResourceVersionCount, rvCount)
_, err = caaf.kubernetesClient.AppsV1().Deployments(deployment.ObjectMeta.Namespace).Patch(ctx, deployment.ObjectMeta.Name, _, err = caaf.kubernetesClient.AppsV1().Deployments(deployment.ObjectMeta.Namespace).Patch(ctx, deployment.ObjectMeta.Name,
@@ -318,29 +317,29 @@ func (caaf *Container) AdoptExistingResources(ctx context.Context) {
func (caaf *Container) CleanupOldExecutorObjects(ctx context.Context) { func (caaf *Container) CleanupOldExecutorObjects(ctx context.Context) {
caaf.logger.Info("CaaF starts to clean orphaned resources", zap.String("instanceID", caaf.instanceID)) caaf.logger.Info("CaaF starts to clean orphaned resources", zap.String("instanceID", caaf.instanceID))
errs := &multierror.Error{} var errs error
listOpts := metav1.ListOptions{ listOpts := metav1.ListOptions{
LabelSelector: labels.Set(map[string]string{fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeContainer)}).AsSelector().String(), LabelSelector: labels.Set(map[string]string{fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeContainer)}).AsSelector().String(),
} }
err := reaper.CleanupHpa(ctx, caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts) err := reaper.CleanupHpa(ctx, caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts)
if err != nil { if err != nil {
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} }
err = reaper.CleanupDeployments(ctx, caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts) err = reaper.CleanupDeployments(ctx, caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts)
if err != nil { if err != nil {
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} }
err = reaper.CleanupServices(ctx, caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts) err = reaper.CleanupServices(ctx, caaf.logger, caaf.kubernetesClient, caaf.instanceID, listOpts)
if err != nil { if err != nil {
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} }
if errs.ErrorOrNil() != nil { if errs != nil {
// TODO retry reaper; logged and ignored for now // TODO retry reaper; logged and ignored for now
caaf.logger.Error("Failed to cleanup old executor objects", zap.Error(err)) caaf.logger.Error("Failed to cleanup old executor objects", zap.Error(errs))
} }
} }
@@ -361,7 +360,7 @@ func (caaf *Container) createFunction(ctx context.Context, fn *fv1.Function) (*f
zap.Error(err), zap.Error(err),
zap.String("function_name", fn.ObjectMeta.Name), zap.String("function_name", fn.ObjectMeta.Name),
zap.String("function_namespace", fn.ObjectMeta.Namespace)) zap.String("function_namespace", fn.ObjectMeta.Namespace))
return nil, errors.Wrapf(err, "%s %s_%s", e, fn.ObjectMeta.Name, fn.ObjectMeta.Namespace) return nil, fmt.Errorf("error creating k8s resources for function %s/%s: %w", fn.ObjectMeta.Namespace, fn.ObjectMeta.Name, err)
} }
fsvc, ok := fsvcObj.(*fscache.FuncSvc) fsvc, ok := fsvcObj.(*fscache.FuncSvc)
@@ -378,7 +377,7 @@ func (caaf *Container) deleteFunction(ctx context.Context, fn *fv1.Function) err
} }
err := caaf.fnDelete(ctx, fn) err := caaf.fnDelete(ctx, fn)
if err != nil { if err != nil {
err = errors.Wrapf(err, "error deleting kubernetes objects of function %v", fn.ObjectMeta) return fmt.Errorf("error deleting kubernetes objects of function %s: %w", k8sCache.MetaObjectToName(fn), err)
} }
return err return err
} }
@@ -408,22 +407,22 @@ func (caaf *Container) fnCreate(ctx context.Context, fn *fv1.Function) (*fscache
if err != nil { if err != nil {
caaf.logger.Error("error creating service", zap.Error(err), zap.String("service", objName)) caaf.logger.Error("error creating service", zap.Error(err), zap.String("service", objName))
go cleanupFunc(ns, objName) go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating service %v", objName) return nil, fmt.Errorf("error creating service %s: %w", objName, err)
} }
svcAddress := fmt.Sprintf("%v.%v", svc.Name, svc.Namespace) svcAddress := fmt.Sprintf("%s.%s", svc.Name, svc.Namespace)
depl, err := caaf.createOrGetDeployment(ctx, fn, objName, deployLabels, deployAnnotations, ns) depl, err := caaf.createOrGetDeployment(ctx, fn, objName, deployLabels, deployAnnotations, ns)
if err != nil { if err != nil {
caaf.logger.Error("error creating deployment", zap.Error(err), zap.String("deployment", objName)) caaf.logger.Error("error creating deployment", zap.Error(err), zap.String("deployment", objName))
go cleanupFunc(ns, objName) go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating deployment %v", objName) return nil, fmt.Errorf("error creating deployment %s: %w", objName, err)
} }
hpa, err := caaf.hpaops.CreateOrGetHpa(ctx, objName, &fn.Spec.InvokeStrategy.ExecutionStrategy, depl, deployLabels, deployAnnotations) hpa, err := caaf.hpaops.CreateOrGetHpa(ctx, objName, &fn.Spec.InvokeStrategy.ExecutionStrategy, depl, deployLabels, deployAnnotations)
if err != nil { if err != nil {
caaf.logger.Error("error creating HPA", zap.Error(err), zap.String("hpa", objName)) caaf.logger.Error("error creating HPA", zap.Error(err), zap.String("hpa", objName))
go cleanupFunc(ns, objName) go cleanupFunc(ns, objName)
return nil, errors.Wrapf(err, "error creating the HPA %v", objName) return nil, fmt.Errorf("error creating HPA %s: %w", objName, err)
} }
kubeObjRefs := []apiv1.ObjectReference{ kubeObjRefs := []apiv1.ObjectReference{
@@ -515,8 +514,7 @@ func (caaf *Container) updateFunction(ctx context.Context, oldFn *fv1.Function,
fsvc, err := caaf.fsCache.GetByFunctionUID(newFn.ObjectMeta.UID) fsvc, err := caaf.fsCache.GetByFunctionUID(newFn.ObjectMeta.UID)
if err != nil { if err != nil {
err = errors.Wrapf(err, "error updating function due to unable to find function service cache: %v", oldFn) return fmt.Errorf("error updating function due to unable to find function service cache %s: %w", k8sCache.MetaObjectToName(oldFn), err)
return err
} }
hpa, err := caaf.hpaops.GetHpa(ctx, ns, fsvc.Name) hpa, err := caaf.hpaops.GetHpa(ctx, ns, fsvc.Name)
@@ -596,8 +594,7 @@ func (caaf *Container) updateFuncDeployment(ctx context.Context, fn *fv1.Functio
fsvc, err := caaf.fsCache.GetByFunctionUID(fn.ObjectMeta.UID) fsvc, err := caaf.fsCache.GetByFunctionUID(fn.ObjectMeta.UID)
if err != nil { if err != nil {
err = errors.Wrapf(err, "error updating function due to unable to find function service cache: %v", fn) return fmt.Errorf("error updating function due to unable to find function service cache %s: %w", k8sCache.MetaObjectToName(fn), err)
return err
} }
fnObjName := fsvc.Name fnObjName := fsvc.Name
@@ -634,7 +631,7 @@ func (caaf *Container) updateFuncDeployment(ctx context.Context, fn *fv1.Functio
} }
func (caaf *Container) fnDelete(ctx context.Context, fn *fv1.Function) error { func (caaf *Container) fnDelete(ctx context.Context, fn *fv1.Function) error {
multierr := &multierror.Error{} var multierr error
// GetByFunction uses resource version as part of cache key, however, // GetByFunction uses resource version as part of cache key, however,
// the resource version in function metadata will be changed when a function // the resource version in function metadata will be changed when a function
@@ -643,16 +640,14 @@ func (caaf *Container) fnDelete(ctx context.Context, fn *fv1.Function) error {
// fsvc entry. // fsvc entry.
fsvc, err := caaf.fsCache.GetByFunctionUID(fn.ObjectMeta.UID) fsvc, err := caaf.fsCache.GetByFunctionUID(fn.ObjectMeta.UID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("fsvc not found in cache: %v", fn.ObjectMeta)) return fmt.Errorf("fsvc not found in cache %s: %w", k8sCache.MetaObjectToName(fn), err)
return err
} }
objName := fsvc.Name objName := fsvc.Name
_, err = caaf.fsCache.DeleteOld(fsvc, time.Second*0) _, err = caaf.fsCache.DeleteOld(fsvc, time.Second*0)
if err != nil { if err != nil {
multierr = multierror.Append(multierr, multierr = errors.Join(multierr, fmt.Errorf("error deleting function from cache: %w", err))
errors.Wrapf(err, "error deleting the function from cache"))
} }
// to support backward compatibility, if the function was created in default ns, we fall back to creating the // to support backward compatibility, if the function was created in default ns, we fall back to creating the
@@ -660,9 +655,8 @@ func (caaf *Container) fnDelete(ctx context.Context, fn *fv1.Function) error {
ns := caaf.nsResolver.GetFunctionNS(fn.ObjectMeta.Namespace) ns := caaf.nsResolver.GetFunctionNS(fn.ObjectMeta.Namespace)
err = caaf.cleanupContainer(ctx, ns, objName) err = caaf.cleanupContainer(ctx, ns, objName)
multierr = multierror.Append(multierr, err) multierr = errors.Join(multierr, err)
return multierr
return multierr.ErrorOrNil()
} }
// getObjName returns a unique name for kubernetes objects of function // getObjName returns a unique name for kubernetes objects of function
@@ -231,7 +231,7 @@ func (cn *Container) getDeploymentSpec(ctx context.Context, fn *fv1.Function, ta
Exec: &apiv1.ExecAction{ Exec: &apiv1.ExecAction{
Command: []string{ Command: []string{
"/bin/sleep", "/bin/sleep",
fmt.Sprintf("%v", gracePeriodSeconds), fmt.Sprintf("%d", gracePeriodSeconds),
}, },
}, },
}, },
@@ -239,7 +239,7 @@ func (cn *Container) getDeploymentSpec(ctx context.Context, fn *fv1.Function, ta
Env: []apiv1.EnvVar{ Env: []apiv1.EnvVar{
{ {
Name: fv1.ResourceVersionCount, Name: fv1.ResourceVersionCount,
Value: fmt.Sprintf("%v", rvCount), Value: fmt.Sprintf("%d", rvCount),
}, },
}, },
EnvFrom: envFromSources, EnvFrom: envFromSources,
@@ -18,11 +18,11 @@ package newdeploy
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strconv" "strconv"
"time" "time"
multierror "github.com/hashicorp/go-multierror"
"go.uber.org/zap" "go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
@@ -195,7 +195,7 @@ func (deploy *NewDeploy) getDeploymentSpec(ctx context.Context, fn *fv1.Function
Exec: &apiv1.ExecAction{ Exec: &apiv1.ExecAction{
Command: []string{ Command: []string{
"/bin/sleep", "/bin/sleep",
fmt.Sprintf("%v", gracePeriodSeconds), fmt.Sprintf("%d", gracePeriodSeconds),
}, },
}, },
}, },
@@ -238,7 +238,7 @@ func (deploy *NewDeploy) getDeploymentSpec(ctx context.Context, fn *fv1.Function
if err == nil { if err == nil {
pod.Spec = *updatedPodSpec pod.Spec = *updatedPodSpec
} else { } else {
deploy.logger.Warn("Failed to merge the specs: %v", zap.Error(err)) deploy.logger.Warn("Failed to merge the specs", zap.Error(err))
} }
} }
@@ -419,7 +419,7 @@ func (deploy *NewDeploy) waitForDeploy(ctx context.Context, depl *appsv1.Deploym
// cleanupNewdeploy cleans all kubernetes objects related to function // cleanupNewdeploy cleans all kubernetes objects related to function
func (deploy *NewDeploy) cleanupNewdeploy(ctx context.Context, ns string, name string) error { func (deploy *NewDeploy) cleanupNewdeploy(ctx context.Context, ns string, name string) error {
result := &multierror.Error{} var result error
err := deploy.deleteSvc(ctx, ns, name) err := deploy.deleteSvc(ctx, ns, name)
if err != nil && !k8s_err.IsNotFound(err) { if err != nil && !k8s_err.IsNotFound(err) {
@@ -427,7 +427,7 @@ func (deploy *NewDeploy) cleanupNewdeploy(ctx context.Context, ns string, name s
zap.Error(err), zap.Error(err),
zap.String("function_name", name), zap.String("function_name", name),
zap.String("function_namespace", ns)) zap.String("function_namespace", ns))
result = multierror.Append(result, err) result = errors.Join(result, err)
} }
err = deploy.hpaops.DeleteHpa(ctx, ns, name) err = deploy.hpaops.DeleteHpa(ctx, ns, name)
@@ -436,7 +436,7 @@ func (deploy *NewDeploy) cleanupNewdeploy(ctx context.Context, ns string, name s
zap.Error(err), zap.Error(err),
zap.String("function_name", name), zap.String("function_name", name),
zap.String("function_namespace", ns)) zap.String("function_namespace", ns))
result = multierror.Append(result, err) result = errors.Join(result, err)
} }
err = deploy.deleteDeployment(ctx, ns, name) err = deploy.deleteDeployment(ctx, ns, name)
@@ -445,10 +445,10 @@ func (deploy *NewDeploy) cleanupNewdeploy(ctx context.Context, ns string, name s
zap.Error(err), zap.Error(err),
zap.String("function_name", name), zap.String("function_name", name),
zap.String("function_namespace", ns)) zap.String("function_namespace", ns))
result = multierror.Append(result, err) result = errors.Join(result, err)
} }
return result.ErrorOrNil() return result
} }
// referencedResourcesRVSum returns the sum of resource version of all resources the function references to. // referencedResourcesRVSum returns the sum of resource version of all resources the function references to.
@@ -18,6 +18,7 @@ package newdeploy
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
@@ -26,8 +27,6 @@ import (
"sync" "sync"
"time" "time"
multierror "github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
autoscalingv1 "k8s.io/api/autoscaling/v1" autoscalingv1 "k8s.io/api/autoscaling/v1"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
@@ -338,27 +337,27 @@ func (deploy *NewDeploy) AdoptExistingResources(ctx context.Context) {
func (deploy *NewDeploy) CleanupOldExecutorObjects(ctx context.Context) { func (deploy *NewDeploy) CleanupOldExecutorObjects(ctx context.Context) {
deploy.logger.Info("Newdeploy starts to clean orphaned resources", zap.String("instanceID", deploy.instanceID)) deploy.logger.Info("Newdeploy starts to clean orphaned resources", zap.String("instanceID", deploy.instanceID))
errs := &multierror.Error{} var errs error
listOpts := metav1.ListOptions{ listOpts := metav1.ListOptions{
LabelSelector: labels.Set(map[string]string{fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeNewdeploy)}).AsSelector().String(), LabelSelector: labels.Set(map[string]string{fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeNewdeploy)}).AsSelector().String(),
} }
err := reaper.CleanupHpa(ctx, deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts) err := reaper.CleanupHpa(ctx, deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts)
if err != nil { if err != nil {
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} }
err = reaper.CleanupDeployments(ctx, deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts) err = reaper.CleanupDeployments(ctx, deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts)
if err != nil { if err != nil {
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} }
err = reaper.CleanupServices(ctx, deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts) err = reaper.CleanupServices(ctx, deploy.logger, deploy.kubernetesClient, deploy.instanceID, listOpts)
if err != nil { if err != nil {
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} }
if errs.ErrorOrNil() != nil { if errs != nil {
// TODO retry reaper; logged and ignored for now // TODO retry reaper; logged and ignored for now
deploy.logger.Error("Failed to cleanup old executor objects", zap.Error(err)) deploy.logger.Error("Failed to cleanup old executor objects", zap.Error(err))
} }
@@ -398,7 +397,7 @@ func (deploy *NewDeploy) createFunction(ctx context.Context, fn *fv1.Function) (
zap.Error(err), zap.Error(err),
zap.String("function_name", fn.ObjectMeta.Name), zap.String("function_name", fn.ObjectMeta.Name),
zap.String("function_namespace", fn.ObjectMeta.Namespace)) zap.String("function_namespace", fn.ObjectMeta.Namespace))
return nil, errors.Wrapf(err, "%s %s_%s", e, fn.ObjectMeta.Name, fn.ObjectMeta.Namespace) return nil, fmt.Errorf("error creating k8s resources for function %s: %w", k8sCache.MetaObjectToName(fn), err)
} }
fsvc, ok := fsvcObj.(*fscache.FuncSvc) fsvc, ok := fsvcObj.(*fscache.FuncSvc)
@@ -416,9 +415,9 @@ func (deploy *NewDeploy) deleteFunction(ctx context.Context, fn *fv1.Function) e
} }
err := deploy.fnDelete(ctx, fn) err := deploy.fnDelete(ctx, fn)
if err != nil { if err != nil {
err = errors.Wrapf(err, "error deleting kubernetes objects of function %v", fn.ObjectMeta) return fmt.Errorf("error deleting kubernetes objects of function %s: %w", k8sCache.MetaObjectToName(fn), err)
} }
return err return nil
} }
func (deploy *NewDeploy) fnCreate(ctx context.Context, fn *fv1.Function) (*fscache.FuncSvc, error) { func (deploy *NewDeploy) fnCreate(ctx context.Context, fn *fv1.Function) (*fscache.FuncSvc, error) {
@@ -453,22 +452,22 @@ func (deploy *NewDeploy) fnCreate(ctx context.Context, fn *fv1.Function) (*fscac
if err != nil { if err != nil {
deploy.logger.Error("error creating service", zap.Error(err), zap.String("service", objName)) deploy.logger.Error("error creating service", zap.Error(err), zap.String("service", objName))
go cleanupFunc(context.Background(), ns, objName) go cleanupFunc(context.Background(), ns, objName)
return nil, errors.Wrapf(err, "error creating service %v", objName) return nil, fmt.Errorf("error creating service %s: %w", objName, err)
} }
svcAddress := fmt.Sprintf("%v.%v", svc.Name, svc.Namespace) svcAddress := fmt.Sprintf("%s.%s", svc.Name, svc.Namespace)
depl, err := deploy.createOrGetDeployment(ctx, fn, env, objName, deployLabels, deployAnnotations, ns) depl, err := deploy.createOrGetDeployment(ctx, fn, env, objName, deployLabels, deployAnnotations, ns)
if err != nil { if err != nil {
deploy.logger.Error("error creating deployment", zap.Error(err), zap.String("deployment", objName)) deploy.logger.Error("error creating deployment", zap.Error(err), zap.String("deployment", objName))
go cleanupFunc(context.Background(), ns, objName) go cleanupFunc(context.Background(), ns, objName)
return nil, errors.Wrapf(err, "error creating deployment %v", objName) return nil, fmt.Errorf("error creating deployment %s: %w", objName, err)
} }
hpa, err := deploy.hpaops.CreateOrGetHpa(ctx, objName, &fn.Spec.InvokeStrategy.ExecutionStrategy, depl, deployLabels, deployAnnotations) hpa, err := deploy.hpaops.CreateOrGetHpa(ctx, objName, &fn.Spec.InvokeStrategy.ExecutionStrategy, depl, deployLabels, deployAnnotations)
if err != nil { if err != nil {
deploy.logger.Error("error creating HPA", zap.Error(err), zap.String("hpa", objName)) deploy.logger.Error("error creating HPA", zap.Error(err), zap.String("hpa", objName))
go cleanupFunc(context.Background(), ns, objName) go cleanupFunc(context.Background(), ns, objName)
return nil, errors.Wrapf(err, "error creating the HPA %v", objName) return nil, fmt.Errorf("error creating HPA %s: %w", objName, err)
} }
kubeObjRefs := []apiv1.ObjectReference{ kubeObjRefs := []apiv1.ObjectReference{
@@ -564,8 +563,7 @@ func (deploy *NewDeploy) updateFunction(ctx context.Context, oldFn *fv1.Function
fsvc, err := deploy.fsCache.GetByFunctionUID(newFn.ObjectMeta.UID) fsvc, err := deploy.fsCache.GetByFunctionUID(newFn.ObjectMeta.UID)
if err != nil { if err != nil {
err = errors.Wrapf(err, "error updating function due to unable to find function service cache: %v", oldFn) return fmt.Errorf("error updating function due to unable to find function service cache %s: %w", k8sCache.MetaObjectToName(oldFn), err)
return err
} }
hpa, err := deploy.hpaops.GetHpa(ctx, ns, fsvc.Name) hpa, err := deploy.hpaops.GetHpa(ctx, ns, fsvc.Name)
@@ -651,8 +649,7 @@ func (deploy *NewDeploy) updateFunction(ctx context.Context, oldFn *fv1.Function
func (deploy *NewDeploy) updateFuncDeployment(ctx context.Context, fn *fv1.Function, env *fv1.Environment) error { func (deploy *NewDeploy) updateFuncDeployment(ctx context.Context, fn *fv1.Function, env *fv1.Environment) error {
fsvc, err := deploy.fsCache.GetByFunctionUID(fn.ObjectMeta.UID) fsvc, err := deploy.fsCache.GetByFunctionUID(fn.ObjectMeta.UID)
if err != nil { if err != nil {
err = errors.Wrapf(err, "error updating function due to unable to find function service cache: %v", fn) return fmt.Errorf("error updating function due to unable to find function service cache: %s: %w", k8sCache.MetaObjectToName(fn), err)
return err
} }
fnObjName := fsvc.Name fnObjName := fsvc.Name
@@ -690,7 +687,7 @@ func (deploy *NewDeploy) updateFuncDeployment(ctx context.Context, fn *fv1.Funct
} }
func (deploy *NewDeploy) fnDelete(ctx context.Context, fn *fv1.Function) error { func (deploy *NewDeploy) fnDelete(ctx context.Context, fn *fv1.Function) error {
multierr := &multierror.Error{} var errs error
// GetByFunction uses resource version as part of cache key, however, // GetByFunction uses resource version as part of cache key, however,
// the resource version in function metadata will be changed when a function // the resource version in function metadata will be changed when a function
@@ -699,16 +696,14 @@ func (deploy *NewDeploy) fnDelete(ctx context.Context, fn *fv1.Function) error {
// fsvc entry. // fsvc entry.
fsvc, err := deploy.fsCache.GetByFunctionUID(fn.ObjectMeta.UID) fsvc, err := deploy.fsCache.GetByFunctionUID(fn.ObjectMeta.UID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("fsvc not found in cache: %v", fn.ObjectMeta)) return fmt.Errorf("fsvc not found in cache: %s: %w", k8sCache.MetaObjectToName(fn), err)
return err
} }
objName := fsvc.Name objName := fsvc.Name
_, err = deploy.fsCache.DeleteOld(fsvc, time.Second*0) _, err = deploy.fsCache.DeleteOld(fsvc, time.Second*0)
if err != nil { if err != nil {
multierr = multierror.Append(multierr, errs = errors.Join(errs, fmt.Errorf("error deleting the function from cache"))
errors.Wrap(err, "error deleting the function from cache"))
} }
// to support backward compatibility, if the function was created in default ns, we fall back to creating the // to support backward compatibility, if the function was created in default ns, we fall back to creating the
@@ -716,9 +711,9 @@ func (deploy *NewDeploy) fnDelete(ctx context.Context, fn *fv1.Function) error {
ns := deploy.nsResolver.GetFunctionNS(fn.ObjectMeta.Namespace) ns := deploy.nsResolver.GetFunctionNS(fn.ObjectMeta.Namespace)
err = deploy.cleanupNewdeploy(ctx, ns, objName) err = deploy.cleanupNewdeploy(ctx, ns, objName)
multierr = multierror.Append(multierr, err) errs = errors.Join(errs, err)
return multierr.ErrorOrNil() return errs
} }
// getObjName returns a unique name for kubernetes objects of function // getObjName returns a unique name for kubernetes objects of function
+8 -8
View File
@@ -324,13 +324,13 @@ func (gp *GenericPool) choosePod(ctx context.Context, newLabels map[string]strin
// So we have to check both of them to ensure the patch success. // So we have to check both of them to ensure the patch success.
for k, v := range newLabels { for k, v := range newLabels {
if newPod.Labels[k] != v { if newPod.Labels[k] != v {
return "", nil, errors.Errorf("value of necessary labels '%v' mismatch: want '%v', get '%v'", return "", nil, errors.Errorf("value of necessary labels '%s' mismatch: want '%s', get '%v'",
k, v, newPod.Labels[k]) k, v, newPod.Labels[k])
} }
} }
for k, v := range annotations { for k, v := range annotations {
if newPod.Annotations[k] != v { if newPod.Annotations[k] != v {
return "", nil, errors.Errorf("value of necessary annotations '%v' mismatch: want '%v', get '%v'", return "", nil, errors.Errorf("value of necessary annotations '%s' mismatch: want '%s', get '%v'",
k, v, newPod.Annotations[k]) k, v, newPod.Annotations[k])
} }
} }
@@ -388,9 +388,9 @@ func (gp *GenericPool) getFetcherURL(podIP string) string {
var baseURL string var baseURL string
if isv6 { // We use bracket if the IP is in IPv6. if isv6 { // We use bracket if the IP is in IPv6.
baseURL = fmt.Sprintf("http://[%v]:8000/", podIP) baseURL = fmt.Sprintf("http://[%s]:8000/", podIP)
} else { } else {
baseURL = fmt.Sprintf("http://%v:8000/", podIP) baseURL = fmt.Sprintf("http://%s:8000/", podIP)
} }
return baseURL return baseURL
} }
@@ -440,7 +440,7 @@ func (gp *GenericPool) specializePod(ctx context.Context, pod *apiv1.Pod, fn *fv
// specialize pod with service // specialize pod with service
if gp.useIstio { if gp.useIstio {
svc := utils.GetFunctionIstioServiceName(fn.ObjectMeta.Name, fn.ObjectMeta.Namespace) svc := utils.GetFunctionIstioServiceName(fn.ObjectMeta.Name, fn.ObjectMeta.Namespace)
podIP = fmt.Sprintf("%v.%v", svc, gp.fnNamespace) podIP = fmt.Sprintf("%s.%s", svc, gp.fnNamespace)
} }
// tell fetcher to get the function. // tell fetcher to get the function.
@@ -548,7 +548,7 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
var svcHost string var svcHost string
if gp.useSvc && !gp.useIstio { if gp.useSvc && !gp.useIstio {
svcName := fmt.Sprintf("svc-%v", fn.ObjectMeta.Name) svcName := fmt.Sprintf("svc-%s", fn.ObjectMeta.Name)
if len(fn.ObjectMeta.UID) > 0 { if len(fn.ObjectMeta.UID) > 0 {
svcName = fmt.Sprintf("%s-%v", svcName, fn.ObjectMeta.UID) svcName = fmt.Sprintf("%s-%v", svcName, fn.ObjectMeta.UID)
} }
@@ -560,7 +560,7 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
} }
if svc.ObjectMeta.Name != svcName { if svc.ObjectMeta.Name != svcName {
go gp.scheduleDeletePod(context.Background(), pod.ObjectMeta.Name) go gp.scheduleDeletePod(context.Background(), pod.ObjectMeta.Name)
return nil, errors.Errorf("sanity check failed for svc %v", svc.ObjectMeta.Name) return nil, errors.Errorf("sanity check failed for svc %s", svc.ObjectMeta.Name)
} }
// the fission router isn't in the same namespace, so return a // the fission router isn't in the same namespace, so return a
@@ -575,7 +575,7 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
otelUtils.SpanTrackEvent(ctx, "addFunctionLabel", otelUtils.GetAttributesForPod(pod)...) otelUtils.SpanTrackEvent(ctx, "addFunctionLabel", otelUtils.GetAttributesForPod(pod)...)
// patch svc-host and resource version to the pod annotations for new executor to adopt the pod // patch svc-host and resource version to the pod annotations for new executor to adopt the pod
patch := fmt.Sprintf(`{"metadata":{"annotations":{"%v":"%v","%v":"%v"}}}`, patch := fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s","%s":"%s"}}}`,
fv1.ANNOTATION_SVC_HOST, svcHost, fv1.FUNCTION_RESOURCE_VERSION, fn.ObjectMeta.ResourceVersion) fv1.ANNOTATION_SVC_HOST, svcHost, fv1.FUNCTION_RESOURCE_VERSION, fn.ObjectMeta.ResourceVersion)
p, err := gp.kubernetesClient.CoreV1().Pods(pod.Namespace).Patch(ctx, pod.Name, k8sTypes.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{}) p, err := gp.kubernetesClient.CoreV1().Pods(pod.Namespace).Patch(ctx, pod.Name, k8sTypes.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{})
if err != nil { if err != nil {
@@ -114,7 +114,7 @@ func (gp *GenericPool) genDeploymentSpec(env *fv1.Environment) (*appsv1.Deployme
Exec: &apiv1.ExecAction{ Exec: &apiv1.ExecAction{
Command: []string{ Command: []string{
"/bin/sleep", "/bin/sleep",
fmt.Sprintf("%v", gracePeriodSeconds), fmt.Sprintf("%d", gracePeriodSeconds),
}, },
}, },
}, },
@@ -151,12 +151,11 @@ func (gp *GenericPool) genDeploymentSpec(env *fv1.Environment) (*appsv1.Deployme
} }
if gp.podSpecPatch != nil { if gp.podSpecPatch != nil {
updatedPodSpec, err := util.MergePodSpec(&pod.Spec, gp.podSpecPatch) updatedPodSpec, err := util.MergePodSpec(&pod.Spec, gp.podSpecPatch)
if err == nil { if err == nil {
pod.Spec = *updatedPodSpec pod.Spec = *updatedPodSpec
} else { } else {
gp.logger.Warn("Failed to merge the specs: %v", zap.Error(err)) gp.logger.Warn("Failed to merge the specs", zap.Error(err))
} }
} }
+8 -8
View File
@@ -18,6 +18,7 @@ package poolmgr
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"math/rand" "math/rand"
"os" "os"
@@ -27,7 +28,6 @@ import (
"time" "time"
"github.com/fission/fission/pkg/executor/metrics" "github.com/fission/fission/pkg/executor/metrics"
"github.com/hashicorp/go-multierror"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.uber.org/zap" "go.uber.org/zap"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
@@ -366,7 +366,7 @@ func (gpm *GenericPoolManager) AdoptExistingResources(ctx context.Context) {
} }
// create environment map for later use // create environment map for later use
key := fmt.Sprintf("%v/%v", env.ObjectMeta.Namespace, env.ObjectMeta.Name) key := fmt.Sprintf("%s/%s", env.ObjectMeta.Namespace, env.ObjectMeta.Name)
envMap[key] = env envMap[key] = env
} }
} }
@@ -398,7 +398,7 @@ func (gpm *GenericPoolManager) AdoptExistingResources(ctx context.Context) {
// avoid too many requests arrive Kubernetes API server at the same time. // avoid too many requests arrive Kubernetes API server at the same time.
time.Sleep(time.Duration(rand.Intn(30)) * time.Millisecond) time.Sleep(time.Duration(rand.Intn(30)) * time.Millisecond)
patch := fmt.Sprintf(`{"metadata":{"annotations":{"%v":"%v"}}}`, fv1.EXECUTOR_INSTANCEID_LABEL, gpm.instanceID) patch := fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"}}}`, fv1.EXECUTOR_INSTANCEID_LABEL, gpm.instanceID)
pod, err = gpm.kubernetesClient.CoreV1().Pods(pod.Namespace).Patch(ctx, pod.Name, k8sTypes.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{}) pod, err = gpm.kubernetesClient.CoreV1().Pods(pod.Namespace).Patch(ctx, pod.Name, k8sTypes.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{})
if err != nil { if err != nil {
// just log the error since it won't affect the function serving // just log the error since it won't affect the function serving
@@ -419,7 +419,7 @@ func (gpm *GenericPoolManager) AdoptExistingResources(ctx context.Context) {
envName, ok5 := pod.Labels[fv1.ENVIRONMENT_NAME] envName, ok5 := pod.Labels[fv1.ENVIRONMENT_NAME]
envNS, ok6 := pod.Labels[fv1.ENVIRONMENT_NAMESPACE] envNS, ok6 := pod.Labels[fv1.ENVIRONMENT_NAMESPACE]
svcHost, ok7 := pod.Annotations[fv1.ANNOTATION_SVC_HOST] svcHost, ok7 := pod.Annotations[fv1.ANNOTATION_SVC_HOST]
env, ok8 := envMap[fmt.Sprintf("%v/%v", envNS, envName)] env, ok8 := envMap[fmt.Sprintf("%s/%s", envNS, envName)]
if !(ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8) { if !(ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8) {
gpm.logger.Warn("failed to adopt pod for function due to lack of necessary information", gpm.logger.Warn("failed to adopt pod for function due to lack of necessary information",
@@ -476,22 +476,22 @@ func (gpm *GenericPoolManager) AdoptExistingResources(ctx context.Context) {
func (gpm *GenericPoolManager) CleanupOldExecutorObjects(ctx context.Context) { func (gpm *GenericPoolManager) CleanupOldExecutorObjects(ctx context.Context) {
gpm.logger.Info("Poolmanager starts to clean orphaned resources", zap.String("instanceID", gpm.instanceID)) gpm.logger.Info("Poolmanager starts to clean orphaned resources", zap.String("instanceID", gpm.instanceID))
errs := &multierror.Error{} var errs error
listOpts := metav1.ListOptions{ listOpts := metav1.ListOptions{
LabelSelector: labels.Set(map[string]string{fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypePoolmgr)}).AsSelector().String(), LabelSelector: labels.Set(map[string]string{fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypePoolmgr)}).AsSelector().String(),
} }
err := reaper.CleanupDeployments(ctx, gpm.logger, gpm.kubernetesClient, gpm.instanceID, listOpts) err := reaper.CleanupDeployments(ctx, gpm.logger, gpm.kubernetesClient, gpm.instanceID, listOpts)
if err != nil { if err != nil {
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} }
err = reaper.CleanupPods(ctx, gpm.logger, gpm.kubernetesClient, gpm.instanceID, listOpts) err = reaper.CleanupPods(ctx, gpm.logger, gpm.kubernetesClient, gpm.instanceID, listOpts)
if err != nil { if err != nil {
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} }
if errs.ErrorOrNil() != nil { if errs != nil {
// TODO retry reaper; logged and ignored for now // TODO retry reaper; logged and ignored for now
gpm.logger.Error("Failed to cleanup old executor objects", zap.Error(err)) gpm.logger.Error("Failed to cleanup old executor objects", zap.Error(err))
} }
+3 -6
View File
@@ -19,6 +19,7 @@ package fscache
import ( import (
"bufio" "bufio"
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
@@ -124,7 +125,7 @@ func (c *PoolCache) service() {
c.cache[req.function] = NewFuncSvcGroup() c.cache[req.function] = NewFuncSvcGroup()
c.cache[req.function].svcWaiting++ c.cache[req.function].svcWaiting++
resp.error = ferror.MakeError(ferror.ErrorNotFound, resp.error = ferror.MakeError(ferror.ErrorNotFound,
fmt.Sprintf("function Name '%v' not found", req.function)) fmt.Sprintf("function Name '%s' not found", req.function))
req.responseChannel <- resp req.responseChannel <- resp
continue continue
} }
@@ -325,11 +326,7 @@ func (c *PoolCache) service() {
} }
err := datawriter.Flush() err := datawriter.Flush()
if err != nil { if err != nil {
if resp.error == nil { resp.error = errors.Join(resp.error, err)
resp.error = err
} else {
resp.error = fmt.Errorf("%v, %v", resp.error, err)
}
} }
req.responseChannel <- resp req.responseChannel <- resp
default: default:
+17 -17
View File
@@ -17,11 +17,11 @@ limitations under the License.
package util package util
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
"dario.cat/mergo" "dario.cat/mergo"
"github.com/hashicorp/go-multierror"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
) )
@@ -40,18 +40,18 @@ func MergeContainer(dst *apiv1.Container, src *apiv1.Container) (*apiv1.Containe
// to prevent any modification to the original obj // to prevent any modification to the original obj
dstC := *dst dstC := *dst
errs := &multierror.Error{} var errs error
err := mergo.Merge(&dstC, src, mergo.WithAppendSlice, mergo.WithOverride) err := mergo.Merge(&dstC, src, mergo.WithAppendSlice, mergo.WithOverride)
if err != nil { if err != nil {
return nil, err return nil, err
} }
errs = multierror.Append(errs, errs = errors.Join(errs,
checkSliceConflicts("Name", dstC.Ports), checkSliceConflicts("Name", dstC.Ports),
checkSliceConflicts("Name", dstC.Env), checkSliceConflicts("Name", dstC.Env),
checkSliceConflicts("Name", dstC.VolumeMounts), checkSliceConflicts("Name", dstC.VolumeMounts),
checkSliceConflicts("Name", dstC.VolumeDevices)) checkSliceConflicts("Name", dstC.VolumeDevices))
return &dstC, errs.ErrorOrNil() return &dstC, errs
} }
// MergePodSpec updates srcPodSpec with targetPodSpec fields if not empty // MergePodSpec updates srcPodSpec with targetPodSpec fields if not empty
@@ -60,21 +60,21 @@ func MergePodSpec(srcPodSpec *apiv1.PodSpec, targetPodSpec *apiv1.PodSpec) (*api
return srcPodSpec, nil return srcPodSpec, nil
} }
multierr := &multierror.Error{} var multierr error
// Get item from spec, if they exist in deployment - merge, else append // Get item from spec, if they exist in deployment - merge, else append
// Same pattern for all lists (Mergo can not handle lists) // Same pattern for all lists (Mergo can not handle lists)
// TODO: At some point this is better done with generics/reflection? // TODO: At some point this is better done with generics/reflection?
cList, err := mergeContainerList(srcPodSpec.Containers, targetPodSpec.Containers) cList, err := mergeContainerList(srcPodSpec.Containers, targetPodSpec.Containers)
if err != nil { if err != nil {
multierr = multierror.Append(multierr, err) multierr = errors.Join(multierr, err)
} else { } else {
srcPodSpec.Containers = cList srcPodSpec.Containers = cList
} }
cList, err = mergeContainerList(srcPodSpec.InitContainers, targetPodSpec.InitContainers) cList, err = mergeContainerList(srcPodSpec.InitContainers, targetPodSpec.InitContainers)
if err != nil { if err != nil {
multierr = multierror.Append(multierr, err) multierr = errors.Join(multierr, err)
} else { } else {
srcPodSpec.InitContainers = cList srcPodSpec.InitContainers = cList
} }
@@ -82,7 +82,7 @@ func MergePodSpec(srcPodSpec *apiv1.PodSpec, targetPodSpec *apiv1.PodSpec) (*api
// For volumes - if duplicate exist, throw error // For volumes - if duplicate exist, throw error
vols, err := mergeVolumeLists(srcPodSpec.Volumes, targetPodSpec.Volumes) vols, err := mergeVolumeLists(srcPodSpec.Volumes, targetPodSpec.Volumes)
if err != nil { if err != nil {
multierr = multierror.Append(multierr, err) multierr = errors.Join(multierr, err)
} else { } else {
srcPodSpec.Volumes = vols srcPodSpec.Volumes = vols
} }
@@ -192,14 +192,14 @@ func MergePodSpec(srcPodSpec *apiv1.PodSpec, targetPodSpec *apiv1.PodSpec) (*api
err = mergo.Merge(&srcPodSpec.NodeSelector, targetPodSpec.NodeSelector) err = mergo.Merge(&srcPodSpec.NodeSelector, targetPodSpec.NodeSelector)
if err != nil { if err != nil {
multierr = multierror.Append(multierr, err) multierr = errors.Join(multierr, err)
} }
return srcPodSpec, multierr.ErrorOrNil() return srcPodSpec, multierr
} }
func mergeContainerList(dst []apiv1.Container, src []apiv1.Container) ([]apiv1.Container, error) { func mergeContainerList(dst []apiv1.Container, src []apiv1.Container) ([]apiv1.Container, error) {
errs := &multierror.Error{} var errs error
list := append(dst, src...) list := append(dst, src...)
containers := make(map[string]*apiv1.Container, len(list)) containers := make(map[string]*apiv1.Container, len(list))
@@ -210,7 +210,7 @@ func mergeContainerList(dst []apiv1.Container, src []apiv1.Container) ([]apiv1.C
newC, err := MergeContainer(container, &c) newC, err := MergeContainer(container, &c)
if err != nil { if err != nil {
// record the error and continue // record the error and continue
errs = multierror.Append(errs, err) errs = errors.Join(errs, err)
} else { } else {
containers[c.Name] = newC containers[c.Name] = newC
} }
@@ -224,8 +224,8 @@ func mergeContainerList(dst []apiv1.Container, src []apiv1.Container) ([]apiv1.C
containerList = append(containerList, *c) containerList = append(containerList, *c)
} }
if errs.ErrorOrNil() != nil { if errs != nil {
return nil, errs.ErrorOrNil() return nil, errs
} }
return containerList, nil return containerList, nil
@@ -252,7 +252,7 @@ func checkSliceConflicts(field string, objs interface{}) (err error) {
return fmt.Errorf("not a slice type: %v", reflect.TypeOf(objs)) return fmt.Errorf("not a slice type: %v", reflect.TypeOf(objs))
} }
errs := &multierror.Error{} var errs error
names := make(map[string]struct{}) names := make(map[string]struct{})
s := reflect.ValueOf(objs) s := reflect.ValueOf(objs)
@@ -281,10 +281,10 @@ func checkSliceConflicts(field string, objs interface{}) (err error) {
_, ok := names[f.String()] _, ok := names[f.String()]
if ok { if ok {
errs = multierror.Append(errs, fmt.Errorf("duplicate name in %v: %v", objType, f.String())) errs = errors.Join(errs, fmt.Errorf("duplicate name in %v: %v", objType, f.String()))
} else { } else {
names[f.String()] = struct{}{} names[f.String()] = struct{}{}
} }
} }
return errs.ErrorOrNil() return errs
} }
+4
View File
@@ -125,6 +125,10 @@ func ConvertConfigSecrets(ctx context.Context, fn *fv1.Function, kc kubernetes.I
} }
func GetSpecFromConfigMap(filePath string) (*apiv1.PodSpec, error) { func GetSpecFromConfigMap(filePath string) (*apiv1.PodSpec, error) {
// check if file exists
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil, err
}
content, err := os.ReadFile(filePath) content, err := os.ReadFile(filePath)
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading YAML file %s: %w", filePath, err) return nil, fmt.Errorf("error reading YAML file %s: %w", filePath, err)
+10 -2
View File
@@ -21,11 +21,20 @@ import (
"fmt" "fmt"
"os" "os"
"go.uber.org/zap"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
// GetFeatureConfig reads the configMap file and unmarshals the config into a feature config struct // GetFeatureConfig reads the configMap file and unmarshals the config into a feature config struct
func GetFeatureConfig() (*FeatureConfig, error) { func GetFeatureConfig(logger *zap.Logger) (*FeatureConfig, error) {
featureConfig := &FeatureConfig{}
// check if the file exists
if _, err := os.Stat(FeatureConfigFile); os.IsNotExist(err) {
logger.Warn("using empty feature config as file not found", zap.String("configPath", FeatureConfigFile))
return featureConfig, nil
}
// read the file // read the file
b64EncodedContent, err := os.ReadFile(FeatureConfigFile) b64EncodedContent, err := os.ReadFile(FeatureConfigFile)
if err != nil { if err != nil {
@@ -39,7 +48,6 @@ func GetFeatureConfig() (*FeatureConfig, error) {
} }
// unmarshal into feature config // unmarshal into feature config
featureConfig := &FeatureConfig{}
err = yaml.Unmarshal(yamlContent, featureConfig) err = yaml.Unmarshal(yamlContent, featureConfig)
if err != nil { if err != nil {
return nil, fmt.Errorf("error unmarshalling YAML config %v", err) return nil, fmt.Errorf("error unmarshalling YAML config %v", err)
+9 -9
View File
@@ -65,17 +65,17 @@ func (opts *TestSubCommand) do(input cli.Input) error {
Name: fnName, Name: fnName,
Namespace: namespace, Namespace: namespace,
} }
routerURL := os.Getenv("FISSION_ROUTER")
if len(routerURL) != 0 {
console.Warn("The environment variable FISSION_ROUTER is no longer supported for this command")
}
// Portforward to the fission router routerURL := os.Getenv("FISSION_ROUTER_URL")
localRouterPort, err := util.SetupPortForward(input.Context(), opts.Client(), util.GetFissionNamespace(), "application=fission-router") if len(routerURL) == 0 {
if err != nil { // Portforward to the fission router
return err localRouterPort, err := util.SetupPortForward(input.Context(), opts.Client(), util.GetFissionNamespace(), "application=fission-router")
if err != nil {
return err
}
routerURL = "http://127.0.0.1:" + localRouterPort
} }
fnURL := "http://127.0.0.1:" + localRouterPort + util.UrlForFunction(m.Name, m.Namespace) fnURL := routerURL + util.UrlForFunction(m.Name, m.Namespace)
if input.IsSet(flagkey.FnSubPath) { if input.IsSet(flagkey.FnSubPath) {
subPath := input.String(flagkey.FnSubPath) subPath := input.String(flagkey.FnSubPath)
if !strings.HasPrefix(subPath, "/") { if !strings.HasPrefix(subPath, "/") {
+1 -1
View File
@@ -140,7 +140,7 @@ func versionHandler(w http.ResponseWriter, r *http.Request) {
func (ts *HTTPTriggerSet) getRouter(fnTimeoutMap map[types.UID]int) (*mux.Router, error) { func (ts *HTTPTriggerSet) getRouter(fnTimeoutMap map[types.UID]int) (*mux.Router, error) {
featureConfig, err := config.GetFeatureConfig() featureConfig, err := config.GetFeatureConfig(ts.logger)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+111 -3
View File
@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/fission/fission/pkg/apis/core/v1"
"github.com/fission/fission/test/e2e/framework" "github.com/fission/fission/test/e2e/framework"
"github.com/fission/fission/test/e2e/framework/cli" "github.com/fission/fission/test/e2e/framework/cli"
"github.com/fission/fission/test/e2e/framework/services" "github.com/fission/fission/test/e2e/framework/services"
@@ -61,8 +62,10 @@ func TestFissionCLI(t *testing.T) {
envName := "test-func-env" envName := "test-func-env"
testFuncName := "hello" testFuncName := "hello"
testFuncNd := "hello-nd"
testFuncCn := "hello-cn"
t.Run("create", func(t *testing.T) { t.Run("create/poolmgr", func(t *testing.T) {
_, err = cli.ExecCommand(f, ctx, "env", "create", "--name", envName, "--image", "fission/python-env") _, err = cli.ExecCommand(f, ctx, "env", "create", "--name", envName, "--image", "fission/python-env")
require.NoError(t, err) require.NoError(t, err)
@@ -75,9 +78,33 @@ func TestFissionCLI(t *testing.T) {
require.NotNil(t, testFunc) require.NotNil(t, testFunc)
require.Equal(t, testFuncName, testFunc.Name) require.Equal(t, testFuncName, testFunc.Name)
require.Equal(t, envName, testFunc.Spec.Environment.Name) require.Equal(t, envName, testFunc.Spec.Environment.Name)
require.Equal(t, v1.ExecutorTypePoolmgr, testFunc.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType)
}) })
t.Run("update", func(t *testing.T) { t.Run("create/newdeploy", func(t *testing.T) {
_, err := cli.ExecCommand(f, ctx, "function", "create", "--name", testFuncNd, "--code", "./hello.js", "--env", envName, "--executortype", "newdeploy")
require.NoError(t, err)
testFunc, err := fissionClient.CoreV1().Functions(metav1.NamespaceDefault).Get(ctx, testFuncNd, metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, testFunc)
require.Equal(t, testFuncNd, testFunc.Name)
require.Equal(t, envName, testFunc.Spec.Environment.Name)
require.Equal(t, v1.ExecutorTypeNewdeploy, testFunc.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType)
})
t.Run("create/container", func(t *testing.T) {
_, err := cli.ExecCommand(f, ctx, "function", "run-container", "--name", testFuncCn, "--image", "gcr.io/google-samples/node-hello:1.0", "--port", "8080")
require.NoError(t, err)
testFunc, err := fissionClient.CoreV1().Functions(metav1.NamespaceDefault).Get(ctx, testFuncCn, metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, testFunc)
require.Equal(t, testFuncCn, testFunc.Name)
require.Equal(t, v1.ExecutorTypeContainer, testFunc.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType)
})
t.Run("update/poolmgr", func(t *testing.T) {
_, err := cli.ExecCommand(f, ctx, "function", "update", "--name", testFuncName, "--labels", "env=test") _, err := cli.ExecCommand(f, ctx, "function", "update", "--name", testFuncName, "--labels", "env=test")
require.NoError(t, err) require.NoError(t, err)
@@ -87,9 +114,41 @@ func TestFissionCLI(t *testing.T) {
require.Equal(t, testFuncName, testFunc.Name) require.Equal(t, testFuncName, testFunc.Name)
require.NotNil(t, testFunc.Labels) require.NotNil(t, testFunc.Labels)
require.Equal(t, "test", testFunc.Labels["env"]) require.Equal(t, "test", testFunc.Labels["env"])
require.Equal(t, v1.ExecutorTypePoolmgr, testFunc.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType)
}) })
t.Run("delete", func(t *testing.T) { // t.Run("test/poolmgr", func(t *testing.T) {
// _, err := cli.ExecCommand(f, ctx, "function", "test", "--name", testFuncName)
// require.NoError(t, err)
// })
// t.Run("test/newdeploy", func(t *testing.T) {
// _, err := cli.ExecCommand(f, ctx, "function", "test", "--name", testFuncNd)
// require.NoError(t, err)
// })
// t.Run("test/container", func(t *testing.T) {
// _, err := cli.ExecCommand(f, ctx, "function", "test", "--name", testFuncCn)
// require.NoError(t, err)
// })
t.Run("delete/newdeploy", func(t *testing.T) {
_, err := cli.ExecCommand(f, ctx, "function", "delete", "--name", testFuncNd)
require.NoError(t, err)
_, err = fissionClient.CoreV1().Functions(metav1.NamespaceDefault).Get(ctx, testFuncNd, metav1.GetOptions{})
require.Error(t, err)
})
t.Run("delete/container", func(t *testing.T) {
_, err := cli.ExecCommand(f, ctx, "function", "delete", "--name", testFuncCn)
require.NoError(t, err)
_, err = fissionClient.CoreV1().Functions(metav1.NamespaceDefault).Get(ctx, testFuncCn, metav1.GetOptions{})
require.Error(t, err)
})
t.Run("delete/poolmgr", func(t *testing.T) {
_, err := cli.ExecCommand(f, ctx, "function", "delete", "--name", testFuncName) _, err := cli.ExecCommand(f, ctx, "function", "delete", "--name", testFuncName)
require.NoError(t, err) require.NoError(t, err)
@@ -100,4 +159,53 @@ func TestFissionCLI(t *testing.T) {
}) })
}) })
t.Run("httptrigger", func(t *testing.T) {
t.Run("create", func(t *testing.T) {
// create env and function first
_, err := cli.ExecCommand(f, ctx, "env", "create", "--name", "test-func-env", "--image", "fission/python-env")
require.NoError(t, err)
_, err = cli.ExecCommand(f, ctx, "function", "create", "--name", "test-func", "--code", "./hello.js", "--env", "test-func-env")
require.NoError(t, err)
_, err = cli.ExecCommand(f, ctx, "httptrigger", "create", "--name", "test-httptrigger", "--function", "test-func", "--url", "/hello")
require.NoError(t, err)
ht, err := fissionClient.CoreV1().HTTPTriggers(metav1.NamespaceDefault).Get(ctx, "test-httptrigger", metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, ht)
require.Equal(t, "test-httptrigger", ht.Name)
require.Equal(t, "test-func", ht.Spec.FunctionReference.Name)
require.Equal(t, "/hello", ht.Spec.RelativeURL)
})
t.Run("update", func(t *testing.T) {
_, err := cli.ExecCommand(f, ctx, "httptrigger", "update", "--name", "test-httptrigger", "--url", "/hello2")
require.NoError(t, err)
ht, err := fissionClient.CoreV1().HTTPTriggers(metav1.NamespaceDefault).Get(ctx, "test-httptrigger", metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, ht)
require.Equal(t, "test-httptrigger", ht.Name)
require.Equal(t, "test-func", ht.Spec.FunctionReference.Name)
require.Equal(t, "/hello2", ht.Spec.RelativeURL)
})
t.Run("delete", func(t *testing.T) {
_, err := cli.ExecCommand(f, ctx, "httptrigger", "delete", "--name", "test-httptrigger")
require.NoError(t, err)
_, err = fissionClient.CoreV1().HTTPTriggers(metav1.NamespaceDefault).Get(ctx, "test-httptrigger", metav1.GetOptions{})
require.Error(t, err)
_, err = cli.ExecCommand(f, ctx, "function", "delete", "--name", "test-func")
require.NoError(t, err)
_, err = cli.ExecCommand(f, ctx, "env", "delete", "--name", "test-func-env")
require.NoError(t, err)
})
})
} }
+13
View File
@@ -22,6 +22,18 @@ func StartServices(ctx context.Context, f *framework.Framework) error {
if err != nil { if err != nil {
return fmt.Errorf("error toggling metric address: %v", err) return fmt.Errorf("error toggling metric address: %v", err)
} }
// namespace settings for components
os.Setenv("FISSION_BUILDER_NAMESPACE", "")
os.Setenv("FISSION_FUNCTION_NAMESPACE", "")
os.Setenv("FISSION_DEFAULT_NAMESPACE", "default")
os.Setenv("FISSION_RESOURCE_NAMESPACES", "default")
utils.DefaultNSResolver().DefaultNamespace = "default"
utils.DefaultNSResolver().FissionResourceNS = map[string]string{
"default": "default",
}
os.Setenv("POD_READY_TIMEOUT", "300s")
err = executor.StartExecutor(ctx, f.ClientGen(), f.Logger(), executorPort) err = executor.StartExecutor(ctx, f.ClientGen(), f.Logger(), executorPort)
if err != nil { if err != nil {
return fmt.Errorf("error starting executor: %v", err) return fmt.Errorf("error starting executor: %v", err)
@@ -88,5 +100,6 @@ func StartServices(ctx context.Context, f *framework.Framework) error {
f.ServiceInfo["router"] = framework.ServiceInfo{ f.ServiceInfo["router"] = framework.ServiceInfo{
Port: routerPort, Port: routerPort,
} }
os.Setenv("FISSION_ROUTER_URL", fmt.Sprintf("http://localhost:%d", routerPort))
return nil return nil
} }