diff --git a/pkg/apis/fission.io/v1/typefields.go b/pkg/apis/fission.io/v1/typefields.go index 49f9ce9d..ec723a12 100644 --- a/pkg/apis/fission.io/v1/typefields.go +++ b/pkg/apis/fission.io/v1/typefields.go @@ -167,10 +167,11 @@ type ( and resources allocated to the function pod. */ ExecutionStrategy struct { - ExecutorType ExecutorType - MinScale int - MaxScale int - TargetCPUPercent int + ExecutorType ExecutorType + MinScale int + MaxScale int + TargetCPUPercent int + SpecializationTimeout int } FunctionReferenceType string diff --git a/pkg/apis/fission.io/v1/validation.go b/pkg/apis/fission.io/v1/validation.go index c1db1c6f..4eddaf6d 100644 --- a/pkg/apis/fission.io/v1/validation.go +++ b/pkg/apis/fission.io/v1/validation.go @@ -346,6 +346,11 @@ func (es ExecutionStrategy) Validate() error { if es.TargetCPUPercent <= 0 || es.TargetCPUPercent > 100 { result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "ExecutionStrategy.TargetCPUPercent", es.TargetCPUPercent, "TargetCPUPercent must be a value between 1 - 100")) } + + // TODO Add validation warning + //if es.SpecializationTimeout < 120 { + // result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "ExecutionStrategy.SpecializationTimeout", es.SpecializationTimeout, "SpecializationTimeout must be a value equal to or greater than 120")) + //} } return result.ErrorOrNil() diff --git a/pkg/executor/newdeploy/newdeploy.go b/pkg/executor/newdeploy/newdeploy.go index 2109f4ec..f2e6ba2b 100644 --- a/pkg/executor/newdeploy/newdeploy.go +++ b/pkg/executor/newdeploy/newdeploy.go @@ -38,14 +38,16 @@ import ( ) const ( - DeploymentKind = "Deployment" - DeploymentVersion = "extensions/v1beta1" + DeploymentKind = "Deployment" + DeploymentVersion = "extensions/v1beta1" + DEFAULT_SPECIALIZATION_TIMEOUT = 120 ) func (deploy *NewDeploy) createOrGetDeployment(fn *fv1.Function, env *fv1.Environment, deployName string, deployLabels map[string]string, deployNamespace string, firstcreate bool) (*v1beta1.Deployment, error) { minScale := int32(fn.Spec.InvokeStrategy.ExecutionStrategy.MinScale) + specializationTimeout := int(fn.Spec.InvokeStrategy.ExecutionStrategy.SpecializationTimeout) // If it's not the first time creation and minscale is 0 means that all pods for function were recycled, // in such cases we need set minscale to 1 for router to serve requests. @@ -65,7 +67,7 @@ func (deploy *NewDeploy) createOrGetDeployment(fn *fv1.Function, env *fv1.Enviro } if existingDepl.Status.AvailableReplicas < minScale { - existingDepl, err = deploy.waitForDeploy(existingDepl, minScale) + existingDepl, err = deploy.waitForDeploy(existingDepl, minScale, specializationTimeout) } } return existingDepl, err @@ -93,7 +95,7 @@ func (deploy *NewDeploy) createOrGetDeployment(fn *fv1.Function, env *fv1.Enviro } if waitForDeploy { - depl, err = deploy.waitForDeploy(depl, minScale) + depl, err = deploy.waitForDeploy(depl, minScale, specializationTimeout) } return depl, err @@ -414,8 +416,13 @@ func (deploy *NewDeploy) deleteSvc(ns string, name string) error { return nil } -func (deploy *NewDeploy) waitForDeploy(depl *v1beta1.Deployment, replicas int32) (*v1beta1.Deployment, error) { - for i := 0; i < 120; i++ { +func (deploy *NewDeploy) waitForDeploy(depl *v1beta1.Deployment, replicas int32, specializationTimeout int) (*v1beta1.Deployment, error) { + // if no specializationTimeout is set, use default value + if specializationTimeout < DEFAULT_SPECIALIZATION_TIMEOUT { + specializationTimeout = DEFAULT_SPECIALIZATION_TIMEOUT + } + + for i := 0; i < specializationTimeout; i++ { latestDepl, err := deploy.kubernetesClient.ExtensionsV1beta1().Deployments(depl.ObjectMeta.Namespace).Get(depl.Name, metav1.GetOptions{}) if err != nil { return nil, err @@ -428,7 +435,10 @@ func (deploy *NewDeploy) waitForDeploy(depl *v1beta1.Deployment, replicas int32) } time.Sleep(time.Second) } - return nil, errors.New("failed to create deployment within timeout window") + + // this error appears in the executor pod logs + timeoutError := fmt.Errorf("failed to create deployment within the timeout window of %d seconds", specializationTimeout) + return nil, timeoutError } // cleanupNewdeploy cleans all kubernetes objects related to function diff --git a/pkg/fission-cli/function.go b/pkg/fission-cli/function.go index 09ad2193..71131de8 100644 --- a/pkg/fission-cli/function.go +++ b/pkg/fission-cli/function.go @@ -29,7 +29,7 @@ import ( "time" "github.com/fission/fission/pkg/types" - "github.com/satori/go.uuid" + uuid "github.com/satori/go.uuid" "github.com/urfave/cli" apiv1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -43,8 +43,9 @@ import ( ) const ( - DEFAULT_MIN_SCALE = 1 - DEFAULT_TARGET_CPU_PERCENTAGE = 80 + DEFAULT_MIN_SCALE = 1 + DEFAULT_TARGET_CPU_PERCENTAGE = 80 + DEFAULT_SPECIALIZATION_TIMEOUT = 120 ) func printPodLogs(c *cli.Context) error { @@ -101,6 +102,10 @@ func getInvokeStrategy(c *cli.Context, existingInvokeStrategy *fv1.InvokeStrateg fnExecutor = newFnExecutor } + if c.IsSet("specializationtimeout") && fnExecutor != types.ExecutorTypeNewdeploy { + return nil, errors.New("specializationtimeout flag is only applicable for newdeploy type of executor") + } + if fnExecutor == types.ExecutorTypePoolmgr { if c.IsSet("targetcpu") || c.IsSet("minscale") || c.IsSet("maxscale") { log.Fatal("To set target CPU or min/max scale for function, please specify \"--executortype newdeploy\"") @@ -120,11 +125,13 @@ func getInvokeStrategy(c *cli.Context, existingInvokeStrategy *fv1.InvokeStrateg targetCPU := DEFAULT_TARGET_CPU_PERCENTAGE minScale := DEFAULT_MIN_SCALE maxScale := minScale + specializationTimeout := DEFAULT_SPECIALIZATION_TIMEOUT if existingInvokeStrategy != nil && existingInvokeStrategy.ExecutionStrategy.ExecutorType == types.ExecutorTypeNewdeploy { minScale = existingInvokeStrategy.ExecutionStrategy.MinScale maxScale = existingInvokeStrategy.ExecutionStrategy.MaxScale targetCPU = existingInvokeStrategy.ExecutionStrategy.TargetCPUPercent + specializationTimeout = existingInvokeStrategy.ExecutionStrategy.SpecializationTimeout } if c.IsSet("targetcpu") { @@ -142,6 +149,13 @@ func getInvokeStrategy(c *cli.Context, existingInvokeStrategy *fv1.InvokeStrateg } } + if c.IsSet("specializationtimeout") { + specializationTimeout = c.Int("specializationtimeout") + if specializationTimeout < DEFAULT_SPECIALIZATION_TIMEOUT { + return nil, errors.New("specializationtimeout must be greater than or equal to 120 seconds") + } + } + if minScale > maxScale { return nil, fmt.Errorf("minscale provided: %v can not be greater than maxscale value %v", minScale, maxScale) } @@ -151,10 +165,11 @@ func getInvokeStrategy(c *cli.Context, existingInvokeStrategy *fv1.InvokeStrateg strategy = &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fnExecutor, - MinScale: minScale, - MaxScale: maxScale, - TargetCPUPercent: targetCPU, + ExecutorType: fnExecutor, + MinScale: minScale, + MaxScale: maxScale, + TargetCPUPercent: targetCPU, + SpecializationTimeout: specializationTimeout, }, } } @@ -489,6 +504,7 @@ func fnUpdate(c *cli.Context) error { secretName := c.String("secret") cfgMapName := c.String("configmap") + specializationTimeout := c.Int("specializationtimeout") if len(srcArchiveFiles) > 0 && len(deployArchiveFiles) > 0 { log.Fatal("Need either of --src or --deploy and not both arguments.") @@ -556,6 +572,19 @@ func fnUpdate(c *cli.Context) error { log.Fatal(err) } function.Spec.InvokeStrategy = *strategy + + if c.IsSet("specializationtimeout") { + if c.String("executortype") != types.ExecutorTypeNewdeploy { + log.Fatal("specializationtimeout flag is only applicable for newdeploy type of executor") + } + + if specializationTimeout < DEFAULT_SPECIALIZATION_TIMEOUT { + log.Fatal("specializationtimeout must be greater than or equal to 120 seconds") + } else { + function.Spec.InvokeStrategy.ExecutionStrategy.SpecializationTimeout = specializationTimeout + } + } + function.Spec.Resources = getResourceReq(c, function.Spec.Resources) pkg, err := client.PackageGet(&metav1.ObjectMeta{ diff --git a/pkg/fission-cli/function_test.go b/pkg/fission-cli/function_test.go index 74d87fd0..24957f2a 100644 --- a/pkg/fission-cli/function_test.go +++ b/pkg/fission-cli/function_test.go @@ -49,10 +49,11 @@ func TestGetInvokeStrategy(t *testing.T) { expectedResult: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: DEFAULT_MIN_SCALE, - MaxScale: DEFAULT_MIN_SCALE, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: DEFAULT_MIN_SCALE, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectError: false, @@ -69,10 +70,11 @@ func TestGetInvokeStrategy(t *testing.T) { expectedResult: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: DEFAULT_MIN_SCALE, - MaxScale: DEFAULT_MIN_SCALE, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: DEFAULT_MIN_SCALE, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectError: false, @@ -83,10 +85,11 @@ func TestGetInvokeStrategy(t *testing.T) { existingInvokeStrategy: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: DEFAULT_MIN_SCALE, - MaxScale: DEFAULT_MIN_SCALE, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: DEFAULT_MIN_SCALE, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectedResult: &fv1.InvokeStrategy{ @@ -108,10 +111,11 @@ func TestGetInvokeStrategy(t *testing.T) { expectedResult: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: 2, - MaxScale: 3, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 3, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectError: false, @@ -147,10 +151,11 @@ func TestGetInvokeStrategy(t *testing.T) { expectedResult: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: DEFAULT_MIN_SCALE, - MaxScale: 3, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: 3, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectError: false, @@ -174,19 +179,21 @@ func TestGetInvokeStrategy(t *testing.T) { existingInvokeStrategy: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: 2, - MaxScale: 5, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectedResult: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: 2, - MaxScale: 9, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 9, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectError: false, @@ -199,19 +206,21 @@ func TestGetInvokeStrategy(t *testing.T) { existingInvokeStrategy: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: 2, - MaxScale: 5, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectedResult: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: 2, - MaxScale: 5, - TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectError: false, @@ -226,10 +235,11 @@ func TestGetInvokeStrategy(t *testing.T) { expectedResult: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: DEFAULT_MIN_SCALE, - MaxScale: DEFAULT_MIN_SCALE, - TargetCPUPercent: 50, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: DEFAULT_MIN_SCALE, + TargetCPUPercent: 50, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectError: false, @@ -243,23 +253,72 @@ func TestGetInvokeStrategy(t *testing.T) { existingInvokeStrategy: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: 2, - MaxScale: 5, - TargetCPUPercent: 88, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: 88, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectedResult: &fv1.InvokeStrategy{ StrategyType: fv1.StrategyTypeExecution, ExecutionStrategy: fv1.ExecutionStrategy{ - ExecutorType: fv1.ExecutorTypeNewdeploy, - MinScale: 2, - MaxScale: 5, - TargetCPUPercent: 20, + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: 20, + SpecializationTimeout: DEFAULT_SPECIALIZATION_TIMEOUT, }, }, expectError: false, }, + { + // case: change specializationtimeout + testArgs: map[string]string{ + "executortype": fv1.ExecutorTypeNewdeploy, + "specializationtimeout": "200", + }, + existingInvokeStrategy: &fv1.InvokeStrategy{ + StrategyType: fv1.StrategyTypeExecution, + ExecutionStrategy: fv1.ExecutionStrategy{ + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectedResult: &fv1.InvokeStrategy{ + StrategyType: fv1.StrategyTypeExecution, + ExecutionStrategy: fv1.ExecutionStrategy{ + ExecutorType: fv1.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + SpecializationTimeout: 200, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectError: false, + }, + { + // case: specializationtimeout should not work for poolmgr + testArgs: map[string]string{ + "executortype": fv1.ExecutorTypePoolmgr, + "specializationtimeout": "10", + }, + existingInvokeStrategy: nil, + expectedResult: nil, + expectError: true, + }, + { + // case: specializationtimeout should not be less than 120 + testArgs: map[string]string{ + "executortype": fv1.ExecutorTypeNewdeploy, + "specializationtimeout": "90", + }, + existingInvokeStrategy: nil, + expectedResult: nil, + expectError: true, + }, } for i, c := range cases { diff --git a/pkg/fission-cli/main.go b/pkg/fission-cli/main.go index df5b7ce1..a20374ea 100644 --- a/pkg/fission-cli/main.go +++ b/pkg/fission-cli/main.go @@ -94,6 +94,7 @@ func NewCliApp() *cli.App { minScale := cli.IntFlag{Name: "minscale", Usage: "Minimum number of pods (Uses resource inputs to configure HPA)"} maxScale := cli.IntFlag{Name: "maxscale", Usage: "Maximum number of pods (Uses resource inputs to configure HPA)"} targetcpu := cli.IntFlag{Name: "targetcpu", Usage: "Target average CPU usage percentage across pods for scaling"} + specializationTimeoutFlag := cli.IntFlag{Name: "specializationtimeout, st", Usage: "Timeout for newdeploy to wait for function pod creation"} // functions fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"} @@ -119,10 +120,10 @@ func NewCliApp() *cli.App { fnTimeoutFlag := cli.DurationFlag{Name: "timeout, t", Value: 30 * time.Second, Usage: "The length of time to wait for the response. If set to zero or negative number, no timeout is set."} fnSubcommands := []cli.Command{ - {Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnNamespaceFlag, fnEnvNameFlag, envNamespaceFlag, specSaveFlag, fnCodeFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnBuildCmdFlag, fnPkgNameFlag, htUrlFlag, htMethodFlag, minCpu, maxCpu, minMem, maxMem, minScale, maxScale, fnExecutorTypeFlag, targetcpu, fnCfgMapFlag, fnSecretFlag}, Action: fnCreate}, + {Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnNamespaceFlag, fnEnvNameFlag, envNamespaceFlag, specSaveFlag, fnCodeFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnBuildCmdFlag, fnPkgNameFlag, htUrlFlag, htMethodFlag, minCpu, maxCpu, minMem, maxMem, minScale, maxScale, fnExecutorTypeFlag, targetcpu, fnCfgMapFlag, fnSecretFlag, specializationTimeoutFlag}, Action: fnCreate}, {Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag, fnNamespaceFlag}, Action: fnGet}, {Name: "getmeta", Usage: "Get function metadata", Flags: []cli.Flag{fnNameFlag, fnNamespaceFlag}, Action: fnGetMeta}, - {Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnNamespaceFlag, fnEnvNameFlag, envNamespaceFlag, fnCodeFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnPkgNameFlag, pkgNamespaceFlag, fnBuildCmdFlag, fnForceFlag, minCpu, maxCpu, minMem, maxMem, minScale, maxScale, fnExecutorTypeFlag, targetcpu}, Action: fnUpdate}, + {Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnNamespaceFlag, fnEnvNameFlag, envNamespaceFlag, fnCodeFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnPkgNameFlag, pkgNamespaceFlag, fnBuildCmdFlag, fnForceFlag, minCpu, maxCpu, minMem, maxMem, minScale, maxScale, fnExecutorTypeFlag, targetcpu, specializationTimeoutFlag}, Action: fnUpdate}, {Name: "delete", Usage: "Delete function", Flags: []cli.Flag{fnNameFlag, fnNamespaceFlag}, Action: fnDelete}, // TODO : for fnList, i feel like it's nice to allow --fns all, to list functions across all namespaces for cluster admins, although, this is against ns isolation. // so, in the future, if we end up using kubeconfig in fission cli and enforcing rolebindings to be created for users by admins etc, we can add this option at the time. diff --git a/pkg/fission-cli/spec.go b/pkg/fission-cli/spec.go index ec9ef25a..736aa729 100644 --- a/pkg/fission-cli/spec.go +++ b/pkg/fission-cli/spec.go @@ -437,6 +437,10 @@ func (fr *FissionResources) validate(c *cli.Context) error { if _, ok := environments[fmt.Sprintf("%s:%s", f.Spec.Environment.Name, f.Spec.Environment.Namespace)]; !ok { log.Warn(fmt.Sprintf("Environment %s is referenced in function %s but not declared in specs", f.Spec.Environment.Name, f.Metadata.Name)) } + strategy := f.Spec.InvokeStrategy.ExecutionStrategy + if strategy.ExecutorType == fv1.ExecutorTypeNewdeploy && strategy.SpecializationTimeout < DEFAULT_SPECIALIZATION_TIMEOUT { + log.Warn(fmt.Sprintf("SpecializationTimeout in function spec.InvokeStrategy.ExecutionStrategy should be a value equal to or greater than %v", DEFAULT_SPECIALIZATION_TIMEOUT)) + } } // (ErrorOrNil returns nil if there were no errors appended.)