From 7cedf8d580e7f5a76ca7325a4ec3644e8806d5e6 Mon Sep 17 00:00:00 2001 From: Ta-Ching Chen Date: Thu, 29 Nov 2018 20:46:48 +0800 Subject: [PATCH] Fix issues when specifying resources/scales during updating/creation process (#970) --- executor/newdeploy/newdeploy.go | 16 +- fission/environment.go | 24 +++ fission/function.go | 183 +++++++++-------- fission/function_test.go | 289 +++++++++++++++++++++++++++ fission/log/log.go | 4 +- fission/main.go | 22 +- pkg/apis/fission.io/v1/validation.go | 22 +- 7 files changed, 448 insertions(+), 112 deletions(-) create mode 100644 fission/function_test.go diff --git a/executor/newdeploy/newdeploy.go b/executor/newdeploy/newdeploy.go index a1c8ca1f..36fd13e4 100644 --- a/executor/newdeploy/newdeploy.go +++ b/executor/newdeploy/newdeploy.go @@ -361,23 +361,23 @@ func (deploy *NewDeploy) getResources(env *crd.Environment, fn *crd.Function) ap resources.Limits = make(map[apiv1.ResourceName]resource.Quantity) } // Only override the once specified at function, rest default to values from env. - _, ok := fn.Spec.Resources.Requests[apiv1.ResourceCPU] - if ok { + val, ok := fn.Spec.Resources.Requests[apiv1.ResourceCPU] + if ok && !val.IsZero() { resources.Requests[apiv1.ResourceCPU] = fn.Spec.Resources.Requests[apiv1.ResourceCPU] } - _, ok = fn.Spec.Resources.Requests[apiv1.ResourceMemory] - if ok { + val, ok = fn.Spec.Resources.Requests[apiv1.ResourceMemory] + if ok && !val.IsZero() { resources.Requests[apiv1.ResourceMemory] = fn.Spec.Resources.Requests[apiv1.ResourceMemory] } - _, ok = fn.Spec.Resources.Limits[apiv1.ResourceCPU] - if ok { + val, ok = fn.Spec.Resources.Limits[apiv1.ResourceCPU] + if ok && !val.IsZero() { resources.Limits[apiv1.ResourceCPU] = fn.Spec.Resources.Limits[apiv1.ResourceCPU] } - _, ok = fn.Spec.Resources.Limits[apiv1.ResourceMemory] - if ok { + val, ok = fn.Spec.Resources.Limits[apiv1.ResourceMemory] + if ok && !val.IsZero() { resources.Limits[apiv1.ResourceMemory] = fn.Spec.Resources.Limits[apiv1.ResourceMemory] } diff --git a/fission/environment.go b/fission/environment.go index 461b6d2b..2c67da4d 100644 --- a/fission/environment.go +++ b/fission/environment.go @@ -218,6 +218,10 @@ func envUpdate(c *cli.Context) error { env.Spec.AllowAccessToExternalNetwork = envExternalNetwork + if c.IsSet("mincpu") || c.IsSet("maxcpu") || c.IsSet("minmemory") || c.IsSet("maxmemory") || c.IsSet("minscale") || c.IsSet("maxscale") { + log.Fatal("Updating resource limits/requests for existing environments is currently unsupported; re-create the environment instead.") + } + _, err = client.EnvironmentUpdate(env) util.CheckErr(err, "update environment") @@ -295,6 +299,7 @@ func getResourceReq(c *cli.Context, resources v1.ResourceRequirements) v1.Resour } var limitResources map[v1.ResourceName]resource.Quantity + if len(resources.Limits) == 0 { limitResources = make(map[v1.ResourceName]resource.Quantity) } else { @@ -319,9 +324,28 @@ func getResourceReq(c *cli.Context, resources v1.ResourceRequirements) v1.Resour limitResources[v1.ResourceMemory] = memLimit } + limitCPU := limitResources[v1.ResourceCPU] + requestCPU := requestResources[v1.ResourceCPU] + + if limitCPU.IsZero() && !requestCPU.IsZero() { + limitResources[v1.ResourceCPU] = requestCPU + } else if limitCPU.Cmp(requestCPU) < 0 { + log.Fatal(fmt.Sprintf("MinCPU (%v) cannot be greater than MaxCPU (%v)", requestCPU.String(), limitCPU.String())) + } + + limitMem := limitResources[v1.ResourceMemory] + requestMem := requestResources[v1.ResourceMemory] + + if limitMem.IsZero() && !requestMem.IsZero() { + limitResources[v1.ResourceMemory] = requestMem + } else if limitMem.Cmp(requestMem) < 0 { + log.Fatal(fmt.Sprintf("MinMemory (%v) cannot be greater than MaxMemory (%v)", requestMem.String(), limitMem.String())) + } + resources = v1.ResourceRequirements{ Requests: requestResources, Limits: limitResources, } + return resources } diff --git a/fission/function.go b/fission/function.go index 7e9e0bae..bb7df223 100644 --- a/fission/function.go +++ b/fission/function.go @@ -41,6 +41,11 @@ import ( "github.com/fission/fission/fission/util" ) +const ( + DEFAULT_MIN_SCALE = 1 + DEFAULT_TARGET_CPU_PERCENTAGE = 80 +) + func printPodLogs(c *cli.Context) error { fnName := c.String("name") if len(fnName) == 0 { @@ -69,40 +74,91 @@ func printPodLogs(c *cli.Context) error { return nil } -func getInvokeStrategy(minScale int, maxScale int, executorType string, targetcpu int) fission.InvokeStrategy { +func getInvokeStrategy(c *cli.Context, existingInvokeStrategy *fission.InvokeStrategy) (strategy *fission.InvokeStrategy, err error) { - if maxScale == 0 { - maxScale = 1 - } + var fnExecutor, newFnExecutor fission.ExecutorType - if minScale > maxScale { - log.Fatal("Maxscale must be higher than or equal to minscale") - } - - var fnExecutor fission.ExecutorType - switch executorType { + switch c.String("executortype") { case "": - fnExecutor = fission.ExecutorTypePoolmgr + fallthrough case fission.ExecutorTypePoolmgr: - fnExecutor = fission.ExecutorTypePoolmgr + newFnExecutor = fission.ExecutorTypePoolmgr case fission.ExecutorTypeNewdeploy: - fnExecutor = fission.ExecutorTypeNewdeploy + newFnExecutor = fission.ExecutorTypeNewdeploy default: - log.Fatal("Executor type must be one of 'poolmgr' or 'newdeploy', defaults to 'poolmgr'") + return nil, errors.New("Executor type must be one of 'poolmgr' or 'newdeploy', defaults to 'poolmgr'") } - // Right now a simple single case strategy implementation - // This will potentially get more sophisticated once we have more strategies in place - strategy := fission.InvokeStrategy{ - StrategyType: fission.StrategyTypeExecution, - ExecutionStrategy: fission.ExecutionStrategy{ - ExecutorType: fnExecutor, - MinScale: minScale, - MaxScale: maxScale, - TargetCPUPercent: targetcpu, - }, + if existingInvokeStrategy != nil { + fnExecutor = existingInvokeStrategy.ExecutionStrategy.ExecutorType + + // override the executor type if user specified a new executor type + if c.IsSet("executortype") { + fnExecutor = newFnExecutor + } + } else { + fnExecutor = newFnExecutor } - return strategy + + if fnExecutor == fission.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\"") + } + + if c.IsSet("mincpu") || c.IsSet("maxcpu") || c.IsSet("minmemory") || c.IsSet("maxmemory") { + log.Warn("To limit CPU/Memory for function with executor type \"poolmgr\", please specify resources limits when creating environment") + } + strategy = &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypePoolmgr, + }, + } + } else { + // set default value + targetCPU := DEFAULT_TARGET_CPU_PERCENTAGE + minScale := DEFAULT_MIN_SCALE + maxScale := minScale + + if existingInvokeStrategy != nil && existingInvokeStrategy.ExecutionStrategy.ExecutorType == fission.ExecutorTypeNewdeploy { + minScale = existingInvokeStrategy.ExecutionStrategy.MinScale + maxScale = existingInvokeStrategy.ExecutionStrategy.MaxScale + targetCPU = existingInvokeStrategy.ExecutionStrategy.TargetCPUPercent + } + + if c.IsSet("targetcpu") { + targetCPU = getTargetCPU(c) + } + + if c.IsSet("minscale") { + minScale = c.Int("minscale") + } + + if c.IsSet("maxscale") { + maxScale = c.Int("maxscale") + if maxScale <= 0 { + return nil, errors.New("Maxscale must be greater than 0") + } + } + + if minScale > maxScale { + return nil, errors.New(fmt.Sprintf("Minscale provided: %v can not be greater than maxscale value %v", minScale, maxScale)) + } + + // Right now a simple single case strategy implementation + // This will potentially get more sophisticated once we have more strategies in place + strategy = &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fnExecutor, + MinScale: minScale, + MaxScale: maxScale, + TargetCPUPercent: targetCPU, + }, + } + } + + return strategy, nil } func getTargetCPU(c *cli.Context) int { @@ -113,7 +169,7 @@ func getTargetCPU(c *cli.Context) int { log.Fatal("TargetCPU must be a value between 1 - 100") } } else { - targetCPU = 80 + targetCPU = DEFAULT_TARGET_CPU_PERCENTAGE } return targetCPU } @@ -150,12 +206,17 @@ func fnCreate(c *cli.Context) error { entrypoint := c.String("entrypoint") pkgName := c.String("pkg") - var pkgMetadata *metav1.ObjectMeta - var envName string - secretName := c.String("secret") cfgMapName := c.String("configmap") + invokeStrategy, err := getInvokeStrategy(c, nil) + if err != nil { + log.Fatal(err) + } + resourceReq := getResourceReq(c, apiv1.ResourceRequirements{}) + + var pkgMetadata *metav1.ObjectMeta + var envName string if len(pkgName) > 0 { // use existing package pkg, err := client.PackageGet(&metav1.ObjectMeta{ @@ -209,13 +270,6 @@ func fnCreate(c *cli.Context) error { fmt.Printf("package '%v' created\n", pkgMetadata.Name) } - invokeStrategy := getInvokeStrategy(c.Int("minscale"), c.Int("maxscale"), c.String("executortype"), getTargetCPU(c)) - resourceReq := getResourceReq(c, apiv1.ResourceRequirements{}) - if (c.IsSet("mincpu") || c.IsSet("maxcpu") || c.IsSet("minmemory") || c.IsSet("maxmemory")) && - invokeStrategy.ExecutionStrategy.ExecutorType == fission.ExecutorTypePoolmgr { - log.Warn("CPU/Memory specified for function with pool manager executor will be ignored in favor of resources specified at environment") - } - var secrets []fission.SecretReference var cfgmaps []fission.ConfigMapReference @@ -274,7 +328,7 @@ func fnCreate(c *cli.Context) error { Secrets: secrets, ConfigMaps: cfgmaps, Resources: resourceReq, - InvokeStrategy: invokeStrategy, + InvokeStrategy: *invokeStrategy, }, } @@ -486,6 +540,13 @@ func fnUpdate(c *cli.Context) error { pkgName = function.Spec.Package.PackageRef.Name } + strategy, err := getInvokeStrategy(c, &function.Spec.InvokeStrategy) + if err != nil { + log.Fatal(err) + } + function.Spec.InvokeStrategy = *strategy + function.Spec.Resources = getResourceReq(c, function.Spec.Resources) + pkg, err := client.PackageGet(&metav1.ObjectMeta{ Namespace: fnNamespace, Name: pkgName, @@ -534,54 +595,6 @@ func fnUpdate(c *cli.Context) error { function.Spec.Environment.Namespace = pkg.Spec.Environment.Namespace } - function.Spec.Resources = getResourceReq(c, function.Spec.Resources) - - if c.IsSet("targetcpu") { - function.Spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent = getTargetCPU(c) - } - - if c.IsSet("minscale") { - minscale := c.Int("minscale") - maxscale := c.Int("maxscale") - if c.IsSet("maxscale") && minscale > c.Int("maxscale") { - log.Fatal(fmt.Sprintf("Minscale's value %v can not be greater than maxscale value %v", minscale, maxscale)) - } - if function.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType != fission.ExecutorTypePoolmgr && - minscale > function.Spec.InvokeStrategy.ExecutionStrategy.MaxScale { - log.Fatal(fmt.Sprintf("Minscale provided: %v can not be greater than maxscale of existing function: %v", minscale, - function.Spec.InvokeStrategy.ExecutionStrategy.MaxScale)) - } - function.Spec.InvokeStrategy.ExecutionStrategy.MinScale = minscale - } - - if c.IsSet("maxscale") { - maxscale := c.Int("maxscale") - if maxscale < function.Spec.InvokeStrategy.ExecutionStrategy.MinScale { - log.Fatal(fmt.Sprintf("Function's minscale: %v can not be greater than maxscale provided: %v", - function.Spec.InvokeStrategy.ExecutionStrategy.MinScale, maxscale)) - } - function.Spec.InvokeStrategy.ExecutionStrategy.MaxScale = maxscale - } - - if c.IsSet("executortype") { - var fnExecutor fission.ExecutorType - switch c.String("executortype") { - case "": - fnExecutor = fission.ExecutorTypePoolmgr - case fission.ExecutorTypePoolmgr: - fnExecutor = fission.ExecutorTypePoolmgr - case fission.ExecutorTypeNewdeploy: - fnExecutor = fission.ExecutorTypeNewdeploy - default: - log.Fatal("Executor type must be one of 'poolmgr' or 'newdeploy', defaults to 'poolmgr'") - } - if (c.IsSet("mincpu") || c.IsSet("maxcpu") || c.IsSet("minmemory") || c.IsSet("maxmemory")) && - fnExecutor == fission.ExecutorTypePoolmgr { - log.Warn("CPU/Memory specified for function with pool manager executor will be ignored in favor of resources specified at environment") - } - function.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType = fnExecutor - } - _, err = client.FunctionUpdate(function) util.CheckErr(err, "update function") diff --git a/fission/function_test.go b/fission/function_test.go new file mode 100644 index 00000000..aa41c932 --- /dev/null +++ b/fission/function_test.go @@ -0,0 +1,289 @@ +package main + +import ( + "flag" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" + + "github.com/fission/fission" +) + +func TestGetInvokeStrategy(t *testing.T) { + cases := []struct { + testArgs map[string]string + existingInvokeStrategy *fission.InvokeStrategy + expectedResult *fission.InvokeStrategy + expectError bool + }{ + { + // case: use default executor poolmgr + testArgs: map[string]string{}, + existingInvokeStrategy: nil, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypePoolmgr, + }, + }, + expectError: false, + }, + { + // case: executor type set to poolmgr + testArgs: map[string]string{"executortype": fission.ExecutorTypePoolmgr}, + existingInvokeStrategy: nil, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypePoolmgr, + }, + }, + expectError: false, + }, + { + // case: executor type set to newdeploy + testArgs: map[string]string{"executortype": fission.ExecutorTypeNewdeploy}, + existingInvokeStrategy: nil, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: DEFAULT_MIN_SCALE, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectError: false, + }, + { + // case: executor type change from poolmgr to newdeploy + testArgs: map[string]string{"executortype": fission.ExecutorTypeNewdeploy}, + existingInvokeStrategy: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypePoolmgr, + }, + }, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: DEFAULT_MIN_SCALE, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectError: false, + }, + { + // case: executor type change from newdeploy to poolmgr + testArgs: map[string]string{"executortype": fission.ExecutorTypePoolmgr}, + existingInvokeStrategy: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: DEFAULT_MIN_SCALE, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypePoolmgr, + }, + }, + expectError: false, + }, + { + // case: minscale < maxscale + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + "minscale": "2", + "maxscale": "3", + }, + existingInvokeStrategy: nil, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 3, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectError: false, + }, + { + // case: minscale > maxscale + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + "minscale": "5", + "maxscale": "3", + }, + existingInvokeStrategy: nil, + expectedResult: nil, + expectError: true, + }, + { + // case: maxscale not specified + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + "minscale": "5", + }, + existingInvokeStrategy: nil, + expectedResult: nil, + expectError: true, + }, + { + // case: minscale not specified + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + "maxscale": "3", + }, + existingInvokeStrategy: nil, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: 3, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectError: false, + }, + { + // case: maxscale set to 0 + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + "maxscale": "0", + }, + existingInvokeStrategy: nil, + expectedResult: nil, + expectError: true, + }, + { + // case: maxscale set to 9 when existing is 5 + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + "maxscale": "9", + }, + existingInvokeStrategy: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 9, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectError: false, + }, + { + // case: change nothing for existing strategy + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + }, + existingInvokeStrategy: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: DEFAULT_TARGET_CPU_PERCENTAGE, + }, + }, + expectError: false, + }, + { + // case: set target cpu percentage + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + "targetcpu": "50", + }, + existingInvokeStrategy: nil, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: DEFAULT_MIN_SCALE, + MaxScale: DEFAULT_MIN_SCALE, + TargetCPUPercent: 50, + }, + }, + expectError: false, + }, + { + // case: change target cpu percentage + testArgs: map[string]string{ + "executortype": fission.ExecutorTypeNewdeploy, + "targetcpu": "20", + }, + existingInvokeStrategy: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: 88, + }, + }, + expectedResult: &fission.InvokeStrategy{ + StrategyType: fission.StrategyTypeExecution, + ExecutionStrategy: fission.ExecutionStrategy{ + ExecutorType: fission.ExecutorTypeNewdeploy, + MinScale: 2, + MaxScale: 5, + TargetCPUPercent: 20, + }, + }, + expectError: false, + }, + } + + for i, c := range cases { + fmt.Printf("=== Test Case %v ===\n", i) + + app := newCliApp() + set := flag.NewFlagSet("test-cmd", 0) + ctx := cli.NewContext(app, set, nil) + + for k, v := range c.testArgs { + set.String(k, v, "") + ctx.Set(k, v) + } + + strategy, err := getInvokeStrategy(ctx, c.existingInvokeStrategy) + if c.expectError { + assert.NotNil(t, err) + if err != nil { + fmt.Println(err) + } + } else { + assert.Nil(t, err) + assert.NoError(t, strategy.Validate(), fmt.Sprintf("Failed at test case %v", i)) + assert.Equal(t, *c.expectedResult, *strategy) + } + } +} diff --git a/fission/log/log.go b/fission/log/log.go index b9da0447..b941d707 100644 --- a/fission/log/log.go +++ b/fission/log/log.go @@ -27,12 +27,12 @@ var ( ) func Fatal(msg interface{}) { - os.Stderr.WriteString(fmt.Sprintf("%v\n", msg)) + os.Stderr.WriteString(fmt.Sprintf("Fatal error: %v\n", msg)) os.Exit(1) } func Warn(msg interface{}) { - os.Stderr.WriteString(fmt.Sprintf("[WARNING] %v\n", msg)) + os.Stderr.WriteString(fmt.Sprintf("Warning: %v\n", msg)) } func Info(msg interface{}) { diff --git a/fission/main.go b/fission/main.go index d3723883..31bc5072 100644 --- a/fission/main.go +++ b/fission/main.go @@ -38,6 +38,10 @@ func cliHook(c *cli.Context) error { } func main() { + newCliApp().Run(os.Args) +} + +func newCliApp() *cli.App { app := cli.NewApp() app.Name = "fission" app.Usage = "Serverless functions for Kubernetes" @@ -74,13 +78,13 @@ func main() { htUrlFlag := cli.StringFlag{Name: "url", Usage: "URL pattern (See gorilla/mux supported patterns)"} // Resource & scale related flags (Used in env and function) - minCpu := cli.StringFlag{Name: "mincpu", Usage: "Minimum CPU to be assigned to pod (In millicore, minimum 1)"} - maxCpu := cli.StringFlag{Name: "maxcpu", Usage: "Maximum CPU to be assigned to pod (In millicore, minimum 1)"} - minMem := cli.StringFlag{Name: "minmemory", Usage: "Minimum memory to be assigned to pod (In megabyte)"} - maxMem := cli.StringFlag{Name: "maxmemory", Usage: "Maximum memory to be assigned to pod (In megabyte)"} - minScale := cli.StringFlag{Name: "minscale", Usage: "Minimum number of pods (Uses resource inputs to configure HPA)"} - maxScale := cli.StringFlag{Name: "maxscale", Usage: "Maximum number of pods (Uses resource inputs to configure HPA)"} - targetcpu := cli.IntFlag{Name: "targetcpu", Value: 80, Usage: "Target average CPU usage percentage across pods for scaling"} + minCpu := cli.IntFlag{Name: "mincpu", Usage: "Minimum CPU to be assigned to pod (In millicore, minimum 1)"} + maxCpu := cli.IntFlag{Name: "maxcpu", Usage: "Maximum CPU to be assigned to pod (In millicore, minimum 1)"} + minMem := cli.IntFlag{Name: "minmemory", Usage: "Minimum memory to be assigned to pod (In megabyte)"} + maxMem := cli.IntFlag{Name: "maxmemory", Usage: "Maximum memory to be assigned to pod (In megabyte)"} + 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"} // functions fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"} @@ -102,7 +106,7 @@ func main() { fnCfgMapFlag := cli.StringFlag{Name: "configmap", Usage: "function access to configmap, should be present in the same namespace as the function"} fnLogCountFlag := cli.StringFlag{Name: "recordcount", Usage: "the n most recent log records"} fnForceFlag := cli.BoolFlag{Name: "force", Usage: "Force update a package even if it is used by one or more functions"} - fnExecutorTypeFlag := cli.StringFlag{Name: "executortype", Usage: "Executor type for execution; one of 'poolmgr', 'newdeploy' defaults to 'poolmgr'"} + fnExecutorTypeFlag := cli.StringFlag{Name: "executortype", Value: fission.ExecutorTypePoolmgr, Usage: "Executor type for execution; one of 'poolmgr', 'newdeploy' defaults to 'poolmgr'"} 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}, @@ -309,7 +313,7 @@ func main() { } app.Before = cliHook app.CommandNotFound = handleCommandNotFound - app.Run(os.Args) + return app } func handleCommandNotFound(ctx *cli.Context, subCommand string) { diff --git a/pkg/apis/fission.io/v1/validation.go b/pkg/apis/fission.io/v1/validation.go index ad9d2e77..ff783d06 100644 --- a/pkg/apis/fission.io/v1/validation.go +++ b/pkg/apis/fission.io/v1/validation.go @@ -329,16 +329,22 @@ func (es ExecutionStrategy) Validate() error { result = multierror.Append(result, MakeValidationErr(ErrorUnsupportedType, "ExecutionStrategy.ExecutorType", es.ExecutorType, "not a valid executor type")) } - if es.MinScale < 0 { - result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "ExecutionStrategy.MinScale", es.MinScale, "minimum scale must be greater or equal to 0")) - } + if es.ExecutorType == ExecutorTypeNewdeploy { + if es.MinScale < 0 { + result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "ExecutionStrategy.MinScale", es.MinScale, "minimum scale must be greater or equal to 0")) + } - if es.MaxScale < es.MinScale { - result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "ExecutionStrategy.MaxScale", es.MaxScale, "maximum scale must be greater or equal to minimum scale")) - } + if es.MaxScale <= 0 { + result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "ExecutionStrategy.MaxScale", es.MaxScale, "maximum scale must be greater than 0")) + } - 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")) + if es.MaxScale < es.MinScale { + result = multierror.Append(result, MakeValidationErr(ErrorInvalidValue, "ExecutionStrategy.MaxScale", es.MaxScale, "maximum scale must be greater or equal to minimum scale")) + } + + 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")) + } } return result.ErrorOrNil()