Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cleanup #3403: removed --memory, --min-memory. --max-memory, --cpu, --min-cpu, --max-cpu flags from 'odo create' command #3475

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions pkg/component/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,31 @@ func ValidateComponentCreateRequest(client *occlient.Client, componentSettings c
}
}

if err := ensureAndLogProperResourceUsage(componentSettings.MinMemory, componentSettings.MaxMemory, "memory"); err != nil {
return err
}

if err := ensureAndLogProperResourceUsage(componentSettings.MinCPU, componentSettings.MaxCPU, "cpu"); err != nil {
return err
}

return
}

func ensureAndLogProperResourceUsage(resourceMin, resourceMax *string, resourceName string) error {
klog.V(4).Infof("Validating configured %v values", resourceName)

err := fmt.Errorf("`min%s` should accompany `max%s` or use `odo config set %s` to use same value for both min and max", resourceName, resourceName, resourceName)

if (resourceMin == nil) != (resourceMax == nil) {
return err
}
if (resourceMin != nil && *resourceMin == "") != (resourceMax != nil && *resourceMax == "") {
return err
}
return nil
}

// ApplyConfig applies the component config onto component dc
// Parameters:
// client: occlient instance
Expand Down
86 changes: 5 additions & 81 deletions pkg/odo/cli/component/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,9 @@ type CreateOptions struct {
componentContext string
componentPorts []string
componentEnvVars []string
memoryMax string

memoryMin string

appName string

memory string
cpuMax string
cpuMin string
cpu string
interactive bool
now bool
appName string
interactive bool
now bool
*CommonPushOptions
devfileMetadata DevfileMetadata
}
Expand Down Expand Up @@ -142,8 +133,8 @@ Note: When you use odo with experimental mode enabled and create devfile compone
# Create new Node.js component with source from remote git repository
%[1]s nodejs --git https://github.com/openshift/nodejs-ex.git

# Create new Node.js component with custom ports, additional environment variables and memory and cpu limits
%[1]s nodejs --port 8080,8100/tcp,9100/udp --env key=value,key1=value1 --memory 4Gi --cpu 2
# Create new Node.js component with custom ports and environment variables
%[1]s nodejs --port 8080,8100/tcp,9100/udp --env key=value,key1=value1

# Create new Node.js component and download the sample project named nodejs-starter
%[1]s nodejs --starter=nodejs-starter`)
Expand Down Expand Up @@ -304,36 +295,6 @@ func createDefaultComponentName(context *genericclioptions.Context, componentTyp
return componentName, nil
}

func (co *CreateOptions) setResourceLimits() error {
ensureAndLogProperResourceUsage(co.memory, co.memoryMin, co.memoryMax, "memory")

ensureAndLogProperResourceUsage(co.cpu, co.cpuMin, co.cpuMax, "cpu")

memoryQuantity, err := util.FetchResourceQuantity(corev1.ResourceMemory, co.memoryMin, co.memoryMax, co.memory)
if err != nil {
return err
}
if memoryQuantity != nil {
minMemory := memoryQuantity.MinQty.String()
maxMemory := memoryQuantity.MaxQty.String()
co.componentSettings.MinMemory = &minMemory
co.componentSettings.MaxMemory = &maxMemory
}

cpuQuantity, err := util.FetchResourceQuantity(corev1.ResourceCPU, co.cpuMin, co.cpuMax, co.cpu)
if err != nil {
return err
}
if cpuQuantity != nil {
minCPU := cpuQuantity.MinQty.String()
maxCPU := cpuQuantity.MaxQty.String()
co.componentSettings.MinCPU = &minCPU
co.componentSettings.MaxCPU = &maxCPU
}

return nil
}

// Complete completes create args
func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) {

Expand Down Expand Up @@ -761,10 +722,6 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
if err != nil {
return err
}
err = co.setResourceLimits()
if err != nil {
return err
}

var portList []string
if len(co.componentPorts) > 0 {
Expand Down Expand Up @@ -1082,33 +1039,6 @@ func (co *CreateOptions) Run() (err error) {
return
}

// The general cpu/memory is used as a fallback when it's set and both min-cpu/memory max-cpu/memory are not set
// when the only thing specified is the min or max value, we exit the application
func ensureAndLogProperResourceUsage(resource, resourceMin, resourceMax, resourceName string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These validations might need to happen at push now, as a user can set them using odo config and then push

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or better yet when these are set using odo config

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a new commit to add validation for push and config set command. Would that suffice?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah

if strings.HasPrefix(resourceMin, "-") {
log.Errorf("min-%s cannot be negative", resource)
os.Exit(1)
}
if strings.HasPrefix(resourceMax, "-") {
log.Errorf("max-%s cannot be negative", resource)
os.Exit(1)
}
if strings.HasPrefix(resource, "-") {
log.Errorf("%s cannot be negative", resource)
os.Exit(1)
}
if resourceMin != "" && resourceMax != "" && resource != "" {
log.Infof("`--%s` will be ignored as `--min-%s` and `--max-%s` has been passed\n", resourceName, resourceName, resourceName)
}
if (resourceMin == "") != (resourceMax == "") && resource != "" {
log.Infof("Using `--%s` %s for min and max limits.\n", resourceName, resource)
}
if (resourceMin == "") != (resourceMax == "") && resource == "" {
log.Errorf("`--min-%s` should accompany `--max-%s` or pass `--%s` to use same value for both min and max or try not passing any of them\n", resourceName, resourceName, resourceName)
os.Exit(1)
}
}

func checkoutProject(sparseCheckoutDir, zipURL, path string) error {

if sparseCheckoutDir != "" {
Expand Down Expand Up @@ -1144,12 +1074,6 @@ func NewCmdCreate(name, fullName string) *cobra.Command {
componentCreateCmd.Flags().StringVarP(&co.componentGit, "git", "g", "", "Create a git component using this repository.")
componentCreateCmd.Flags().StringVarP(&co.componentGitRef, "ref", "r", "", "Use a specific ref e.g. commit, branch or tag of the git repository (only valid for --git components)")
genericclioptions.AddContextFlag(componentCreateCmd, &co.componentContext)
componentCreateCmd.Flags().StringVar(&co.memory, "memory", "", "Amount of memory to be allocated to the component. ex. 100Mi (sets min-memory and max-memory to this value)")
componentCreateCmd.Flags().StringVar(&co.memoryMin, "min-memory", "", "Limit minimum amount of memory to be allocated to the component. ex. 100Mi")
componentCreateCmd.Flags().StringVar(&co.memoryMax, "max-memory", "", "Limit maximum amount of memory to be allocated to the component. ex. 100Mi")
componentCreateCmd.Flags().StringVar(&co.cpu, "cpu", "", "Amount of cpu to be allocated to the component. ex. 100m or 0.1 (sets min-cpu and max-cpu to this value)")
componentCreateCmd.Flags().StringVar(&co.cpuMin, "min-cpu", "", "Limit minimum amount of cpu to be allocated to the component. ex. 100m")
componentCreateCmd.Flags().StringVar(&co.cpuMax, "max-cpu", "", "Limit maximum amount of cpu to be allocated to the component. ex. 1")
componentCreateCmd.Flags().StringSliceVarP(&co.componentPorts, "port", "p", []string{}, "Ports to be used when the component is created (ex. 8080,8100/tcp,9100/udp)")
componentCreateCmd.Flags().StringSliceVar(&co.componentEnvVars, "env", []string{}, "Environmental variables for the component. For example --env VariableName=Value")

Expand Down
5 changes: 5 additions & 0 deletions pkg/odo/cli/config/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ func isValidArgumentList(args []string) error {
}

switch param {
case "memory", "minmemory", "maxmemory", "cpu", "mincpu", "maxcpu":
err = validation.NonNegativeValidator(args[1])
if err != nil {
err = errors.Errorf("%s is invalid %v", param, err)
}
case "ports", "debugport":
err = validation.PortsValidator(args[1])
}
Expand Down
15 changes: 13 additions & 2 deletions pkg/odo/util/validation/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package validation

import (
"fmt"
"github.com/openshift/odo/pkg/util"
"gopkg.in/AlecAivazis/survey.v1"
"strconv"
"strings"

"github.com/openshift/odo/pkg/util"
"gopkg.in/AlecAivazis/survey.v1"
)

// NameValidator provides a Validator view of the ValidateName function.
Expand Down Expand Up @@ -40,6 +41,16 @@ func IntegerValidator(ans interface{}) error {
return fmt.Errorf("don't know how to convert %v into an integer", ans)
}

// NonNegativeValidator validates whether the given value is not negative or not
func NonNegativeValidator(arg interface{}) error {
if s, ok := arg.(string); ok {
if strings.HasPrefix(s, "-") {
return fmt.Errorf("negative value is not acceptable")
}
}
return nil
}

// PathValidator validates whether the given path exists on the file system
func PathValidator(path interface{}) error {
if s, ok := path.(string); ok {
Expand Down
5 changes: 4 additions & 1 deletion tests/e2escenarios/e2e_images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ var _ = Describe("odo supported images e2e tests", func() {

// create the component
helper.CopyExample(filepath.Join("source", srcType), context)
helper.CmdShouldPass("odo", "create", cmpType, srcType+"-app", "--project", project, "--context", context, "--app", appName, "--min-memory", "400Mi", "--max-memory", "700Mi")
helper.CmdShouldPass("odo", "create", cmpType, srcType+"-app", "--project", project, "--context", context, "--app", appName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e2e test stands for broader user specific scenario. These validation can be done through UTs or integration test label. So please remove those lines.

Ofcourse if we find a user specific scenario where we have such hard requirement of adding those validation then we will add these line for sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I don't trim the memory and cpu flags from here then this will fail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh...got it. I did not realize it. Sorry for the false alarm.


helper.CmdShouldPass("odo", "config", "set", "minmemory", "400Mi", "--context", context)
helper.CmdShouldPass("odo", "config", "set", "maxmemory", "700Mi", "--context", context)

// push component and validate
helper.CmdShouldPass("odo", "push", "--context", context)
Expand Down
66 changes: 66 additions & 0 deletions tests/integration/cmd_push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,72 @@ var _ = Describe("odo push command tests", func() {

})

Context("Check memory and cpu config before odo push", func() {
It("Should work when memory is set..", func() {

helper.CopyExample(filepath.Join("source", "nodejs"), context)

helper.CmdShouldPass("odo", "component", "create", "nodejs", cmpName, "--project", project, "--context", context, "--app", appName)

helper.CmdShouldPass("odo", "config", "set", "Memory", "300Mi", "--context", context)
helper.CmdShouldPass("odo", "push", "--context", context)
})

It("Should fail if minMemory is set but maxmemory is not set..", func() {

helper.CopyExample(filepath.Join("source", "nodejs"), context)

helper.CmdShouldPass("odo", "component", "create", "nodejs", cmpName, "--project", project, "--context", context, "--app", appName)

helper.CmdShouldPass("odo", "config", "set", "minmemory", "100Mi", "--context", context)
output := helper.CmdShouldFail("odo", "push", "--context", context)
Expect(output).To(ContainSubstring("`minmemory` should accompany `maxmemory` or use `odo config set memory` to use same value for both min and max"))
})

It("should fail if maxmemory is set but minmemory is not set..", func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these validation be done through UTs instead of over crowding the integration test file ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it covers multiple packages. Not just config package. It cannot be covered in UTs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The memory and cpu is set in config package but the overall/e2e validation of config parameters occur in component push package


helper.CopyExample(filepath.Join("source", "nodejs"), context)

helper.CmdShouldPass("odo", "component", "create", "nodejs", cmpName, "--project", project, "--context", context, "--app", appName)

helper.CmdShouldPass("odo", "config", "set", "maxmemory", "400Mi", "--context", context)
output := helper.CmdShouldFail("odo", "push", "--context", context)
Expect(output).To(ContainSubstring("`minmemory` should accompany `maxmemory` or use `odo config set memory` to use same value for both min and max"))
})

It("Should work when cpu is set", func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cna it be done through code level validation i mean through UTs ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I don't think so, due to the same reason I mentioned above.


helper.CopyExample(filepath.Join("source", "nodejs"), context)

helper.CmdShouldPass("odo", "component", "create", "nodejs", cmpName, "--project", project, "--context", context, "--app", appName)

helper.CmdShouldPass("odo", "config", "set", "cpu", "0.4", "--context", context)
helper.CmdShouldPass("odo", "push", "--context", context)
})

It("Should fail if mincpu is set but maxcpu is not set..", func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be done through code level validation i mean through UTs ?


helper.CopyExample(filepath.Join("source", "nodejs"), context)

helper.CmdShouldPass("odo", "component", "create", "nodejs", cmpName, "--project", project, "--context", context, "--app", appName)

helper.CmdShouldPass("odo", "config", "set", "mincpu", "0.4", "--context", context)
output := helper.CmdShouldFail("odo", "push", "--context", context)
Expect(output).To(ContainSubstring("`mincpu` should accompany `maxcpu` or use `odo config set cpu` to use same value for both min and max"))
})

It("should fail if maxcpu is set but mincpu is not set..", func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be done through code level validation i mean through UTs ?


helper.CopyExample(filepath.Join("source", "nodejs"), context)

helper.CmdShouldPass("odo", "component", "create", "nodejs", cmpName, "--project", project, "--context", context, "--app", appName)

helper.CmdShouldPass("odo", "config", "set", "maxcpu", "0.5", "--context", context)
output := helper.CmdShouldFail("odo", "push", "--context", context)
Expect(output).To(ContainSubstring("`mincpu` should accompany `maxcpu` or use `odo config set cpu` to use same value for both min and max"))
})
})

Context("Check for label propagation after pushing", func() {

It("Check for labels", func() {
Expand Down
43 changes: 8 additions & 35 deletions tests/integration/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ func componentTests(args ...string) {
})

It("should list the component", func() {
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--git", "https://github.com/openshift/nodejs-ex", "--min-memory", "100Mi", "--max-memory", "300Mi", "--min-cpu", "0.1", "--max-cpu", "2", "--context", context, "--app", "testing")...)
helper.ValidateLocalCmpExist(context, "Type,nodejs", "Name,cmp-git", "Application,testing", "MaxMemory,300Mi")
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--git", "https://github.com/openshift/nodejs-ex", "--context", context, "--app", "testing")...)
helper.ValidateLocalCmpExist(context, "Type,nodejs", "Name,cmp-git", "Application,testing")
helper.CmdShouldPass("odo", append(args, "push", "--context", context)...)

cmpList := helper.CmdShouldPass("odo", append(args, "list", "--project", project)...)
Expand All @@ -207,15 +207,15 @@ func componentTests(args ...string) {
})

It("should list the component when it is not pushed", func() {
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--git", "https://github.com/openshift/nodejs-ex", "--min-memory", "100Mi", "--max-memory", "300Mi", "--min-cpu", "0.1", "--max-cpu", "2", "--context", context, "--app", "testing")...)
helper.ValidateLocalCmpExist(context, "Type,nodejs", "Name,cmp-git", "Application,testing", "MinCPU,100m")
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--git", "https://github.com/openshift/nodejs-ex", "--context", context, "--app", "testing")...)
helper.ValidateLocalCmpExist(context, "Type,nodejs", "Name,cmp-git", "Application,testing")
cmpList := helper.CmdShouldPass("odo", append(args, "list", "--context", context)...)
helper.MatchAllInOutput(cmpList, []string{"cmp-git", "Not Pushed"})
helper.CmdShouldPass("odo", append(args, "delete", "-f", "--all", "--context", context)...)
})
It("should list the state as unknown for disconnected cluster", func() {
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--git", "https://github.com/openshift/nodejs-ex", "--min-memory", "100Mi", "--max-memory", "300Mi", "--min-cpu", "0.1", "--max-cpu", "2", "--context", context, "--app", "testing")...)
helper.ValidateLocalCmpExist(context, "Type,nodejs", "Name,cmp-git", "Application,testing", "MinCPU,100m")
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--git", "https://github.com/openshift/nodejs-ex", "--context", context, "--app", "testing")...)
helper.ValidateLocalCmpExist(context, "Type,nodejs", "Name,cmp-git", "Application,testing")
kubeconfigOrig := os.Getenv("KUBECONFIG")
os.Setenv("KUBECONFIG", "/no/such/path")
cmpList := helper.CmdShouldPass("odo", append(args, "list", "--context", context, "--v", "9")...)
Expand Down Expand Up @@ -596,12 +596,8 @@ func componentTests(args ...string) {

It("should be able to create a git component and update it from local to git", func() {
helper.CopyExample(filepath.Join("source", "nodejs"), context)
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--min-cpu", "0.1", "--max-cpu", "2", "--context", context, "--app", "testing")...)
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--context", context, "--app", "testing")...)
helper.CmdShouldPass("odo", append(args, "push", "--context", context)...)
getCPULimit := oc.MaxCPU("cmp-git", "testing", project)
Expect(getCPULimit).To(ContainSubstring("2"))
getCPURequest := oc.MinCPU("cmp-git", "testing", project)
Expect(getCPURequest).To(ContainSubstring("100m"))

helper.CmdShouldPass("odo", "update", "--git", "https://github.com/openshift/nodejs-ex.git", "--context", context)
// check the source location and type in the deployment config
Expand All @@ -612,23 +608,14 @@ func componentTests(args ...string) {
})

It("should be able to update a component from git to local", func() {
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--git", "https://github.com/openshift/nodejs-ex", "--min-memory", "100Mi", "--max-memory", "300Mi", "--context", context, "--app", "testing")...)
helper.CmdShouldPass("odo", append(args, "create", "nodejs", "cmp-git", "--project", project, "--git", "https://github.com/openshift/nodejs-ex", "--context", context, "--app", "testing")...)
helper.CmdShouldPass("odo", append(args, "push", "--context", context)...)
getMemoryLimit := oc.MaxMemory("cmp-git", "testing", project)
Expect(getMemoryLimit).To(ContainSubstring("300Mi"))
getMemoryRequest := oc.MinMemory("cmp-git", "testing", project)
Expect(getMemoryRequest).To(ContainSubstring("100Mi"))

// update the component config according to the git component
helper.CopyExample(filepath.Join("source", "nodejs"), context)

helper.CmdShouldPass("odo", "update", "--local", "./", "--context", context)

getMemoryLimit = oc.MaxMemory("cmp-git", "testing", project)
Expect(getMemoryLimit).To(ContainSubstring("300Mi"))
getMemoryRequest = oc.MinMemory("cmp-git", "testing", project)
Expect(getMemoryRequest).To(ContainSubstring("100Mi"))

// check the source location and type in the deployment config
getSourceLocation := oc.SourceLocationDC("cmp-git", "testing", project)
Expect(getSourceLocation).To(ContainSubstring(""))
Expand Down Expand Up @@ -758,20 +745,6 @@ func componentTests(args ...string) {
})
})

Context("when creating component with improper memory quantities", func() {
JustBeforeEach(func() {
project = helper.CreateRandProject()
})
JustAfterEach(func() {
helper.DeleteProject(project)
})
It("should fail gracefully with proper error message", func() {
stdError := helper.CmdShouldFail("odo", append(args, "create", "java:8", "backend", "--memory", "1GB", "--project", project, "--context", context)...)
Expect(stdError).ToNot(ContainSubstring("panic: cannot parse"))
Expect(stdError).To(ContainSubstring("quantities must match the regular expression"))
})
})

Context("Creating component using symlink", func() {
var symLinkPath string

Expand Down
Loading