Skip to content

Commit

Permalink
added --s2i flag for odo create command
Browse files Browse the repository at this point in the history
  • Loading branch information
devang-gaur authored and cdrage committed Jul 30, 2020
1 parent b9976ec commit 2b0a05e
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 31 deletions.
27 changes: 27 additions & 0 deletions docs/dev/experimental-mode.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,30 @@ $ odo delete myspring
The devfile registry can be accessed link:https://github.com/elsony/devfile-registry[here].

For more information on how to develop and write a devfile, please read the link:https://docs.google.com/document/d/1piBG2Zu2PqPZSl0WySj25UouK3X5MKcFKqPrfC9ZAsc[Odo stack creation] document.

#### Forcing s2i type component creation over devfile type components:

If there is an s2i type component with the same name as a devfile type component, you can use `--s2i` flag to force the creation of s2i type component over devfile type.
If there is a devfile type component with a given name but no s2i component, `odo create --s2i` will fail.
Also, using flags specific to s2i component creation without using --s2i would also fail the `odo create` command.

In the above example output of `odo catalog list components` command, you can observe that `nodejs` component type is available in both s2i and devfile categories.

The following command should create an s2i type component.
```
$ odo create nodejs mynode --s2i
Experimental mode is enabled, use at your own risk

Validation
✓ Validating component [3s]

Please use `odo push` command to create the component with source deployed
```

The following command would fail for using `--s2i` flag as there is no s2i component type available with name "java-spring-boot".
```
$ odo create java-spring-boot myspring --s2i
Experimental mode is enabled, use at your own risk

✗ Cannot select this component with --s2i flag
```
4 changes: 2 additions & 2 deletions pkg/component/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const (
// StateTypePushed means that Storage is present both locally and on cluster
StateTypePushed State = "Pushed"
// StateTypeNotPushed means that Storage is only in local config, but not on the cluster
StateTypeNotPushed = "Not Pushed"
StateTypeNotPushed State = "Not Pushed"
// StateTypeUnknown means that odo cannot tell its state
StateTypeUnknown = "Unknown"
StateTypeUnknown State = "Unknown"
)
109 changes: 83 additions & 26 deletions pkg/odo/cli/component/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,16 @@ type CreateOptions struct {
componentContext string
componentPorts []string
componentEnvVars []string
appName string
memoryMax string

memoryMin string

appName string

memory string
cpuMax string
cpuMin string
cpu string
interactive bool
now bool
memoryMin string
memory string
cpuMax string
cpuMin string
cpu string
interactive bool
now bool
forceS2i bool
*CommonPushOptions
devfileMetadata DevfileMetadata
}
Expand Down Expand Up @@ -330,17 +328,50 @@ func (co *CreateOptions) setResourceLimits() error {
co.componentSettings.MinCPU = &minCPU
co.componentSettings.MaxCPU = &maxCPU
}
return nil
}

func (co *CreateOptions) checkConflictingFlags() error {
errorString := "flag %s, requires --s2i flag to be set, when in experimental mode."

if !co.forceS2i {
if co.now {
return errors.New(fmt.Sprintf(errorString, "--now"))
}

if len(co.componentBinary) != 0 {
return errors.New(fmt.Sprintf(errorString, "--binary"))
}

if len(co.componentGit) != 0 {
return errors.New(fmt.Sprintf(errorString, "--git"))
}

if len(co.componentEnvVars) != 0 {
return errors.New(fmt.Sprintf(errorString, "--env"))
}

if len(co.componentPorts) != 0 {
return errors.New(fmt.Sprintf(errorString, "--port"))
}
}
return nil
}

// Complete completes create args
func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) {
// this populates the LocalConfigInfo as well
co.Context = genericclioptions.NewContextCreatingAppIfNeeded(cmd)

if experimental.IsExperimentalModeEnabled() {
// Add a disclaimer that we are in *experimental mode*
log.Experimental("Experimental mode is enabled, use at your own risk")

err = co.checkConflictingFlags()
if err != nil {
return
}

// Configure the context
if co.componentContext != "" {
DevfilePath = filepath.Join(co.componentContext, devFile)
Expand All @@ -359,8 +390,20 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string

co.appName = genericclioptions.ResolveAppFlag(cmd)

var catalogList catalog.ComponentTypeList
if co.forceS2i {
client := co.Client
catalogList, err = catalog.ListComponents(client)
if err != nil {
return err
}
}

// Validate user specify devfile path
if co.devfileMetadata.devfilePath.value != "" {
if co.forceS2i {
return errors.New("You can't set --s2i flag as true if you want to use the devfile that is specified via --devfile")
}
fileErr := util.ValidateFile(co.devfileMetadata.devfilePath.value)
urlErr := util.ValidateURL(co.devfileMetadata.devfilePath.value)
if fileErr != nil && urlErr != nil {
Expand All @@ -374,6 +417,10 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string

// Validate user specify registry
if co.devfileMetadata.devfileRegistry.Name != "" {
if co.forceS2i {
return errors.New("You can't specify registry via --registry if you want to force the S2I build with --s2i")
}

if co.devfileMetadata.devfilePath.value != "" {
return errors.New("You can't specify registry via --registry if you want to use the devfile that is specified via --devfile")
}
Expand Down Expand Up @@ -410,7 +457,7 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
var catalogDevfileList catalog.DevfileComponentTypeList
isDevfileRegistryPresent := true // defaulted to true since odo ships with a default registry set

if co.interactive {
if co.interactive && !co.forceS2i {
// Interactive mode

// Get available devfile components for checking devfile compatibility
Expand Down Expand Up @@ -448,6 +495,10 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
if util.CheckPathExists(DevfilePath) || co.devfileMetadata.devfilePath.value != "" {
// Use existing devfile directly

if co.forceS2i {
return errors.Errorf("Existing devfile component detected. Please remove the devfile component before creating an s2i component")
}

if len(args) > 1 {
return errors.Errorf("Accepts between 0 and 1 arg when using existing devfile, received %d", len(args))
}
Expand All @@ -465,7 +516,7 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
}

co.devfileMetadata.devfileSupport = true
} else {
} else if len(args) > 0 {
// Download devfile from registry

// Component type: Get from full command's first argument (mandatory in direct mode)
Expand Down Expand Up @@ -543,7 +594,7 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
return nil
}

if isDevfileRegistryPresent {
if isDevfileRegistryPresent && !co.forceS2i {
// Categorize the sections
log.Info("Validation")

Expand All @@ -563,6 +614,19 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
}
}

if co.forceS2i && hasComponent {
s2iOverride := false
for _, item := range catalogList.Items {
if item.Name == co.devfileMetadata.componentType {
s2iOverride = true
break
}
}
if !s2iOverride {
return errors.New("Cannot select this component with --s2i flag")
}
}

existSpinner := log.Spinner("Checking devfile existence")
if hasComponent {
existSpinner.End(true)
Expand Down Expand Up @@ -590,22 +654,16 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
// we should error out instead of running s2i componet code and throw warning message
if co.devfileMetadata.devfileRegistry.Name != "" {
return errors.Errorf("Devfile component type %s is not supported, please run `odo catalog list components` for a list of supported devfile component types", co.devfileMetadata.componentType)
} else {
log.Warningf("Devfile component type %s is not supported, please run `odo catalog list components` for a list of supported devfile component types", co.devfileMetadata.componentType)
}

log.Warningf("Devfile component type %s is not supported, please run `odo catalog list components` for a list of supported devfile component types", co.devfileMetadata.componentType)
}
}

if len(args) == 0 || !cmd.HasFlags() {
co.interactive = true
}

// this populates the LocalConfigInfo as well
co.Context = genericclioptions.NewContextCreatingAppIfNeeded(cmd)
if err != nil {
return errors.Wrap(err, "failed initiating local config")
}

// Do not execute S2I specific code on Kubernetes Cluster or Docker
// return from here, if it is not an openshift cluster.
var openshiftCluster bool
Expand All @@ -626,8 +684,6 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string

co.componentSettings = co.LocalConfigInfo.GetComponentSettings()

co.Context = genericclioptions.NewContextCreatingAppIfNeeded(cmd)

// Below code is for INTERACTIVE mode
if co.interactive {
client := co.Client
Expand Down Expand Up @@ -800,7 +856,7 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
func (co *CreateOptions) Validate() (err error) {

if experimental.IsExperimentalModeEnabled() {
if co.devfileMetadata.devfileSupport {
if !co.forceS2i && co.devfileMetadata.devfileSupport {
// Validate if the devfile component that user wants to create already exists
spinner := log.Spinner("Validating devfile component")
defer spinner.End(false)
Expand Down Expand Up @@ -954,7 +1010,7 @@ func (co *CreateOptions) downloadProject(projectPassed string) error {
// Run has the logic to perform the required actions as part of command
func (co *CreateOptions) Run() (err error) {
if experimental.IsExperimentalModeEnabled() {
if co.devfileMetadata.devfileSupport {
if !co.forceS2i && co.devfileMetadata.devfileSupport {
// Use existing devfile directly from --devfile flag
if co.devfileMetadata.devfilePath.value != "" {
if co.devfileMetadata.devfilePath.protocol == "http(s)" {
Expand Down Expand Up @@ -1166,6 +1222,7 @@ func NewCmdCreate(name, fullName string) *cobra.Command {
componentCreateCmd.Flags().StringVar(&co.devfileMetadata.devfileRegistry.Name, "registry", "", "Create devfile component from specific registry")
componentCreateCmd.Flags().StringVar(&co.devfileMetadata.devfilePath.value, "devfile", "", "Path to the user specify devfile")
componentCreateCmd.Flags().StringVar(&co.devfileMetadata.token, "token", "", "Token to be used when downloading devfile from the devfile path that is specified via --devfile")
componentCreateCmd.Flags().BoolVar(&co.forceS2i, "s2i", false, "Path to the user specify devfile")
}

componentCreateCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
Expand Down
6 changes: 3 additions & 3 deletions pkg/odo/cli/component/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,10 @@ func (po *PushOptions) Run() (err error) {
if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(po.DevfilePath) {
// Return Devfile push
return po.DevfilePush()
} else {
// Legacy odo push
return po.Push()
}

// Legacy odo push
return po.Push()
}

// NewCmdPush implements the push odo command
Expand Down
35 changes: 35 additions & 0 deletions tests/integration/devfile/cmd_devfile_catalog_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package devfile

import (
"encoding/json"
"os"
"path/filepath"
"time"
Expand Down Expand Up @@ -127,4 +128,38 @@ var _ = Describe("odo devfile catalog command tests", func() {
helper.MatchAllInOutput(output, []string{"accepts 1 arg(s), received 0"})
})
})

Context("When executing catalog list components with experimental mode set to true", func() {

componentName := "nodejs"

It("should prove that nodejs is present in both S2I Component list and Devfile Component list", func() {

output := helper.CmdShouldPass("odo", "catalog", "list", "components", "-o", "json")

wantOutput := []string{componentName}

var data map[string]interface{}

err := json.Unmarshal([]byte(output), &data)

if err != nil {
Expect(err).Should(BeNil())
}
outputBytes, err := json.Marshal(data["s2iItems"])
if err == nil {
output = string(outputBytes)
}

helper.MatchAllInOutput(output, wantOutput)

outputBytes, err = json.Marshal(data["devfileItems"])
if err == nil {
output = string(outputBytes)
}

helper.MatchAllInOutput(output, wantOutput)
})
})

})
Loading

0 comments on commit 2b0a05e

Please sign in to comment.