diff --git a/docs/dev/experimental-mode.adoc b/docs/dev/experimental-mode.adoc index 6fda622ffcb..7fd563b90f9 100644 --- a/docs/dev/experimental-mode.adoc +++ b/docs/dev/experimental-mode.adoc @@ -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 +``` \ No newline at end of file diff --git a/docs/file-reference/index.md b/docs/file-reference/index.md new file mode 100644 index 00000000000..4a1798400bd --- /dev/null +++ b/docs/file-reference/index.md @@ -0,0 +1,539 @@ +--- +title: Odo Devfile Reference + +language_tabs: + - yaml + +toc_footers: + - Odo Homepage + - Devfile GitHub Page + +search: true +--- + +# Introduction + +> Download a starter application to get an example devfile.yaml + +```sh +$ odo create nodejs --starter +``` + +**Installation:** + +Installing odo can be found at our [installation page](https://odo.dev/docs/installing-odo/). + +**Quick Start:** + +If you haven't used odo yet, we recommend going through our [Deploying a devfile using odo guide](https://odo.dev/docs/deploying-a-devfile-using-odo/). + + +# Devfile Properties + +> All definable devfile properties + +```yaml +schemaVersion: +metadata: +projects: +- +components: +- +commands: +- +``` + +| Key | Type | Required | Description | +|---------------|-----------------------------------------------|----------|-----------------------------------------------------------------------------------| +| schemaVersion | string | yes | Schema version of devfile | +| metadata | [metadataObject](#metadataobject) | no | Metadata information that describes the project | +| projects | array of [projectObject](#project-object) | no | List of projects that devfile will use when creating your development environment | +| components | array of [componentObject](#component-object) | no | List of components to be used within your development environment | +| commands | array of [commandObject](#commandobject) | no | List of commands to be executed | + + +## schemaVersion + +```yaml +schemaVersion: 2.0.0 +``` + +| Key | Type | Required | Description | +|---------------|--------|----------|---------------------| +| schemaVersion | string | yes | 2.0.0 of the schema | + + +## metadata + +```yaml +metadata: + name: nodejs + version: 1.0.0 +``` + +| Key | Type | Description | +|----------------|------------------------------------|---------------------| +| metadataObject | [metadataObject](#metadata-object) | Metadata to be used | + +## projects + +> Pulling from a git location + +```yaml +projects: + - name: nodejs-web-app + git: + location: https://github.com/odo-devfiles/nodejs-ex.git + branch: master + sparseCheckoutDir: /app/ # optional checkout dir + startPoint: 1.0.0 # tag or commit to start at +``` + +> Pulling from a GitHub location + +```yaml +projects: + - name: nodejs-web-app + github: + location: https://github.com/odo-devfiles/nodejs-ex.git + branch: master + sparseCheckoutDir: /app/ # optional checkout dir + startPoint: 1.0.0 # tag or commit to start at +``` + +> Pulling from a zip + +```yaml +projects: + - name: nodejs-web-app + zip: + location: https://github.com/odo-devfiles/nodejs-ex/archive/0.0.1.zip + sparseCheckoutDir: /app/ # optional checkout dir +``` + +| Key | Type | Description | +|---------------|----------------------------------|-----------------------------| +| projectObject | [projectObject](#project-object) | List of projects to be used | + +## components + +> Example using an [OpenLiberty](https://github.com/odo-devfiles/registry/blob/master/devfiles/openLiberty/devfile.yaml) container + +```yaml +components: + - container: + name: appsodyrun + image: 'ajymau/java-openliberty-dev:latest' + memoryLimit: 1512Mi + mountSources: true + endpoints: + - name: 9080/tcp + exposure: public + public: true + protocol: http + targetPort: 9080 + env: + - name: MODE2 + value: TEST2 + - name: myprop2 + value: myval2 +``` + +| Key | Type | Description | +|-----------------|--------------------------------------|-------------------------------| +| componentObject | [componentObject](#component-object) | List of components to be used | + + +## commands + +> Example using the exec command + +```yaml +commands: + - exec: + id: devBuild + commandLine: "/artifacts/bin/build-container-full.sh" + component: tools + workingDir: /projects/springbootproject + group: + kind: build + isDefault: true + - exec: + id: devRun + commandLine: "/artifacts/bin/start-server.sh" + component: runtime + workingDir: / + group: + kind: run + isDefault: true +``` + +| Key | Type | Description | +|---------------|----------------------------------|-----------------------------------------------------------| +| commandObject | [commandObject](#command-object) | Command to be executed in an existing component container | + + +# Metadata Object + +> Example using metadata + +```yaml +metadata: + name: nodejs + version: 1.0.0 +``` + +| Key | Type | Required | Description | +|---------|--------|----------|-----------------------------------------------------------------------------------| +| name | string | yes | Name of your devfile. This name will propagate to the devfile registry if listed. | +| version | string | yes | Version of your devfile | + + +# Project Object + +> Pulling from a git location + +```yaml +projects: + - name: nodejs-web-app + git: + location: https://github.com/odo-devfiles/nodejs-ex.git + branch: master + sparseCheckoutDir: /app/ # optional checkout dir + startPoint: 1.0.0 # tag or commit to start at +``` + +> Pulling from a GitHub location + +```yaml +projects: + - name: nodejs-web-app + github: + location: https://github.com/odo-devfiles/nodejs-ex.git + branch: master + sparseCheckoutDir: /app/ # optional checkout dir + startPoint: 1.0.0 # tag or commit to start at +``` + +> Pulling from a zip + +```yaml +projects: + - name: nodejs-web-app + zip: + location: https://github.com/odo-devfiles/nodejs-ex/archive/0.0.1.zip + sparseCheckoutDir: /app/ # optional checkout dir +``` + +Each project may contain three different objects, `git`, `github` or `zip`. + + + +| Key | Type | Description | +|-----------|-------------------------------|------------------------------------------------------------------------------------| +| name | string | Name of your devfile | +| clonePath | string | Path relative the root of your projects to which the project should be cloned into | +| git | [gitObject](#gitobject) | Pull from a Git location | +| github | [githubObject](#githubobject) | Pull from GitHub | +| zip | [zipObject](#zipobject) | Get from a zip location | + +## gitObject + +> Using the gitObject + +```yaml +projects: + - name: nodejs-web-app + git: + location: https://github.com/odo-devfiles/nodejs-ex.git + branch: master + sparseCheckoutDir: /app/ # optional checkout dir + startPoint: 1.0.0 # tag or commit to start at +``` + +| Key | Type | Description | +|------------------|--------|------------------------------------| +| location | string | Location of the git repository | +| branch | string | What branch to use | +| spareCheckoutDir | string | What directory to use when pulling | +| startPoint | string | Tag or commit to start from | + +## githubObject + +> Using the githubObject + +```yaml +projects: + - name: nodejs-web-app + github: + location: https://github.com/odo-devfiles/nodejs-ex.git + branch: master + sparseCheckoutDir: /app/ # optional checkout dir + startPoint: 1.0.0 # tag or commit to start at +``` + +| Key | Type | Description | +|------------------|--------|------------------------------------| +| location | string | Location of the git repository | +| branch | string | What branch to use | +| spareCheckoutDir | string | What directory to use when pulling | +| startPoint | string | Tag or commit to start from | + +## zipObject + +> Using the zipObject + +```yaml +projects: + - name: nodejs-web-app + zip: + location: https://github.com/odo-devfiles/nodejs-ex/archive/0.0.1.zip + sparseCheckoutDir: /app/ # optional checkout dir +``` + +| Key | Type | Description | +|------------------|--------|------------------------------------| +| location | string | Location of the zip | +| spareCheckoutDir | string | What directory to use when pulling | + + + +# Component Object + +> Example using a container using the minimum amount of keys + +```yaml +components: + - container: + name: appsodyrun + image: 'ajymau/java-openliberty-dev:latest' +``` + +Each component must use the `container` object. + + +| Key | Type | Description | +|-----------|-------------------------------------|-------------------------------| +| container | [containerObject](#containerobject) | List of containers to be used | + + +## containerObject + +> Example using an [OpenLiberty](https://github.com/odo-devfiles/registry/blob/master/devfiles/openLiberty/devfile.yaml) container + +```yaml +components: + - container: + name: appsodyrun + image: 'ajymau/java-openliberty-dev:latest' + memoryLimit: 1512Mi + mountSources: true + endpoints: + - name: 9080/tcp + exposure: public + public: true + protocol: http + targetPort: 9080 + env: + - name: MODE2 + value: TEST2 + - name: myprop2 + value: myval2 +``` + +| Key | Type | Required | Description | +|----------------|-----------------------------------------|----------|-------------------------------------------------------------------------------------------------------------| +| name | string | yes | Name of your container | +| image | string | yes | Image version | +| memoryLimit | string | no | Memory limit to be used with your container | +| mountSources | boolean | no | Mount the source or not | +| sourceMapping | string | no | Path in the container where project sources should be transferred / mounted when mountSource is set to true | +| endpoints[] | [endpointObject](#endpointobject) | no | List of endpoints to use | +| volumeMounts[] | [volumeMountsObject](#volumemountsobject) | no | List of volumes to mount | +| env[] | [envObject](#envobject) | no | List of environment variables to use | + + + +## endpointObject + +> Example using an endpoint + +```yaml +components: + - container: + name: appsodyrun + image: 'ajymau/java-openliberty-dev:latest' + endpoints: + - name: 9080/tcp + exposure: public + public: true + protocol: http + targetPort: 9080 +``` + +| Key | Type | Required | Description | +|------------|---------|----------|------------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | yes | Name of your endpoint | +| targetPort | integer | yes | Port number that you are targeting | +| exposure | string | no | `public`, `internal` or `none`. Describes how the endpoint should be exposed on the network | +| path | string | no | Path to the endpoint URL | +| protocol | string | no | `http`, `https`, `ws`, `wss`, `tcp`, `udp`. Describes the application and transport protocols of the traffic that will go through the endpoint | +| secure | boolean | no | Whether or not the endpoint is defined as secure | + + +## volumeMountsObject + +> Example using a volume with a container + +```yaml +components: + - container: + name: tools + image: maysunfaisal/springbootbuild + memoryLimit: 768Mi + command: ['tail'] + args: [ '-f', '/dev/null'] + mountSources: true + volumeMounts: + - name: springbootpvc + path: /data +``` + +| Key | Type | Required | Description | +|------|--------|----------|----------------------------------------------------------------------------------------------------| +| name | string | yes | Name of the volume | +| path | string | no | Path in the component container where the volume should be mounted. Defaults to `/` if blank | + + +# Command Object + +> Example using an exec command within a command object + +```yaml +commands: + - exec: + id: devBuild + component: tools + commandLine: "/artifacts/bin/build-container-full.sh" + workingDir: /projects/springbootproject + group: + kind: build + isDefault: true +``` + +Each command must use the `exec` object. + +| Key | Type | Description | +|------|---------------------------|---------------------------| +| exec | [execObject](#execobject) | The CLI command to be ran | + +## execObject + +> Example using exec command within a command object + +```yaml +commands: + - exec: + id: devBuild + commandLine: "/artifacts/bin/build-container-full.sh" + component: tools + workingDir: /projects/springbootproject + group: + kind: build + isDefault: true + env: + - name: FOO + value: BAR +``` + +| Key | Type | Required | Description | +|-------------|-----------------------------|----------|--------------------------------------------------------| +| id | string | yes | ID of the command | +| commandLine | string | yes | Command to be ran | +| component | string | no | What component that the actions relate to | +| label | string | no | Optional label to be used to describe the command | +| workingDir | string | no | Working directory where the command should be executed | +| group | [groupObject](#groupObject) | no | Group that the command is part of | +| env | [envObject](#envObject) | no | List of environment variables used | + +## groupObject + +> Example using the group object within exec + +```yaml +commands: + - exec: + id: devBuild + commandLine: "/artifacts/bin/build-container-full.sh" + group: + kind: build + isDefault: true +``` + +| Key | Type | Required | Description | +|-----------|---------|----------|-------------------------------------------------------------------------------------------------------------| +| kind | string | yes | Kind of group the command is part of. Options: `build`, `run`, `test`, `debug` | +| isDefault | boolean | no | Identifies that it is the default command to be ran. Only one default command can be defined for each group | + +# Universal objects + +List of objects which are found in multiple parts of devfile. For example, the [envObject](#envobject) is found within [containerObject](#containerobject) and [execObject](#execobject). + +## envObject + +> Example using environment variables with a container + +```yaml +components: + - container: + name: appsodyrun + image: 'ajymau/java-openliberty-dev:latest' + targetPort: 9080 + env: + - name: MODE2 + value: TEST2 + - name: myprop2 + value: myval2 +``` + +> Example using environment variables within the exec command + +```yaml +commands: + - exec: + id: devBuild + commandLine: "/artifacts/bin/build-container-full.sh" + env: + - name: FOO + value: BAR +``` + +| Key | Type | Required | Description | +|-------|--------|----------|-----------------------------------| +| name | string | yes | Name of the environment variable | +| value | string | yes | Value of the environment variable | + +# Unsupported features + +**PLEASE NOTE:** Devfile v2 is still in its early stages and there are some parts of the schema that have not yet been implemented. + +Please refer to the below table for a list of features which are *not yet* implemented: + +| Key | Key Description | Status | Description | +|------------------------------------|-------------------------------------------|-------------|-------------------------------------------------------------------------------------------------| +| projects[].github.startPoint | [githubObject](#githubObject) | IN PROGRESS | Refer to issue: https://github.com/openshift/odo/issues/3002 | +| projects[].git.startPoint | [gitObject](#gitObject) | IN PROGRESS | Refer to issue: https://github.com/openshift/odo/issues/3002 | +| projects[].zip | [zipObject](#zipobject) | IN PROGRESS | Entire object not yet implemented. Refer to issue: https://github.com/openshift/odo/issues/3442 | +| parent | [parentObject](#parent-object) | IN PROGRESS | Refer to issue: https://github.com/openshift/odo/issues/2936 | +| events | [eventObject](#event-object) | IN PROGRESS | Refer to issue: https://github.com/openshift/odo/issues/2919 | +| component[].kubernetes | [kubernetesObject](#kubernetesobject) | IN PROGRESS | | +| component[].openshift | [openshiftObject](#openshiftobject) | IN PROGRESS | | +| component[].volume | [volumeObject](#volumeobject) | IN PROGRESS | Refer to: https://github.com/openshift/odo/issues/3407 | +| component[].plugin | [pluginObject](#pluginobject) | IN PROGRESS | Refer to: https://github.com/openshift/odo/issues/3407 | +| component[].container.endpoints | [endpointObject](#endpointobject) | IN PROGRESS | Refer to: https://github.com/openshift/odo/issues/3544 | +| component[].container.dedicatedPod | [containerObject](#containerobject) | UNKNOWN | In schema but not yet implemented. | +| commands[].apply | [applyObject](#applyobject) | UNKNOWN | In schema but not yet implemented. | +| commands[].composite | [compositeObject](#compositeobject) | IN PROGRESS | Refer to: https://github.com/openshift/odo/issues/3338 | +| commands[].vscodeLaunch | [vscodeLaunchObject](#vscodeLaunchobject) | N/A | Not applicable to odo. | +| commands[].vscodeTask | [vscodeTaskObject](#vscodeTaskobject) | N/A | Not applicable to odo. | + diff --git a/docs/public/debugging-using-devfile.adoc b/docs/public/debugging-using-devfile.adoc index fe4493bedf1..cf8a8012ca9 100644 --- a/docs/public/debugging-using-devfile.adoc +++ b/docs/public/debugging-using-devfile.adoc @@ -32,19 +32,19 @@ commands: - type: exec component: runtime command: "npm install" - workdir: ${CHE_PROJECTS_ROOT}/nodejs-starter/app + workdir: ${PROJECTS_ROOT}/nodejs-starter/app - name: devrun actions: - type: exec component: runtime command: "nodemon app.js" - workdir: ${CHE_PROJECTS_ROOT}/nodejs-starter/app + workdir: ${PROJECTS_ROOT}/nodejs-starter/app - name: debugrun actions: - type: exec component: runtime command: "nodemon --inspect=${DEBUG_PORT}" - workdir: ${CHE_PROJECTS_ROOT}/nodejs-starter/ + workdir: ${PROJECTS_ROOT}/nodejs-starter/ ``` - Now we need to create the component using `odo create nodejs` diff --git a/docs/public/deploying-a-devfile-using-odo.adoc b/docs/public/deploying-a-devfile-using-odo.adoc index 992075817eb..743fcf3fba7 100644 --- a/docs/public/deploying-a-devfile-using-odo.adoc +++ b/docs/public/deploying-a-devfile-using-odo.adoc @@ -2,12 +2,11 @@ What is a devfile? -A https://redhat-developer.github.io/devfile/[devfile] is a portable file that describes your development environment. It allows for a _portable_ developmental environment without the need of reconfiguration. +A https://redhat-developer.github.io/devfile/[devfile] is a portable file that describes your development environment. It allows reproducing a _portable_ developmental environment without the need of reconfiguration. With a devfile you can describe: -* The source code being used -* Development components such as IDE tools (VSCode) and application runtimes (Yarn / NPM) +* Development components such as container definition for build and application runtimes * A list of pre-defined commands that can be run * Projects to initially clone @@ -394,3 +393,13 @@ You can now continue developing your application. Just run `odo push` and refres You can also run `odo watch` to watch changes in the source code. Just refreshing the browser will render the source code changes. Run `odo delete` to delete the application from cluster. + +. To delete your deployed application: ++ +[source,sh] +---- + $ odo delete + ? Are you sure you want to delete the devfile component: java-spring-boot? Yes + ✓ Deleting devfile component java-spring-boot [139ms] + ✓ Successfully deleted component +---- diff --git a/pkg/catalog/catalog.go b/pkg/catalog/catalog.go index 033a1ae9e7c..e66a00ea61a 100644 --- a/pkg/catalog/catalog.go +++ b/pkg/catalog/catalog.go @@ -200,6 +200,7 @@ func ListComponents(client *occlient.Client) (ComponentTypeList, error) { }, Items: catalogList, }, nil + } // SearchComponent searches for the component diff --git a/pkg/component/component.go b/pkg/component/component.go index e880fcdb303..51afbeda77c 100644 --- a/pkg/component/component.go +++ b/pkg/component/component.go @@ -25,7 +25,6 @@ import ( "github.com/openshift/odo/pkg/exec" "github.com/openshift/odo/pkg/log" "github.com/openshift/odo/pkg/occlient" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/odo/util/validation" "github.com/openshift/odo/pkg/preference" "github.com/openshift/odo/pkg/storage" @@ -564,10 +563,10 @@ func ensureAndLogProperResourceUsage(resourceMin, resourceMax *string, resourceN // componentConfig: Component configuration // envSpecificInfo: Component environment specific information, available if uses devfile // cmpExist: true if components exists in the cluster +// isS2I: Legacy option. Set as true if you want to use the old S2I method as it differentiates slightly. // Returns: // err: Errors if any else nil -func ApplyConfig(client *occlient.Client, kClient *kclient.Client, componentConfig config.LocalConfigInfo, envSpecificInfo envinfo.EnvSpecificInfo, stdout io.Writer, cmpExist bool) (err error) { - isExperimentalModeEnabled := experimental.IsExperimentalModeEnabled() +func ApplyConfig(client *occlient.Client, kClient *kclient.Client, componentConfig config.LocalConfigInfo, envSpecificInfo envinfo.EnvSpecificInfo, stdout io.Writer, cmpExist bool, isS2I bool) (err error) { if client == nil { var err error @@ -578,7 +577,7 @@ func ApplyConfig(client *occlient.Client, kClient *kclient.Client, componentConf client.Namespace = envSpecificInfo.GetNamespace() } - if !isExperimentalModeEnabled { + if isS2I { // if component exist then only call the update function if cmpExist { if err = Update(client, componentConfig, componentConfig.GetSourceLocation(), stdout); err != nil { @@ -589,7 +588,7 @@ func ApplyConfig(client *occlient.Client, kClient *kclient.Client, componentConf var componentName string var applicationName string - if !isExperimentalModeEnabled || kClient == nil { + if isS2I || kClient == nil { componentName = componentConfig.GetName() applicationName = componentConfig.GetApplication() } else { @@ -603,12 +602,12 @@ func ApplyConfig(client *occlient.Client, kClient *kclient.Client, componentConf } return urlpkg.Push(client, kClient, urlpkg.PushParameters{ - ComponentName: componentName, - ApplicationName: applicationName, - ConfigURLs: componentConfig.GetURL(), - EnvURLS: envSpecificInfo.GetURL(), - IsRouteSupported: isRouteSupported, - IsExperimentalModeEnabled: isExperimentalModeEnabled, + ComponentName: componentName, + ApplicationName: applicationName, + ConfigURLs: componentConfig.GetURL(), + EnvURLS: envSpecificInfo.GetURL(), + IsRouteSupported: isRouteSupported, + IsS2I: isS2I, }) } diff --git a/pkg/component/component_full_description.go b/pkg/component/component_full_description.go index 8b92dfd725a..65f8e49a9f1 100644 --- a/pkg/component/component_full_description.go +++ b/pkg/component/component_full_description.go @@ -8,7 +8,6 @@ import ( "github.com/openshift/odo/pkg/config" "github.com/openshift/odo/pkg/log" "github.com/openshift/odo/pkg/occlient" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/storage" urlpkg "github.com/openshift/odo/pkg/url" corev1 "k8s.io/api/core/v1" @@ -190,22 +189,22 @@ func (cfd *ComponentFullDescription) Print(client *occlient.Client) error { if len(cfd.Spec.URL.Items) > 0 { var output string - if !experimental.IsExperimentalModeEnabled() { - // if the component is not pushed - for i, componentURL := range cfd.Spec.URL.Items { - if componentURL.Status.State == urlpkg.StateTypePushed { - output += fmt.Sprintf(" · %v exposed via %v\n", urlpkg.GetURLString(componentURL.Spec.Protocol, componentURL.Spec.Host, "", experimental.IsExperimentalModeEnabled()), componentURL.Spec.Port) + // For S2I Only.. + // if the component is not pushed + for i, componentURL := range cfd.Spec.URL.Items { + if componentURL.Status.State == urlpkg.StateTypePushed { + output += fmt.Sprintf(" · %v exposed via %v\n", urlpkg.GetURLString(componentURL.Spec.Protocol, componentURL.Spec.Host, "", true), componentURL.Spec.Port) + } else { + var p string + if i >= len(cfd.Spec.Ports) { + p = cfd.Spec.Ports[len(cfd.Spec.Ports)-1] } else { - var p string - if i >= len(cfd.Spec.Ports) { - p = cfd.Spec.Ports[len(cfd.Spec.Ports)-1] - } else { - p = cfd.Spec.Ports[i] - } - output += fmt.Sprintf(" · URL named %s will be exposed via %v\n", componentURL.Name, p) + p = cfd.Spec.Ports[i] } + output += fmt.Sprintf(" · URL named %s will be exposed via %v\n", componentURL.Name, p) } } + // Cut off the last newline and output if len(output) > 0 { output = output[:len(output)-1] diff --git a/pkg/component/types.go b/pkg/component/types.go index 0c0ad274243..116d6528523 100644 --- a/pkg/component/types.go +++ b/pkg/component/types.go @@ -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" ) diff --git a/pkg/devfile/adapters/common/command.go b/pkg/devfile/adapters/common/command.go index e4bc3ebfcee..dee06fc5175 100644 --- a/pkg/devfile/adapters/common/command.go +++ b/pkg/devfile/adapters/common/command.go @@ -7,6 +7,7 @@ import ( "github.com/openshift/odo/pkg/devfile/parser/data" "github.com/openshift/odo/pkg/devfile/parser/data/common" + "github.com/pkg/errors" "k8s.io/klog" ) @@ -143,7 +144,7 @@ func validateCommand(data data.DevfileData, command common.DevfileCommand) (err if command.Composite != nil { parentCommands := make(map[string]string) commandsMap := GetCommandsMap(data.GetCommands()) - return validateCompositeCommand(command.Composite, parentCommands, commandsMap) + return validateCompositeCommand(data, command.Composite, parentCommands, commandsMap) } // component must be specified @@ -173,7 +174,7 @@ func validateCommand(data data.DevfileData, command common.DevfileCommand) (err } // validateCompositeCommand checks that the specified composite command is valid -func validateCompositeCommand(compositeCommand *common.Composite, parentCommands map[string]string, devfileCommands map[string]common.DevfileCommand) error { +func validateCompositeCommand(data data.DevfileData, compositeCommand *common.Composite, parentCommands map[string]string, devfileCommands map[string]common.DevfileCommand) error { if compositeCommand.Group != nil && compositeCommand.Group.Kind == common.RunCommandGroupType { return fmt.Errorf("composite commands of run Kind are not supported currently") } @@ -200,12 +201,16 @@ func validateCompositeCommand(compositeCommand *common.Composite, parentCommands if subCommand.Composite != nil { // Recursively validate the composite subcommand - err := validateCompositeCommand(subCommand.Composite, parentCommands, devfileCommands) + err := validateCompositeCommand(data, subCommand.Composite, parentCommands, devfileCommands) if err != nil { // Don't wrap the error message here to make the error message more readable to the user return err } - + } else { + err := validateCommand(data, subCommand) + if err != nil { + return errors.Wrapf(err, "the composite command %q references an invalid command %q", compositeCommand.Id, subCommand.GetID()) + } } } return nil diff --git a/pkg/devfile/adapters/common/command_test.go b/pkg/devfile/adapters/common/command_test.go index 5005ad59b65..fa80129aee1 100644 --- a/pkg/devfile/adapters/common/command_test.go +++ b/pkg/devfile/adapters/common/command_test.go @@ -1868,6 +1868,33 @@ func TestValidateCompositeCommand(t *testing.T) { }, wantErr: true, }, + { + name: "Case 6: Invalid composite command, points to invalid exec command", + compositeCommands: []common.Composite{ + { + Id: id[3], + Commands: []string{id[0], id[1]}, + Group: &versionsCommon.Group{Kind: buildGroup}, + }, + }, + execCommands: []common.Exec{ + { + Id: id[0], + CommandLine: command[0], + Component: component, + Group: &common.Group{Kind: runGroup}, + WorkingDir: workDir[0], + }, + { + Id: id[1], + CommandLine: command[1], + Component: "some-fake-component", + Group: &common.Group{Kind: runGroup}, + WorkingDir: workDir[1], + }, + }, + wantErr: true, + }, } for _, tt := range tests { devObj := devfileParser.DevfileObj{ @@ -1882,7 +1909,7 @@ func TestValidateCompositeCommand(t *testing.T) { commandsMap := GetCommandsMap(devObj.Data.GetCommands()) parentCommands := make(map[string]string) - err := validateCompositeCommand(cmd.Composite, parentCommands, commandsMap) + err := validateCompositeCommand(devObj.Data, cmd.Composite, parentCommands, commandsMap) if !tt.wantErr == (err != nil) { t.Errorf("TestValidateAction unexpected error: %v", err) return diff --git a/pkg/devfile/adapters/common/utils.go b/pkg/devfile/adapters/common/utils.go index ceca0fd7647..be260d9c3ca 100644 --- a/pkg/devfile/adapters/common/utils.go +++ b/pkg/devfile/adapters/common/utils.go @@ -61,8 +61,8 @@ const ( // Default volume size for volumes defined in a devfile volumeSize = "5Gi" - // EnvCheProjectsRoot is the env defined for /projects where component mountSources=true - EnvCheProjectsRoot = "CHE_PROJECTS_ROOT" + // EnvProjectsRoot is the env defined for /projects where component mountSources=true + EnvProjectsRoot = "PROJECTS_ROOT" // EnvOdoCommandRunWorkingDir is the env defined in the runtime component container which holds the work dir for the run command EnvOdoCommandRunWorkingDir = "ODO_COMMAND_RUN_WORKING_DIR" @@ -235,3 +235,14 @@ func GetCommandsMap(commands []common.DevfileCommand) map[string]common.DevfileC } return commandsMap } + +// GetComponentEnvVar returns true if a list of env vars contains the specified env var +// If the env exists, it returns the value of it +func GetComponentEnvVar(env string, envs []common.Env) string { + for _, envVar := range envs { + if envVar.Name == env { + return envVar.Value + } + } + return "" +} diff --git a/pkg/devfile/adapters/common/utils_test.go b/pkg/devfile/adapters/common/utils_test.go index ec4a8cce1ee..57595ab314f 100644 --- a/pkg/devfile/adapters/common/utils_test.go +++ b/pkg/devfile/adapters/common/utils_test.go @@ -587,3 +587,68 @@ func TestGetCommandsMap(t *testing.T) { } } + +func TestGetComponentEnvVar(t *testing.T) { + + tests := []struct { + name string + env string + envs []common.Env + want string + }{ + { + name: "Case 1: No env vars", + env: "test", + envs: nil, + want: "", + }, + { + name: "Case 2: Has env", + env: "PROJECTS_ROOT", + envs: []common.Env{ + { + Name: "SOME_ENV", + Value: "test", + }, + { + Name: "TESTER", + Value: "tester", + }, + { + Name: "PROJECTS_ROOT", + Value: "/test", + }, + }, + want: "/test", + }, + { + name: "Case 3: No env, multiple values", + env: "PROJECTS_ROOT", + envs: []common.Env{ + { + Name: "TESTER", + Value: "fake", + }, + { + Name: "FAKE", + Value: "fake", + }, + { + Name: "ENV", + Value: "fake", + }, + }, + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + value := GetComponentEnvVar(tt.env, tt.envs) + if value != tt.want { + t.Errorf("TestGetComponentEnvVar error: env value mismatch, expected: %v got: %v", tt.want, value) + } + + }) + } + +} diff --git a/pkg/devfile/adapters/docker/component/utils.go b/pkg/devfile/adapters/docker/component/utils.go index 8a53a66f207..31433c25829 100644 --- a/pkg/devfile/adapters/docker/component/utils.go +++ b/pkg/devfile/adapters/docker/component/utils.go @@ -198,17 +198,20 @@ func (a Adapter) startComponent(mounts []mount.Mount, comp versionsCommon.Devfil } updateComponentWithSupervisord(&comp, runCommand, a.supervisordVolumeName, &hostConfig) - // If the component set `mountSources` to true, add the source volume and env CHE_PROJECTS_ROOT to it + // If the component set `mountSources` to true, add the source volume and env PROJECTS_ROOT to it if comp.Container.MountSources { + var syncFolder, projectsRoot string if comp.Container.SourceMapping != "" { - utils.AddVolumeToContainer(a.projectVolumeName, comp.Container.SourceMapping, &hostConfig) + syncFolder = comp.Container.SourceMapping + } else if projectsRoot = common.GetComponentEnvVar(common.EnvProjectsRoot, comp.Container.Env); projectsRoot != "" { + syncFolder = projectsRoot } else { - utils.AddVolumeToContainer(a.projectVolumeName, lclient.OdoSourceVolumeMount, &hostConfig) + syncFolder = lclient.OdoSourceVolumeMount } - - if !common.IsEnvPresent(comp.Container.Env, common.EnvCheProjectsRoot) { - envName := common.EnvCheProjectsRoot - envValue := lclient.OdoSourceVolumeMount + utils.AddVolumeToContainer(a.projectVolumeName, syncFolder, &hostConfig) + if projectsRoot != "" { + envName := common.EnvProjectsRoot + envValue := syncFolder comp.Container.Env = append(comp.Container.Env, versionsCommon.Env{ Name: envName, Value: envValue, diff --git a/pkg/devfile/adapters/kubernetes/component/adapter.go b/pkg/devfile/adapters/kubernetes/component/adapter.go index d1198e7a75e..f02884bb2a8 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter.go @@ -124,7 +124,7 @@ func (a Adapter) Push(parameters common.PushParameters) (err error) { return errors.Wrapf(err, "unable to get pod for component %s", a.ComponentName) } - err = component.ApplyConfig(nil, &a.Client, config.LocalConfigInfo{}, parameters.EnvSpecificInfo, color.Output, componentExists) + err = component.ApplyConfig(nil, &a.Client, config.LocalConfigInfo{}, parameters.EnvSpecificInfo, color.Output, componentExists, false) if err != nil { odoutil.LogErrorAndExit(err, "Failed to update config to component deployed.") } diff --git a/pkg/devfile/adapters/kubernetes/utils/utils.go b/pkg/devfile/adapters/kubernetes/utils/utils.go index 6cc515e9ca6..0e57d0cd996 100644 --- a/pkg/devfile/adapters/kubernetes/utils/utils.go +++ b/pkg/devfile/adapters/kubernetes/utils/utils.go @@ -83,9 +83,11 @@ func GetContainers(devfileObj devfileParser.DevfileObj) ([]corev1.Container, err // If `mountSources: true` was set, add an empty dir volume to the container to sync the source to // Sync to `Container.SourceMapping` if set if comp.Container.MountSources { - var syncFolder string + var syncFolder, projectsRoot string if comp.Container.SourceMapping != "" { syncFolder = comp.Container.SourceMapping + } else if projectsRoot = adaptersCommon.GetComponentEnvVar(adaptersCommon.EnvProjectsRoot, comp.Container.Env); projectsRoot != "" { + syncFolder = projectsRoot } else { syncFolder = kclient.OdoSourceVolumeMount } @@ -96,10 +98,10 @@ func GetContainers(devfileObj devfileParser.DevfileObj) ([]corev1.Container, err }) // only add the env if it is not set by the devfile - if !isEnvPresent(container.Env, adaptersCommon.EnvCheProjectsRoot) { + if projectsRoot == "" { container.Env = append(container.Env, corev1.EnvVar{ - Name: adaptersCommon.EnvCheProjectsRoot, + Name: adaptersCommon.EnvProjectsRoot, Value: syncFolder, }) } diff --git a/pkg/devfile/validate/components_test.go b/pkg/devfile/validate/components_test.go index 539a01a4367..b26e5704903 100644 --- a/pkg/devfile/validate/components_test.go +++ b/pkg/devfile/validate/components_test.go @@ -126,7 +126,7 @@ func TestValidateComponents(t *testing.T) { got := validateComponents(components) want := "size randomgarbage for volume component myvol is invalid" - if !strings.Contains(got.Error(), want) { + if got != nil && !strings.Contains(got.Error(), want) { t.Errorf("TestValidateComponents error - got: '%v', want substring: '%v'", got.Error(), want) } }) diff --git a/pkg/occlient/occlient.go b/pkg/occlient/occlient.go index 75278f8b99e..a3981b6c65a 100644 --- a/pkg/occlient/occlient.go +++ b/pkg/occlient/occlient.go @@ -22,7 +22,6 @@ import ( "github.com/openshift/odo/pkg/config" "github.com/openshift/odo/pkg/devfile/adapters/common" "github.com/openshift/odo/pkg/log" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/preference" "github.com/openshift/odo/pkg/util" @@ -751,11 +750,7 @@ func (c *Client) GetImageStream(imageNS string, imageName string, imageTag strin } } if e != nil && err != nil { - // Imagestream not found in openshift and current namespaces - if experimental.IsExperimentalModeEnabled() { - return nil, fmt.Errorf("component type %q not found", imageName) - } - return nil, err + return nil, fmt.Errorf("component type %q not found", imageName) } // Required tag not in openshift and current namespaces diff --git a/pkg/odo/cli/catalog/list/components.go b/pkg/odo/cli/catalog/list/components.go index 3e5efe180e9..78293a26607 100644 --- a/pkg/odo/cli/catalog/list/components.go +++ b/pkg/odo/cli/catalog/list/components.go @@ -12,7 +12,6 @@ import ( "github.com/openshift/odo/pkg/machineoutput" catalogutil "github.com/openshift/odo/pkg/odo/cli/catalog/util" "github.com/openshift/odo/pkg/odo/genericclioptions" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/odo/util/pushtarget" "github.com/openshift/odo/pkg/util" "github.com/spf13/cobra" @@ -47,32 +46,32 @@ func (o *ListComponentsOptions) Complete(name string, cmd *cobra.Command, args [ if !pushtarget.IsPushTargetDocker() { o.Context = genericclioptions.NewContext(cmd) + supported, err := o.Client.IsImageStreamSupported() + if err != nil { + klog.V(4).Info("ignoring error while checking imagestream support:", err.Error()) + } - tasks.Add(util.ConcurrentTask{ToRun: func(errChannel chan error) { - o.catalogList, err = catalog.ListComponents(o.Client) - if err != nil { - if experimental.IsExperimentalModeEnabled() { - klog.V(4).Info("Please log in to an OpenShift cluster to list OpenShift/s2i components") - } else { + if supported { + tasks.Add(util.ConcurrentTask{ToRun: func(errChannel chan error) { + o.catalogList, err = catalog.ListComponents(o.Client) + if err != nil { errChannel <- err + } else { + o.catalogList.Items = catalogutil.FilterHiddenComponents(o.catalogList.Items) } - } else { - o.catalogList.Items = catalogutil.FilterHiddenComponents(o.catalogList.Items) - } - }}) + }}) + } } - if experimental.IsExperimentalModeEnabled() { - tasks.Add(util.ConcurrentTask{ToRun: func(errChannel chan error) { - o.catalogDevfileList, err = catalog.ListDevfileComponents("") - if o.catalogDevfileList.DevfileRegistries == nil { - log.Warning("Please run 'odo registry add ' to add registry for listing devfile components\n") - } - if err != nil { - errChannel <- err - } - }}) - } + tasks.Add(util.ConcurrentTask{ToRun: func(errChannel chan error) { + o.catalogDevfileList, err = catalog.ListDevfileComponents("") + if o.catalogDevfileList.DevfileRegistries == nil { + log.Warning("Please run 'odo registry add ' to add registry for listing devfile components\n") + } + if err != nil { + errChannel <- err + } + }}) return tasks.Run() } @@ -101,19 +100,15 @@ func (o *ListComponentsOptions) Run() (err error) { supported, _ := catalog.SliceSupportedTags(image) o.catalogList.Items[i].Spec.SupportedTags = supported } - if experimental.IsExperimentalModeEnabled() { - combinedList := combinedCatalogList{ - TypeMeta: metav1.TypeMeta{ - Kind: "List", - APIVersion: "odo.dev/v1alpha1", - }, - S2iItems: o.catalogList.Items, - DevfileItems: o.catalogDevfileList.Items, - } - machineoutput.OutputSuccess(combinedList) - } else { - machineoutput.OutputSuccess(o.catalogList) + combinedList := combinedCatalogList{ + TypeMeta: metav1.TypeMeta{ + Kind: "List", + APIVersion: "odo.dev/v1alpha1", + }, + S2iItems: o.catalogList.Items, + DevfileItems: o.catalogDevfileList.Items, } + machineoutput.OutputSuccess(combinedList) } else { w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent) var supCatalogList, unsupCatalogList []catalog.ComponentType @@ -132,7 +127,18 @@ func (o *ListComponentsOptions) Run() (err error) { } } + if len(o.catalogDevfileList.Items) != 0 { + fmt.Fprintln(w, "Odo Devfile Components:") + fmt.Fprintln(w, "NAME", "\t", "DESCRIPTION", "\t", "REGISTRY") + + o.printDevfileCatalogList(w, o.catalogDevfileList.Items, "") + } + if len(supCatalogList) != 0 || len(unsupCatalogList) != 0 { + // add a new line of there was something before this + if len(o.catalogDevfileList.Items) != 0 { + fmt.Fprintln(w) + } fmt.Fprintln(w, "Odo OpenShift Components:") fmt.Fprintln(w, "NAME", "\t", "PROJECT", "\t", "TAGS", "\t", "SUPPORTED") @@ -146,14 +152,7 @@ func (o *ListComponentsOptions) Run() (err error) { o.printCatalogList(w, unsupCatalogList, supported) } - fmt.Fprintln(w) - } - - if len(o.catalogDevfileList.Items) != 0 { - fmt.Fprintln(w, "Odo Devfile Components:") - fmt.Fprintln(w, "NAME", "\t", "DESCRIPTION", "\t", "REGISTRY") - - o.printDevfileCatalogList(w, o.catalogDevfileList.Items, "") + fmt.Fprint(w) } w.Flush() diff --git a/pkg/odo/cli/cli.go b/pkg/odo/cli/cli.go index 9dbea1893c8..c35ed0a3bcf 100644 --- a/pkg/odo/cli/cli.go +++ b/pkg/odo/cli/cli.go @@ -24,7 +24,6 @@ import ( "github.com/openshift/odo/pkg/odo/cli/version" "github.com/openshift/odo/pkg/odo/util" odoutil "github.com/openshift/odo/pkg/odo/util" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -205,12 +204,10 @@ func odoRootCmd(name, fullName string) *cobra.Command { debug.NewCmdDebug(debug.RecommendedCommandName, util.GetFullName(fullName, debug.RecommendedCommandName)), ) - if experimental.IsExperimentalModeEnabled() { - rootCmd.AddCommand( - registry.NewCmdRegistry(registry.RecommendedCommandName, util.GetFullName(fullName, registry.RecommendedCommandName)), - component.NewCmdTest(component.TestRecommendedCommandName, util.GetFullName(fullName, component.TestRecommendedCommandName)), - ) - } + rootCmd.AddCommand( + registry.NewCmdRegistry(registry.RecommendedCommandName, util.GetFullName(fullName, registry.RecommendedCommandName)), + component.NewCmdTest(component.TestRecommendedCommandName, util.GetFullName(fullName, component.TestRecommendedCommandName)), + ) odoutil.VisitCommands(rootCmd, reconfigureCmdWithSubcmd) diff --git a/pkg/odo/cli/component/common_link.go b/pkg/odo/cli/component/common_link.go index fef37ce49df..d450622e528 100644 --- a/pkg/odo/cli/component/common_link.go +++ b/pkg/odo/cli/component/common_link.go @@ -12,7 +12,6 @@ import ( "github.com/openshift/odo/pkg/log" "github.com/openshift/odo/pkg/occlient" "github.com/openshift/odo/pkg/odo/genericclioptions" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/secret" svc "github.com/openshift/odo/pkg/service" "github.com/openshift/odo/pkg/util" @@ -38,6 +37,8 @@ type commonLinkOptions struct { secretName string isTargetAService bool + devfilePath string + suppliedName string operation func(secretName, componentName, applicationName string) error operationName string @@ -55,12 +56,13 @@ func newCommonLinkOptions() *commonLinkOptions { // Complete completes LinkOptions after they've been created func (o *commonLinkOptions) complete(name string, cmd *cobra.Command, args []string) (err error) { + o.operationName = name suppliedName := args[0] o.suppliedName = suppliedName - if experimental.IsExperimentalModeEnabled() { + if util.CheckPathExists(o.devfilePath) { o.Context = genericclioptions.NewDevfileContext(cmd) oclient, err := occlient.New() @@ -161,7 +163,7 @@ func (o *commonLinkOptions) complete(name string, cmd *cobra.Command, args []str func (o *commonLinkOptions) validate(wait bool) (err error) { - if experimental.IsExperimentalModeEnabled() { + if util.CheckPathExists(o.devfilePath) { // let's validate if the service exists svcFullName := strings.Join([]string{o.serviceType, o.serviceName}, "/") svcExists, err := svc.OperatorSvcExists(o.KClient, svcFullName) @@ -227,7 +229,8 @@ func (o *commonLinkOptions) validate(wait bool) (err error) { } func (o *commonLinkOptions) run() (err error) { - if experimental.IsExperimentalModeEnabled() { + + if util.CheckPathExists(o.devfilePath) { // convert service binding request into a ma[string]interface{} type so // as to use it with dynamic client sbrMap := make(map[string]interface{}) diff --git a/pkg/odo/cli/component/common_push.go b/pkg/odo/cli/component/common_push.go index 5dc09f58fbb..80e8dffea84 100644 --- a/pkg/odo/cli/component/common_push.go +++ b/pkg/odo/cli/component/common_push.go @@ -127,7 +127,7 @@ func (cpo *CommonPushOptions) createCmpIfNotExistsAndApplyCmpConfig(stdout io.Wr } } // Apply config - err := component.ApplyConfig(cpo.Context.Client, nil, *cpo.LocalConfigInfo, envinfo.EnvSpecificInfo{}, stdout, cpo.doesComponentExist) + err := component.ApplyConfig(cpo.Context.Client, nil, *cpo.LocalConfigInfo, envinfo.EnvSpecificInfo{}, stdout, cpo.doesComponentExist, true) if err != nil { odoutil.LogErrorAndExit(err, "Failed to update config to component deployed.") } diff --git a/pkg/odo/cli/component/component.go b/pkg/odo/cli/component/component.go index 763a7a8d559..844dcbdf912 100644 --- a/pkg/odo/cli/component/component.go +++ b/pkg/odo/cli/component/component.go @@ -73,6 +73,8 @@ func NewCmdComponent(name, fullName string) *cobra.Command { componentCmd.Flags().AddFlagSet(componentGetCmd.Flags()) componentCmd.AddCommand(componentGetCmd, createCmd, deleteCmd, describeCmd, linkCmd, unlinkCmd, listCmd, logCmd, pushCmd, updateCmd, watchCmd, execCmd) + + // Experimental feature to be added, "odo test" command. if experimental.IsExperimentalModeEnabled() { componentCmd.AddCommand(testCmd) } diff --git a/pkg/odo/cli/component/create.go b/pkg/odo/cli/component/create.go index 89c519d83f7..ac45d8693ab 100644 --- a/pkg/odo/cli/component/create.go +++ b/pkg/odo/cli/component/create.go @@ -30,7 +30,6 @@ import ( "github.com/openshift/odo/pkg/odo/genericclioptions" odoutil "github.com/openshift/odo/pkg/odo/util" "github.com/openshift/odo/pkg/odo/util/completion" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/odo/util/pushtarget" "github.com/openshift/odo/pkg/util" @@ -50,6 +49,7 @@ type CreateOptions struct { appName string interactive bool now bool + forceS2i bool *CommonPushOptions devfileMetadata DevfileMetadata } @@ -80,24 +80,12 @@ const CreateRecommendedCommandName = "create" // since the application will always be in the same directory as `.odo`, we will always set this as: ./ const LocalDirectoryDefaultLocation = "./" -// Constants for devfile component -const ( - devFile = "devfile.yaml" -) - var ( envFile = filepath.Join(".odo", "env", "env.yaml") configFile = filepath.Join(".odo", "config.yaml") envDir = filepath.Join(".odo", "env") ) -// DevfilePath is the devfile path that is used by odo, -// which means odo can: -// 1. Directly use the devfile in DevfilePath -// 2. Download devfile from registry to DevfilePath then use the devfile in DevfilePath -// 3. Copy user's own devfile (path is specified via --devfile flag) to DevfilePath then use the devfile in DevfilePath -var DevfilePath = filepath.Join(LocalDirectoryDefaultLocation, devFile) - // EnvFilePath is the path of env file for devfile component var EnvFilePath = filepath.Join(LocalDirectoryDefaultLocation, envFile) @@ -200,7 +188,7 @@ func (co *CreateOptions) setComponentSourceAttributes() (err error) { // Error out by default if no type of sources were passed.. default: - return fmt.Errorf("The source can be either --binary or --local or --git") + return fmt.Errorf("the source can be either --binary or --local or --git") } @@ -211,7 +199,7 @@ func (co *CreateOptions) setComponentSourceAttributes() (err error) { // Error out if reference is passed but no --git flag passed if len(co.componentGit) == 0 && len(co.componentGitRef) != 0 { - return fmt.Errorf("The --ref flag is only valid for --git flag") + return fmt.Errorf("the --ref flag is only valid for --git flag") } return @@ -295,12 +283,76 @@ func createDefaultComponentName(context *genericclioptions.Context, componentTyp return componentName, nil } +func (co *CreateOptions) checkConflictingFlags() (err error) { + if err = co.checkConflictingS2IFlags(); err != nil { + return + } + + if err = co.checkConflictingDevfileFlags(); err != nil { + return + } + + return nil +} + +func (co *CreateOptions) checkConflictingS2IFlags() error { + if !co.forceS2i { + errorString := "flag --%s, requires --s2i flag to be set true, when in experimental mode." + + var flagName string + if co.now { + flagName = "now" + } else if len(co.componentBinary) != 0 { + flagName = "binary" + } else if len(co.componentGit) != 0 { + flagName = "git" + } else if len(co.componentEnvVars) != 0 { + flagName = "env" + } else if len(co.componentPorts) != 0 { + flagName = "port" + } + + if len(flagName) != 0 { + return errors.New(fmt.Sprintf(errorString, flagName)) + } + } + return nil +} + +func (co *CreateOptions) checkConflictingDevfileFlags() error { + if co.forceS2i { + errorString := "you can't set --s2i flag as true if you want to use the %s via --%s" + + var flagName string + if len(co.devfileMetadata.devfilePath.value) != 0 { + flagName = "devfile" + } else if len(co.devfileMetadata.devfileRegistry.Name) != 0 { + flagName = "registry" + } else if len(co.devfileMetadata.token) != 0 { + flagName = "token" + } else if len(co.devfileMetadata.starter) != 0 { + flagName = "starter" + } + + if len(flagName) != 0 { + return errors.New(fmt.Sprintf(errorString, flagName, flagName)) + } + } + return nil +} + // Complete completes create args func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) { - if experimental.IsExperimentalModeEnabled() { - // Add a disclaimer that we are in *experimental mode* - log.Experimental("Experimental mode is enabled, use at your own risk") + // this populates the LocalConfigInfo as well + co.Context = genericclioptions.NewContextCreatingAppIfNeeded(cmd) + + // If not using --s2i + if !co.forceS2i { + err = co.checkConflictingFlags() + if err != nil { + return + } // Configure the context if co.componentContext != "" { @@ -311,21 +363,33 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string } if util.CheckPathExists(ConfigFilePath) || util.CheckPathExists(EnvFilePath) { - return errors.New("This directory already contains a component") + return errors.New("this directory already contains a component") } if util.CheckPathExists(DevfilePath) && co.devfileMetadata.devfilePath.value != "" && !util.PathEqual(DevfilePath, co.devfileMetadata.devfilePath.value) { - return errors.New("This directory already contains a devfile, you can't specify devfile via --devfile") + return errors.New("this directory already contains a devfile, you can't specify devfile via --devfile") } 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 { - return errors.Errorf("The devfile path you specify is invalid with either file error \"%v\" or url error \"%v\"", fileErr, urlErr) + return errors.Errorf("the devfile path you specify is invalid with either file error \"%v\" or url error \"%v\"", fileErr, urlErr) } else if fileErr == nil { co.devfileMetadata.devfilePath.protocol = "file" } else if urlErr == nil { @@ -335,16 +399,21 @@ 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") + return errors.New("you can't specify registry via --registry if you want to use the devfile that is specified via --devfile") } registryList, err := catalog.GetDevfileRegistries(co.devfileMetadata.devfileRegistry.Name) if err != nil { - return errors.Wrap(err, "Failed to get registry") + return errors.Wrap(err, "failed to get registry") } if len(registryList) == 0 { - return errors.Errorf("Registry %s doesn't exist, please specify a valid registry via --registry", co.devfileMetadata.devfileRegistry.Name) + return errors.Errorf("registry %s doesn't exist, please specify a valid registry via --registry", co.devfileMetadata.devfileRegistry.Name) } } @@ -371,7 +440,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 @@ -409,8 +478,12 @@ 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)) + return errors.Errorf("accepts between 0 and 1 arg when using existing devfile, received %d", len(args)) } // If user can use existing devfile directly, the first arg is component name instead of component type @@ -426,7 +499,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) @@ -445,7 +518,7 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string return err } if co.devfileMetadata.devfileRegistry.Name != "" && catalogDevfileList.Items == nil { - return errors.Errorf("Can't create devfile component from registry %s", co.devfileMetadata.devfileRegistry.Name) + return errors.Errorf("can't create devfile component from registry %s", co.devfileMetadata.devfileRegistry.Name) } if len(catalogDevfileList.DevfileRegistries) == 0 { @@ -504,7 +577,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") @@ -524,6 +597,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 devfile component type with --s2i flag") + } + } + existSpinner := log.Spinner("Checking devfile existence") if hasComponent { existSpinner.End(true) @@ -551,9 +637,9 @@ 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) } } @@ -561,12 +647,6 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string 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 @@ -587,8 +667,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 @@ -756,29 +834,27 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string // Validate validates the create parameters func (co *CreateOptions) Validate() (err error) { - if experimental.IsExperimentalModeEnabled() { - if 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) + 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) - err = util.ValidateK8sResourceName("component name", co.devfileMetadata.componentName) + err = util.ValidateK8sResourceName("component name", co.devfileMetadata.componentName) + if err != nil { + return err + } + + // Only validate namespace if pushtarget isn't docker + if !pushtarget.IsPushTargetDocker() { + err := util.ValidateK8sResourceName("component namespace", co.devfileMetadata.componentNamespace) if err != nil { return err } + } - // Only validate namespace if pushtarget isn't docker - if !pushtarget.IsPushTargetDocker() { - err := util.ValidateK8sResourceName("component namespace", co.devfileMetadata.componentNamespace) - if err != nil { - return err - } - } - - spinner.End(true) + spinner.End(true) - return nil - } + return nil } log.Info("Validation") @@ -910,92 +986,90 @@ 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 { - // Use existing devfile directly from --devfile flag - if co.devfileMetadata.devfilePath.value != "" { - if co.devfileMetadata.devfilePath.protocol == "http(s)" { - // User specify devfile path is http(s) URL - params := util.DownloadParams{ - Request: util.HTTPRequestParams{ - URL: co.devfileMetadata.devfilePath.value, - Token: co.devfileMetadata.token, - }, - Filepath: DevfilePath, - } - err = util.DownloadFile(params) - if err != nil { - return errors.Wrapf(err, "failed to download devfile for devfile component from %s", co.devfileMetadata.devfilePath.value) - } - } else if co.devfileMetadata.devfilePath.protocol == "file" { - // User specify devfile path is file system link - info, err := os.Stat(co.devfileMetadata.devfilePath.value) - if err != nil { - return err - } - err = util.CopyFile(co.devfileMetadata.devfilePath.value, DevfilePath, info) - if err != nil { - return errors.Wrapf(err, "failed to copy devfile from %s to %s", co.devfileMetadata.devfilePath, DevfilePath) - } - } - } - - if !util.CheckPathExists(DevfilePath) { - // Download devfile from registry + 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)" { + // User specify devfile path is http(s) URL params := util.DownloadParams{ Request: util.HTTPRequestParams{ - URL: co.devfileMetadata.devfileRegistry.URL + co.devfileMetadata.devfileLink, + URL: co.devfileMetadata.devfilePath.value, + Token: co.devfileMetadata.token, }, Filepath: DevfilePath, } - if registryUtil.IsSecure(co.devfileMetadata.devfileRegistry.Name) { - token, err := keyring.Get(util.CredentialPrefix+co.devfileMetadata.devfileRegistry.Name, "default") - if err != nil { - return errors.Wrap(err, "unable to get secure registry credential from keyring") - } - params.Request.Token = token + err = util.DownloadFile(params) + if err != nil { + return errors.Wrapf(err, "failed to download devfile for devfile component from %s", co.devfileMetadata.devfilePath.value) } - err := util.DownloadFile(params) + } else if co.devfileMetadata.devfilePath.protocol == "file" { + // User specify devfile path is file system link + info, err := os.Stat(co.devfileMetadata.devfilePath.value) if err != nil { - return errors.Wrapf(err, "failed to download devfile for devfile component from %s", co.devfileMetadata.devfileRegistry.URL+co.devfileMetadata.devfileLink) + return err + } + err = util.CopyFile(co.devfileMetadata.devfilePath.value, DevfilePath, info) + if err != nil { + return errors.Wrapf(err, "failed to copy devfile from %s to %s", co.devfileMetadata.devfilePath, DevfilePath) } } + } - if util.CheckPathExists(DevfilePath) && co.devfileMetadata.starter != "" { - err = co.downloadProject(co.devfileMetadata.starter) + if !util.CheckPathExists(DevfilePath) { + // Download devfile from registry + params := util.DownloadParams{ + Request: util.HTTPRequestParams{ + URL: co.devfileMetadata.devfileRegistry.URL + co.devfileMetadata.devfileLink, + }, + Filepath: DevfilePath, + } + if registryUtil.IsSecure(co.devfileMetadata.devfileRegistry.Name) { + token, err := keyring.Get(util.CredentialPrefix+co.devfileMetadata.devfileRegistry.Name, "default") if err != nil { - return errors.Wrap(err, "failed to download project for devfile component") + return errors.Wrap(err, "unable to get secure registry credential from keyring") } + params.Request.Token = token } - - // Generate env file - err = co.EnvSpecificInfo.SetComponentSettings(envinfo.ComponentSettings{ - Name: co.devfileMetadata.componentName, - Namespace: co.devfileMetadata.componentNamespace, - AppName: co.appName, - }) + err := util.DownloadFile(params) if err != nil { - return errors.Wrap(err, "failed to create env file for devfile component") + return errors.Wrapf(err, "failed to download devfile for devfile component from %s", co.devfileMetadata.devfileRegistry.URL+co.devfileMetadata.devfileLink) } + } - sourcePath, err := util.GetAbsPath(co.componentContext) + if util.CheckPathExists(DevfilePath) && co.devfileMetadata.starter != "" { + err = co.downloadProject(co.devfileMetadata.starter) if err != nil { - return errors.Wrap(err, "unable to get source path") + return errors.Wrap(err, "failed to download project for devfile component") } + } - ignoreFile, err := util.CheckGitIgnoreFile(sourcePath) - if err != nil { - return err - } + // Generate env file + err = co.EnvSpecificInfo.SetComponentSettings(envinfo.ComponentSettings{ + Name: co.devfileMetadata.componentName, + Namespace: co.devfileMetadata.componentNamespace, + AppName: co.appName, + }) + if err != nil { + return errors.Wrap(err, "failed to create env file for devfile component") + } - err = util.AddFileToIgnoreFile(ignoreFile, filepath.Join(co.componentContext, envDir)) - if err != nil { - return err - } + sourcePath, err := util.GetAbsPath(co.componentContext) + if err != nil { + return errors.Wrap(err, "unable to get source path") + } - log.Italic("\nPlease use `odo push` command to create the component with source deployed") - return nil + ignoreFile, err := util.CheckGitIgnoreFile(sourcePath) + if err != nil { + return err } + + err = util.AddFileToIgnoreFile(ignoreFile, filepath.Join(co.componentContext, envDir)) + if err != nil { + return err + } + + log.Italic("\nPlease use `odo push` command to create the component with source deployed") + return nil } err = co.LocalConfigInfo.SetComponentSettings(co.componentSettings) @@ -1084,13 +1158,12 @@ func NewCmdCreate(name, fullName string) *cobra.Command { 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") - if experimental.IsExperimentalModeEnabled() { - componentCreateCmd.Flags().StringVar(&co.devfileMetadata.starter, "starter", "", "Download a project specified in the devfile") - componentCreateCmd.Flags().Lookup("starter").NoOptDefVal = defaultProjectName //Default value to pass to the flag if one is not specified. - 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().StringVar(&co.devfileMetadata.starter, "starter", "", "Download a project specified in the devfile") + componentCreateCmd.Flags().Lookup("starter").NoOptDefVal = defaultProjectName //Default value to pass to the flag if one is not specified. + 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) diff --git a/pkg/odo/cli/component/delete.go b/pkg/odo/cli/component/delete.go index bcea7317634..cc76327e4d8 100644 --- a/pkg/odo/cli/component/delete.go +++ b/pkg/odo/cli/component/delete.go @@ -6,7 +6,6 @@ import ( "path/filepath" "github.com/openshift/odo/pkg/envinfo" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/odo/util/pushtarget" "github.com/openshift/odo/pkg/util" @@ -67,7 +66,7 @@ func (do *DeleteOptions) Complete(name string, cmd *cobra.Command, args []string ConfigFilePath = filepath.Join(do.componentContext, configFile) // if experimental mode is enabled and devfile is present - if !do.componentDeleteS2iFlag && experimental.IsExperimentalModeEnabled() && util.CheckPathExists(do.devfilePath) { + if !do.componentDeleteS2iFlag && util.CheckPathExists(do.devfilePath) { do.EnvSpecificInfo, err = envinfo.NewEnvSpecificInfo(do.componentContext) if err != nil { return err @@ -92,7 +91,7 @@ func (do *DeleteOptions) Complete(name string, cmd *cobra.Command, args []string func (do *DeleteOptions) Validate() (err error) { // if experimental mode is enabled and devfile is present - if !do.componentDeleteS2iFlag && experimental.IsExperimentalModeEnabled() && util.CheckPathExists(do.devfilePath) { + if !do.componentDeleteS2iFlag && util.CheckPathExists(do.devfilePath) { return nil } @@ -119,7 +118,7 @@ func (do *DeleteOptions) Run() (err error) { klog.V(4).Infof("component delete called") klog.V(4).Infof("args: %#v", do) - if !do.componentDeleteS2iFlag && experimental.IsExperimentalModeEnabled() && util.CheckPathExists(do.devfilePath) { + if !do.componentDeleteS2iFlag && util.CheckPathExists(do.devfilePath) { return do.DevFileRun() } diff --git a/pkg/odo/cli/component/devfile.go b/pkg/odo/cli/component/devfile.go index 8c22dec2fd0..b055f47b9db 100644 --- a/pkg/odo/cli/component/devfile.go +++ b/pkg/odo/cli/component/devfile.go @@ -2,6 +2,7 @@ package component import ( "os" + "path/filepath" "strings" "github.com/openshift/odo/pkg/devfile" @@ -32,6 +33,18 @@ The behaviour of this feature is subject to change as development for this feature progresses. */ +// Constants for devfile component +const ( + devFile = "devfile.yaml" +) + +// DevfilePath is the devfile path that is used by odo, +// which means odo can: +// 1. Directly use the devfile in DevfilePath +// 2. Download devfile from registry to DevfilePath then use the devfile in DevfilePath +// 3. Copy user's own devfile (path is specified via --devfile flag) to DevfilePath then use the devfile in DevfilePath +var DevfilePath = filepath.Join(LocalDirectoryDefaultLocation, devFile) + // DevfilePush has the logic to perform the required actions for a given devfile func (po *PushOptions) DevfilePush() error { diff --git a/pkg/odo/cli/component/exec.go b/pkg/odo/cli/component/exec.go index 348ee9e8611..98c15e1bc66 100644 --- a/pkg/odo/cli/component/exec.go +++ b/pkg/odo/cli/component/exec.go @@ -8,8 +8,8 @@ import ( "github.com/openshift/odo/pkg/odo/genericclioptions" odoutil "github.com/openshift/odo/pkg/odo/util" "github.com/openshift/odo/pkg/odo/util/completion" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/odo/util/pushtarget" + "github.com/openshift/odo/pkg/util" "path/filepath" @@ -63,7 +63,7 @@ Please provide a command to execute, odo exec -- `) eo.devfilePath = filepath.Join(eo.componentContext, eo.devfilePath) // if experimental mode is enabled and devfile is present - if experimental.IsExperimentalModeEnabled() { + if util.CheckPathExists(eo.devfilePath) { eo.componentOptions.Context = genericclioptions.NewDevfileContext(cmd) if !pushtarget.IsPushTargetDocker() { diff --git a/pkg/odo/cli/component/link.go b/pkg/odo/cli/component/link.go index 5a9620f14f2..4b8bd532534 100644 --- a/pkg/odo/cli/component/link.go +++ b/pkg/odo/cli/component/link.go @@ -2,17 +2,17 @@ package component import ( "fmt" + "path/filepath" "github.com/openshift/odo/pkg/component" "github.com/openshift/odo/pkg/odo/genericclioptions" sbo "github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1" - appCmd "github.com/openshift/odo/pkg/odo/cli/application" projectCmd "github.com/openshift/odo/pkg/odo/cli/project" "github.com/openshift/odo/pkg/odo/util/completion" - "github.com/openshift/odo/pkg/odo/util/experimental" + "github.com/openshift/odo/pkg/util" - "github.com/openshift/odo/pkg/odo/util" + odoutil "github.com/openshift/odo/pkg/odo/util" ktemplates "k8s.io/kubectl/pkg/util/templates" "github.com/spf13/cobra" @@ -88,6 +88,7 @@ odo link EtcdCluster/myetcd type LinkOptions struct { waitForTarget bool componentContext string + *commonLinkOptions } @@ -101,8 +102,10 @@ func NewLinkOptions() *LinkOptions { // Complete completes LinkOptions after they've been created func (o *LinkOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) { + o.commonLinkOptions.devfilePath = filepath.Join(o.componentContext, DevfilePath) + err = o.complete(name, cmd, args) - if !experimental.IsExperimentalModeEnabled() { + if !util.CheckPathExists(o.commonLinkOptions.devfilePath) { o.operation = o.Client.LinkSecret } return err @@ -115,7 +118,8 @@ func (o *LinkOptions) Validate() (err error) { return err } - if experimental.IsExperimentalModeEnabled() { + // Wat? + if util.CheckPathExists(o.commonLinkOptions.devfilePath) { return } @@ -160,22 +164,27 @@ func NewCmdLink(name, fullName string) *cobra.Command { linkCmd.PersistentFlags().BoolVarP(&o.wait, "wait", "w", false, "If enabled the link will return only when the component is fully running after the link is created") linkCmd.PersistentFlags().BoolVar(&o.waitForTarget, "wait-for-target", false, "If enabled, the link command will wait for the service to be provisioned (has no effect when linking to a component)") - linkCmd.SetUsageTemplate(util.CmdUsageTemplate) + linkCmd.SetUsageTemplate(odoutil.CmdUsageTemplate) + + // Update the use / example / long to the Devfile description + linkCmd.Use = fmt.Sprintf("%s /", name) + linkCmd.Example = fmt.Sprintf(linkExampleExperimental, fullName) + linkCmd.Long = linkLongDescExperimental - // Modifications for the case when experimental mode is enabled - if experimental.IsExperimentalModeEnabled() { - linkCmd.Use = fmt.Sprintf("%s /", name) - linkCmd.Example = fmt.Sprintf(linkExampleExperimental, fullName) - linkCmd.Long = linkLongDescExperimental - } //Adding `--project` flag projectCmd.AddProjectFlag(linkCmd) - //Adding `--application` flag - if !experimental.IsExperimentalModeEnabled() { - appCmd.AddApplicationFlag(linkCmd) - } + + // Adding `--application` flag + // Should we just remove this if Devfile is default? I guess so! + /* + if !experimental.IsExperimentalModeEnabled() { + appCmd.AddApplicationFlag(linkCmd) + } + */ + //Adding `--component` flag AddComponentFlag(linkCmd) + //Adding context flag genericclioptions.AddContextFlag(linkCmd, &o.componentContext) diff --git a/pkg/odo/cli/component/list.go b/pkg/odo/cli/component/list.go index 28ad0402559..4332b87851b 100644 --- a/pkg/odo/cli/component/list.go +++ b/pkg/odo/cli/component/list.go @@ -27,7 +27,6 @@ import ( "github.com/openshift/odo/pkg/odo/genericclioptions" odoutil "github.com/openshift/odo/pkg/odo/util" "github.com/openshift/odo/pkg/odo/util/completion" - "github.com/openshift/odo/pkg/odo/util/experimental" ktemplates "k8s.io/kubectl/pkg/util/templates" ) @@ -51,7 +50,6 @@ type ListOptions struct { hasDCSupport bool hasDevfileComponents bool hasS2IComponents bool - isExperimentalMode bool devfilePath string *genericclioptions.Context } @@ -63,11 +61,10 @@ func NewListOptions() *ListOptions { // Complete completes log args func (lo *ListOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) { - lo.isExperimentalMode = experimental.IsExperimentalModeEnabled() + lo.devfilePath = filepath.Join(lo.componentContext, DevfilePath) - if lo.isExperimentalMode && util.CheckPathExists(lo.devfilePath) { - // Add a disclaimer that we are in *experimental mode* - log.Experimental("Experimental mode is enabled, use at your own risk") + + if util.CheckPathExists(lo.devfilePath) { lo.Context = genericclioptions.NewDevfileContext(cmd) lo.Client = genericclioptions.Client(cmd) @@ -113,7 +110,7 @@ func (lo *ListOptions) Validate() (err error) { klog.V(4).Infof("either --app and --all-apps both provided or provided --all-apps in a folder has app, use --all-apps anyway") } - if lo.isExperimentalMode { + if util.CheckPathExists(lo.devfilePath) { if lo.Context.Application == "" && lo.Context.KClient.Namespace == "" { return odoutil.ThrowContextError() } @@ -144,7 +141,7 @@ func (lo *ListOptions) Run() (err error) { if len(lo.pathFlag) != 0 { - if lo.isExperimentalMode && util.CheckPathExists(lo.devfilePath) { + if util.CheckPathExists(lo.devfilePath) { log.Experimental("--path flag is not supported for devfile components") } components, err := component.ListIfPathGiven(lo.Context.Client, filepath.SplitList(lo.pathFlag)) @@ -176,7 +173,7 @@ func (lo *ListOptions) Run() (err error) { // experimental workflow - if lo.isExperimentalMode && util.CheckPathExists(lo.devfilePath) { + if util.CheckPathExists(lo.devfilePath) { var deploymentList *appsv1.DeploymentList var err error diff --git a/pkg/odo/cli/component/log.go b/pkg/odo/cli/component/log.go index 57ac5c8afbc..71ac2bb05f4 100644 --- a/pkg/odo/cli/component/log.go +++ b/pkg/odo/cli/component/log.go @@ -11,7 +11,6 @@ import ( appCmd "github.com/openshift/odo/pkg/odo/cli/application" projectCmd "github.com/openshift/odo/pkg/odo/cli/project" "github.com/openshift/odo/pkg/odo/util/completion" - "github.com/openshift/odo/pkg/odo/util/experimental" ktemplates "k8s.io/kubectl/pkg/util/templates" odoutil "github.com/openshift/odo/pkg/odo/util" @@ -48,7 +47,7 @@ func (lo *LogOptions) Complete(name string, cmd *cobra.Command, args []string) ( lo.devfilePath = filepath.Join(lo.componentContext, lo.devfilePath) // if experimental mode is enabled and devfile is present - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(lo.devfilePath) { + if util.CheckPathExists(lo.devfilePath) { lo.ComponentOptions.Context = genericclioptions.NewDevfileContext(cmd) return nil } @@ -66,7 +65,7 @@ func (lo *LogOptions) Run() (err error) { stdout := os.Stdout // If experimental mode is enabled, use devfile push - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(lo.devfilePath) { + if util.CheckPathExists(lo.devfilePath) { err = lo.DevfileComponentLog() } else { // Retrieve the log diff --git a/pkg/odo/cli/component/push.go b/pkg/odo/cli/component/push.go index 82119dcf5d8..864689de7a4 100644 --- a/pkg/odo/cli/component/push.go +++ b/pkg/odo/cli/component/push.go @@ -15,7 +15,6 @@ import ( projectCmd "github.com/openshift/odo/pkg/odo/cli/project" "github.com/openshift/odo/pkg/odo/genericclioptions" "github.com/openshift/odo/pkg/odo/util/completion" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -85,7 +84,7 @@ func (po *PushOptions) Complete(name string, cmd *cobra.Command, args []string) po.CompleteDevfilePath() // if experimental mode is enabled and devfile is present - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(po.DevfilePath) { + if util.CheckPathExists(po.DevfilePath) { po.Devfile, err = devfile.ParseAndValidate(po.DevfilePath) if err != nil { @@ -176,7 +175,7 @@ func (po *PushOptions) Validate() (err error) { // If the experimental flag is set and devfile is present, then we do *not* validate // TODO: We need to clean this section up a bit.. We should also validate Devfile here // too. - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(po.DevfilePath) { + if util.CheckPathExists(po.DevfilePath) { return nil } @@ -208,13 +207,13 @@ func (po *PushOptions) Validate() (err error) { // Run has the logic to perform the required actions as part of command func (po *PushOptions) Run() (err error) { // If experimental mode is enabled, use devfile push - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(po.DevfilePath) { + if 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 @@ -224,14 +223,8 @@ func NewCmdPush(name, fullName string) *cobra.Command { annotations := map[string]string{"command": "component"} pushCmdExampleText := pushCmdExample - - if experimental.IsExperimentalModeEnabled() { - // The '-o json' option should only appear in help output when experimental mode is enabled. - annotations["machineoutput"] = "json" - - // The '-o json' example should likewise only appear in experimental only. - pushCmdExampleText += pushCmdExampleExperimentalOnly - } + annotations["machineoutput"] = "json" + pushCmdExampleText += pushCmdExampleExperimentalOnly var pushCmd = &cobra.Command{ Use: fmt.Sprintf("%s [component name]", name), @@ -252,15 +245,12 @@ func NewCmdPush(name, fullName string) *cobra.Command { pushCmd.Flags().BoolVar(&po.pushSource, "source", false, "Use source flag to only push latest source on to cluster") pushCmd.Flags().BoolVarP(&po.forceBuild, "force-build", "f", false, "Use force-build flag to force building the component") - // enable devfile flag if experimental mode is enabled - if experimental.IsExperimentalModeEnabled() { - pushCmd.Flags().StringVar(&po.namespace, "namespace", "", "Namespace to push the component to") - pushCmd.Flags().StringVar(&po.devfileInitCommand, "init-command", "", "Devfile Init Command to execute") - pushCmd.Flags().StringVar(&po.devfileBuildCommand, "build-command", "", "Devfile Build Command to execute") - pushCmd.Flags().StringVar(&po.devfileRunCommand, "run-command", "", "Devfile Run Command to execute") - pushCmd.Flags().BoolVar(&po.debugRun, "debug", false, "Runs the component in debug mode") - pushCmd.Flags().StringVar(&po.devfileDebugCommand, "debug-command", "", "Devfile Debug Command to execute") - } + pushCmd.Flags().StringVar(&po.namespace, "namespace", "", "Namespace to push the component to") + pushCmd.Flags().StringVar(&po.devfileInitCommand, "init-command", "", "Devfile Init Command to execute") + pushCmd.Flags().StringVar(&po.devfileBuildCommand, "build-command", "", "Devfile Build Command to execute") + pushCmd.Flags().StringVar(&po.devfileRunCommand, "run-command", "", "Devfile Run Command to execute") + pushCmd.Flags().BoolVar(&po.debugRun, "debug", false, "Runs the component in debug mode") + pushCmd.Flags().StringVar(&po.devfileDebugCommand, "debug-command", "", "Devfile Debug Command to execute") //Adding `--project` flag projectCmd.AddProjectFlag(pushCmd) diff --git a/pkg/odo/cli/component/update.go b/pkg/odo/cli/component/update.go index 9ec01a79b92..76c8d5a292d 100644 --- a/pkg/odo/cli/component/update.go +++ b/pkg/odo/cli/component/update.go @@ -18,7 +18,6 @@ import ( "github.com/openshift/odo/pkg/odo/genericclioptions" odoutil "github.com/openshift/odo/pkg/odo/util" "github.com/openshift/odo/pkg/odo/util/completion" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/util" ktemplates "k8s.io/kubectl/pkg/util/templates" @@ -78,10 +77,7 @@ func NewUpdateOptions() *UpdateOptions { func (uo *UpdateOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) { uo.devfilePath = filepath.Join(uo.componentContext, DevfilePath) - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(uo.devfilePath) { - // Add a disclaimer that we are in *experimental mode* - log.Experimental("Experimental mode is enabled, use at your own risk") - + if util.CheckPathExists(uo.devfilePath) { // Configure the devfile context uo.Context = genericclioptions.NewDevfileContext(cmd) return @@ -99,7 +95,7 @@ func (uo *UpdateOptions) Complete(name string, cmd *cobra.Command, args []string func (uo *UpdateOptions) Validate() (err error) { // if experimental mode is enabled and devfile is present - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(uo.devfilePath) { + if util.CheckPathExists(uo.devfilePath) { return nil } @@ -171,8 +167,8 @@ func (uo *UpdateOptions) Validate() (err error) { // Run has the logic to perform the required actions as part of command func (uo *UpdateOptions) Run() (err error) { - // if experimental mode is enabled and devfile is present - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(uo.devfilePath) { + // if devfile is present + if util.CheckPathExists(uo.devfilePath) { return errors.New(devfileErrorString) } diff --git a/pkg/odo/cli/component/watch.go b/pkg/odo/cli/component/watch.go index 90947ed4aca..007380f6769 100644 --- a/pkg/odo/cli/component/watch.go +++ b/pkg/odo/cli/component/watch.go @@ -2,11 +2,12 @@ package component import ( "fmt" - "github.com/openshift/odo/pkg/devfile/adapters/common" "os" "path/filepath" "strings" + "github.com/openshift/odo/pkg/devfile/adapters/common" + "github.com/openshift/odo/pkg/config" "github.com/openshift/odo/pkg/devfile" "github.com/openshift/odo/pkg/devfile/adapters" @@ -14,7 +15,6 @@ import ( "github.com/openshift/odo/pkg/occlient" appCmd "github.com/openshift/odo/pkg/odo/cli/application" projectCmd "github.com/openshift/odo/pkg/odo/cli/project" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/odo/util/pushtarget" "github.com/pkg/errors" ktemplates "k8s.io/kubectl/pkg/util/templates" @@ -40,10 +40,6 @@ var watchExampleWithDevfile = ktemplates.Examples(` # Watch for changes in dire %[1]s --build-command="mybuild" --run-command="myrun" `) -var watchExample = ktemplates.Examples(` # Watch for changes in directory for current component -%[1]s - `) - // WatchOptions contains attributes of the watch command type WatchOptions struct { ignores []string @@ -78,7 +74,7 @@ func (wo *WatchOptions) Complete(name string, cmd *cobra.Command, args []string) wo.devfilePath = filepath.Join(wo.componentContext, DevfilePath) // if experimental mode is enabled and devfile is present - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(wo.devfilePath) { + if util.CheckPathExists(wo.devfilePath) { wo.Context = genericclioptions.NewDevfileContext(cmd) // Set the source path to either the context or current working directory (if context not set) @@ -157,7 +153,7 @@ func (wo *WatchOptions) Validate() (err error) { } // if experimental mode is enabled and devfile is present, return. The rest of the validation is for non-devfile components - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(wo.devfilePath) { + if util.CheckPathExists(wo.devfilePath) { exists, err := wo.devfileHandler.DoesComponentExist(wo.componentName) if err != nil { return err @@ -199,7 +195,7 @@ func (wo *WatchOptions) Validate() (err error) { // Run has the logic to perform the required actions as part of command func (wo *WatchOptions) Run() (err error) { // if experimental mode is enabled and devfile is present - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(wo.devfilePath) { + if util.CheckPathExists(wo.devfilePath) { err = watch.DevfileWatchAndPush( os.Stdout, @@ -249,12 +245,10 @@ func (wo *WatchOptions) Run() (err error) { func NewCmdWatch(name, fullName string) *cobra.Command { wo := NewWatchOptions() - example := fmt.Sprintf(watchExample, fullName) usage := name - if experimental.IsExperimentalModeEnabled() { - example = fmt.Sprintf(watchExampleWithDevfile, fullName) - } + // Add information on Devfile + example := fmt.Sprintf(watchExampleWithDevfile, fullName) var watchCmd = &cobra.Command{ Use: usage, @@ -274,12 +268,9 @@ func NewCmdWatch(name, fullName string) *cobra.Command { watchCmd.SetUsageTemplate(odoutil.CmdUsageTemplate) - // enable devfile flag if experimental mode is enabled - if experimental.IsExperimentalModeEnabled() { - watchCmd.Flags().StringVar(&wo.devfileInitCommand, "init-command", "", "Devfile Init Command to execute") - watchCmd.Flags().StringVar(&wo.devfileBuildCommand, "build-command", "", "Devfile Build Command to execute") - watchCmd.Flags().StringVar(&wo.devfileRunCommand, "run-command", "", "Devfile Run Command to execute") - } + watchCmd.Flags().StringVar(&wo.devfileInitCommand, "init-command", "", "Devfile Init Command to execute") + watchCmd.Flags().StringVar(&wo.devfileBuildCommand, "build-command", "", "Devfile Build Command to execute") + watchCmd.Flags().StringVar(&wo.devfileRunCommand, "run-command", "", "Devfile Run Command to execute") // Adding context flag genericclioptions.AddContextFlag(watchCmd, &wo.componentContext) diff --git a/pkg/odo/cli/debug/info.go b/pkg/odo/cli/debug/info.go index ec4d89f5ec2..c35d46ad4a2 100644 --- a/pkg/odo/cli/debug/info.go +++ b/pkg/odo/cli/debug/info.go @@ -2,11 +2,13 @@ package debug import ( "fmt" + "path/filepath" + "github.com/openshift/odo/pkg/debug" "github.com/openshift/odo/pkg/log" "github.com/openshift/odo/pkg/machineoutput" + "github.com/openshift/odo/pkg/odo/cli/component" "github.com/openshift/odo/pkg/odo/genericclioptions" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/util" "github.com/spf13/cobra" k8sgenclioptions "k8s.io/cli-runtime/pkg/genericclioptions" @@ -20,8 +22,8 @@ type InfoOptions struct { Namespace string PortForwarder *debug.DefaultPortForwarder *genericclioptions.Context - contextDir string - DevfilePath string + componentContext string + devfilePath string } var ( @@ -46,7 +48,9 @@ func NewInfoOptions() *InfoOptions { // Complete completes all the required options for port-forward cmd. func (o *InfoOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) { - if experimental.IsExperimentalModeEnabled() && util.CheckPathExists(o.DevfilePath) { + o.devfilePath = filepath.Join(o.componentContext, component.DevfilePath) + + if util.CheckPathExists(o.devfilePath) { o.Context = genericclioptions.NewDevfileContext(cmd) // a small shortcut @@ -103,10 +107,7 @@ func NewCmdInfo(name, fullName string) *cobra.Command { genericclioptions.GenericRun(opts, cmd, args) }, } - genericclioptions.AddContextFlag(cmd, &opts.contextDir) - if experimental.IsExperimentalModeEnabled() { - cmd.Flags().StringVar(&opts.DevfilePath, "devfile", "./devfile.yaml", "Path to a devfile.yaml") - } + genericclioptions.AddContextFlag(cmd, &opts.componentContext) return cmd } diff --git a/pkg/odo/cli/debug/portforward.go b/pkg/odo/cli/debug/portforward.go index 0fa737754b9..0e53097bafc 100644 --- a/pkg/odo/cli/debug/portforward.go +++ b/pkg/odo/cli/debug/portforward.go @@ -5,14 +5,15 @@ import ( "net" "os" "os/signal" + "path/filepath" "strconv" "syscall" "github.com/openshift/odo/pkg/config" "github.com/openshift/odo/pkg/debug" "github.com/openshift/odo/pkg/log" + "github.com/openshift/odo/pkg/odo/cli/component" "github.com/openshift/odo/pkg/odo/genericclioptions" - "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/util" "github.com/spf13/cobra" @@ -30,8 +31,8 @@ type PortForwardOptions struct { // PortPair is the combination of local and remote port in the format "local:remote" PortPair string - localPort int - contextDir string + localPort int + componentContext string PortForwarder *debug.DefaultPortForwarder // StopChannel is used to stop port forwarding @@ -39,7 +40,7 @@ type PortForwardOptions struct { // ReadChannel is used to receive status of port forwarding ( ready or not ready ) ReadyChannel chan struct{} *genericclioptions.Context - DevfilePath string + devfilePath string isExperimental bool } @@ -71,12 +72,11 @@ func NewPortForwardOptions() *PortForwardOptions { // Complete completes all the required options for port-forward cmd. func (o *PortForwardOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) { + o.devfilePath = filepath.Join(o.componentContext, component.DevfilePath) var remotePort int - o.isExperimental = experimental.IsExperimentalModeEnabled() - - if o.isExperimental && util.CheckPathExists(o.DevfilePath) { + if util.CheckPathExists(o.devfilePath) { o.Context = genericclioptions.NewDevfileContext(cmd) // a small shortcut @@ -181,10 +181,7 @@ func NewCmdPortForward(name, fullName string) *cobra.Command { genericclioptions.GenericRun(opts, cmd, args) }, } - genericclioptions.AddContextFlag(cmd, &opts.contextDir) - if experimental.IsExperimentalModeEnabled() { - cmd.Flags().StringVar(&opts.DevfilePath, "devfile", "./devfile.yaml", "Path to a devfile.yaml") - } + genericclioptions.AddContextFlag(cmd, &opts.componentContext) cmd.Flags().IntVarP(&opts.localPort, "local-port", "l", config.DefaultDebugPort, "Set the local port") return cmd diff --git a/pkg/odo/cli/service/create.go b/pkg/odo/cli/service/create.go index 399c3ae6f6e..b6849c09a3e 100644 --- a/pkg/odo/cli/service/create.go +++ b/pkg/odo/cli/service/create.go @@ -6,10 +6,12 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "strings" "text/template" "github.com/openshift/odo/pkg/log" + "github.com/openshift/odo/pkg/odo/cli/component" "github.com/openshift/odo/pkg/odo/cli/service/ui" commonui "github.com/openshift/odo/pkg/odo/cli/ui" "github.com/openshift/odo/pkg/odo/genericclioptions" @@ -17,6 +19,7 @@ import ( "github.com/openshift/odo/pkg/odo/util/experimental" "github.com/openshift/odo/pkg/odo/util/validation" svc "github.com/openshift/odo/pkg/service" + "github.com/openshift/odo/pkg/util" "github.com/ghodss/yaml" "github.com/pkg/errors" @@ -107,6 +110,9 @@ type ServiceCreateOptions struct { // Location of the file in which yaml specification of CR is stored. // TODO: remove this after service create's interactive mode supports creating operator backed services fromFile string + + // Devfile + devfilePath string } // NewServiceCreateOptions creates a new ServiceCreateOptions instance @@ -128,11 +134,13 @@ func NewDynamicCRD() *DynamicCRD { // Complete completes ServiceCreateOptions after they've been created func (o *ServiceCreateOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) { + o.devfilePath = filepath.Join(o.componentContext, component.DevfilePath) + if len(args) == 0 || !cmd.HasFlags() { o.interactive = true } - if experimental.IsExperimentalModeEnabled() { + if util.CheckPathExists(o.devfilePath) { o.Context = genericclioptions.NewDevfileContext(cmd) } else if o.componentContext != "" { o.Context = genericclioptions.NewContext(cmd) @@ -144,8 +152,7 @@ func (o *ServiceCreateOptions) Complete(name string, cmd *cobra.Command, args [] var class scv1beta1.ClusterServiceClass - if experimental.IsExperimentalModeEnabled() { - // we don't support interactive mode for Operator Hub yet + if util.CheckPathExists(o.devfilePath) { o.interactive = false // if user has just used "odo service create", simply return @@ -209,7 +216,7 @@ func (o *ServiceCreateOptions) Complete(name string, cmd *cobra.Command, args [] } // if only one arg is given, then it is considered as service name and service type both // ONLY if not running in Experimental mode - if !experimental.IsExperimentalModeEnabled() { + if !util.CheckPathExists(o.devfilePath) { // This is because an operator with name // "etcdoperator.v0.9.4-clusterwide" would lead to creation of a // serice with name like @@ -277,7 +284,7 @@ func (o *ServiceCreateOptions) Validate() (err error) { } // we want to find an Operator only if something's passed to the crd flag on CLI - if experimental.IsExperimentalModeEnabled() { + if util.CheckPathExists(o.devfilePath) { d := NewDynamicCRD() // if the user wants to create service from a file, we check for // existence of file and validate if the requested operator and CR @@ -430,7 +437,7 @@ func (o *ServiceCreateOptions) Validate() (err error) { // Run contains the logic for the odo service create command func (o *ServiceCreateOptions) Run() (err error) { s := &log.Status{} - if experimental.IsExperimentalModeEnabled() { + if util.CheckPathExists(o.devfilePath) { // in case of an opertor backed service, name of the service is // provided by the yaml specification in alm-examples. It might also // happen that a user spins up Service Catalog based service in @@ -445,7 +452,7 @@ func (o *ServiceCreateOptions) Run() (err error) { log.Infof("Deploying service %s of type: %s", o.ServiceName, o.ServiceType) } - if experimental.IsExperimentalModeEnabled() && o.CustomResource != "" { + if util.CheckPathExists(o.devfilePath) && o.CustomResource != "" { // if experimental mode is enabled and o.CustomResource is not empty, we're expected to create an Operator backed service if o.DryRun { // if it's dry run, only print the alm-example (o.CustomResourceDefinition) and exit @@ -512,15 +519,13 @@ func NewCmdServiceCreate(name, fullName string) *cobra.Command { }, } - if experimental.IsExperimentalModeEnabled() { - serviceCreateCmd.Use += fmt.Sprintf(" [flags]\n %s / [service_name] [flags]", o.CmdFullName) - serviceCreateCmd.Short = createShortDescExperimental - serviceCreateCmd.Long = createLongDescExperimental - serviceCreateCmd.Example += fmt.Sprintf("\n\n") + fmt.Sprintf(createOperatorExample, fullName) - serviceCreateCmd.Flags().BoolVar(&o.DryRun, "dry-run", false, "Print the yaml specificiation that will be used to create the service") - // remove this feature after enabling service create interactive mode for operator backed services - serviceCreateCmd.Flags().StringVar(&o.fromFile, "from-file", "", "Path to the file containing yaml specification to use to start operator backed service") - } + serviceCreateCmd.Use += fmt.Sprintf(" [flags]\n %s / [service_name] [flags]", o.CmdFullName) + serviceCreateCmd.Short = createShortDescExperimental + serviceCreateCmd.Long = createLongDescExperimental + serviceCreateCmd.Example += fmt.Sprintf("\n\n") + fmt.Sprintf(createOperatorExample, fullName) + serviceCreateCmd.Flags().BoolVar(&o.DryRun, "dry-run", false, "Print the yaml specificiation that will be used to create the service") + // remove this feature after enabling service create interactive mode for operator backed services + serviceCreateCmd.Flags().StringVar(&o.fromFile, "from-file", "", "Path to the file containing yaml specification to use to start operator backed service") serviceCreateCmd.Flags().StringVar(&o.Plan, "plan", "", "The name of the plan of the service to be created") serviceCreateCmd.Flags().StringArrayVarP(&o.parameters, "parameters", "p", []string{}, "Parameters of the plan where a parameter is expressed as = 0 { output = output[:len(output)-1] diff --git a/pkg/sync/adapter.go b/pkg/sync/adapter.go index 0be5efea74c..e584f9109f8 100644 --- a/pkg/sync/adapter.go +++ b/pkg/sync/adapter.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/openshift/odo/pkg/devfile/adapters/common" - versionsCommon "github.com/openshift/odo/pkg/devfile/parser/data/common" "github.com/openshift/odo/pkg/exec" "github.com/openshift/odo/pkg/kclient" "github.com/openshift/odo/pkg/log" @@ -157,17 +156,7 @@ func (a Adapter) pushLocal(path string, files []string, delFiles []string, isFor s := log.Spinner("Syncing files to the component") defer s.End(false) - // Determine which folder we need to sync to - var syncFolder string - if compInfo.SourceMount != kclient.OdoSourceVolumeMount { - syncFolder = compInfo.SourceMount - } else { - // If there's only one project defined in the devfile, sync to `/projects/project-name`, otherwise sync to /projects - syncFolder, err = getSyncFolder(a.Devfile.Data.GetProjects()) - if err != nil { - return errors.Wrapf(err, "unable to determine sync folder") - } - } + syncFolder := compInfo.SourceMount if syncFolder != kclient.OdoSourceVolumeMount { // Need to make sure the folder already exists on the component or else sync will fail @@ -210,31 +199,6 @@ func (a Adapter) pushLocal(path string, files []string, delFiles []string, isFor return nil } -// getSyncFolder returns the folder that we need to sync the source files to -// If there's exactly one project defined in the devfile, and clonePath isn't set return `/projects/` -// If there's exactly one project, and clonePath is set, return `/projects/` -// If the clonePath is an absolute path or contains '..', return an error -// Otherwise (zero projects or many), return `/projects` -func getSyncFolder(projects []versionsCommon.DevfileProject) (string, error) { - if len(projects) == 1 { - project := projects[0] - // If the clonepath is set to a value, set it to be the sync folder - // As some devfiles rely on the code being synced to the folder in the clonepath - if project.ClonePath != "" { - if strings.HasPrefix(project.ClonePath, "/") { - return "", fmt.Errorf("the clonePath in the devfile must be a relative path") - } - if strings.Contains(project.ClonePath, "..") { - return "", fmt.Errorf("the clonePath in the devfile cannot escape the projects root. Don't use .. to try and do that") - } - return filepath.ToSlash(filepath.Join(kclient.OdoSourceVolumeMount, project.ClonePath)), nil - } - return filepath.ToSlash(filepath.Join(kclient.OdoSourceVolumeMount, projects[0].Name)), nil - } - return kclient.OdoSourceVolumeMount, nil - -} - // getCmdToCreateSyncFolder returns the command used to create the remote sync folder on the running container func getCmdToCreateSyncFolder(syncFolder string) []string { return []string{"mkdir", "-p", syncFolder} diff --git a/pkg/sync/adapter_test.go b/pkg/sync/adapter_test.go index b4813ce3641..5b77315b2d0 100644 --- a/pkg/sync/adapter_test.go +++ b/pkg/sync/adapter_test.go @@ -17,132 +17,6 @@ import ( "github.com/openshift/odo/tests/helper" ) -func TestGetSyncFolder(t *testing.T) { - projectNames := []string{"some-name", "another-name"} - projectRepos := []string{"https://github.com/some/repo.git", "https://github.com/another/repo.git"} - projectClonePath := "src/github.com/golang/example/" - invalidClonePaths := []string{"/var", "../var", "pkg/../../var"} - - tests := []struct { - name string - projects []versionsCommon.DevfileProject - want string - wantErr bool - }{ - { - name: "Case 1: No projects", - projects: []versionsCommon.DevfileProject{}, - want: kclient.OdoSourceVolumeMount, - wantErr: false, - }, - { - name: "Case 2: One project", - projects: []versionsCommon.DevfileProject{ - { - Name: projectNames[0], - Git: &versionsCommon.Git{ - Location: projectRepos[0], - }, - }, - }, - want: filepath.ToSlash(filepath.Join(kclient.OdoSourceVolumeMount, projectNames[0])), - wantErr: false, - }, - { - name: "Case 3: Multiple projects", - projects: []versionsCommon.DevfileProject{ - { - Name: projectNames[0], - Git: &versionsCommon.Git{ - Location: projectRepos[0], - }, - }, - { - Name: projectNames[1], - Github: &versionsCommon.Github{ - Location: projectRepos[1], - }, - }, - { - Name: projectNames[1], - Zip: &versionsCommon.Zip{ - Location: projectRepos[1], - }, - }, - }, - want: kclient.OdoSourceVolumeMount, - wantErr: false, - }, - { - name: "Case 4: Clone path set", - projects: []versionsCommon.DevfileProject{ - { - ClonePath: projectClonePath, - Name: projectNames[0], - Zip: &versionsCommon.Zip{ - Location: projectRepos[0], - }, - }, - }, - want: filepath.ToSlash(filepath.Join(kclient.OdoSourceVolumeMount, projectClonePath)), - wantErr: false, - }, - { - name: "Case 5: Invalid clone path, set with absolute path", - projects: []versionsCommon.DevfileProject{ - { - ClonePath: invalidClonePaths[0], - Name: projectNames[0], - Github: &versionsCommon.Github{ - Location: projectRepos[0], - }, - }, - }, - want: "", - wantErr: true, - }, - { - name: "Case 6: Invalid clone path, starts with ..", - projects: []versionsCommon.DevfileProject{ - { - ClonePath: invalidClonePaths[1], - Name: projectNames[0], - Git: &versionsCommon.Git{ - Location: projectRepos[0], - }, - }, - }, - want: "", - wantErr: true, - }, - { - name: "Case 7: Invalid clone path, contains ..", - projects: []versionsCommon.DevfileProject{ - { - ClonePath: invalidClonePaths[2], - Name: projectNames[0], - Zip: &versionsCommon.Zip{ - Location: projectRepos[0], - }, - }, - }, - want: "", - wantErr: true, - }, - } - for _, tt := range tests { - syncFolder, err := getSyncFolder(tt.projects) - - if !tt.wantErr == (err != nil) { - t.Errorf("expected %v, actual %v", tt.wantErr, err) - } - - if syncFolder != tt.want { - t.Errorf("expected %s, actual %s", tt.want, syncFolder) - } - } -} - func TestGetCmdToCreateSyncFolder(t *testing.T) { tests := []struct { name string diff --git a/pkg/url/url.go b/pkg/url/url.go index fee72e1ce86..29301d72861 100644 --- a/pkg/url/url.go +++ b/pkg/url/url.go @@ -242,7 +242,7 @@ type CreateParameters struct { // Create creates a URL and returns url string and error if any // portNumber is the target port number for the route and is -1 in case no port number is specified in which case it is automatically detected for components which expose only one service port) -func Create(client *occlient.Client, kClient *kclient.Client, parameters CreateParameters, isRouteSupported bool, isExperimental bool) (string, error) { +func Create(client *occlient.Client, kClient *kclient.Client, parameters CreateParameters, isRouteSupported bool, isS2I bool) (string, error) { if parameters.urlKind != envinfo.INGRESS && parameters.urlKind != envinfo.ROUTE { return "", fmt.Errorf("urlKind %s is not supported for URL creation", parameters.urlKind) @@ -256,7 +256,7 @@ func Create(client *occlient.Client, kClient *kclient.Client, parameters CreateP serviceName := "" - if isExperimental && parameters.urlKind == envinfo.INGRESS && kClient != nil { + if !isS2I && parameters.urlKind == envinfo.INGRESS && kClient != nil { if parameters.host == "" { return "", errors.Errorf("the host cannot be empty") } @@ -318,14 +318,14 @@ func Create(client *occlient.Client, kClient *kclient.Client, parameters CreateP if err != nil { return "", errors.Wrap(err, "unable to create ingress") } - return GetURLString(GetProtocol(routev1.Route{}, *ingress), "", ingressDomain, isExperimental), nil + return GetURLString(GetProtocol(routev1.Route{}, *ingress), "", ingressDomain, false), nil } else { if !isRouteSupported { return "", errors.Errorf("routes are not available on non OpenShift clusters") } var ownerReference metav1.OwnerReference - if !isExperimental || kClient == nil { + if isS2I || kClient == nil { var err error parameters.urlName, err = util.NamespaceOpenShiftObject(parameters.urlName, parameters.applicationName) if err != nil { @@ -360,7 +360,7 @@ func Create(client *occlient.Client, kClient *kclient.Client, parameters CreateP if err != nil { return "", errors.Wrap(err, "unable to create route") } - return GetURLString(GetProtocol(*route, iextensionsv1.Ingress{}), route.Spec.Host, "", isExperimental), nil + return GetURLString(GetProtocol(*route, iextensionsv1.Ingress{}), route.Spec.Host, "", true), nil } } @@ -684,8 +684,8 @@ func ConvertEnvinfoURL(envinfoURL envinfo.EnvInfoURL, serviceName string) URL { } // GetURLString returns a string representation of given url -func GetURLString(protocol, URL string, ingressDomain string, isExperimentalMode bool) string { - if isExperimentalMode && URL == "" { +func GetURLString(protocol, URL string, ingressDomain string, isS2I bool) string { + if !isS2I && URL == "" { return protocol + "://" + ingressDomain } return protocol + "://" + URL @@ -862,12 +862,12 @@ func getMachineReadableFormatDocker(internalPort int, externalPort int, hostIP s } type PushParameters struct { - ComponentName string - ApplicationName string - ConfigURLs []config.ConfigURL - EnvURLS []envinfo.EnvInfoURL - IsRouteSupported bool - IsExperimentalModeEnabled bool + ComponentName string + ApplicationName string + ConfigURLs []config.ConfigURL + EnvURLS []envinfo.EnvInfoURL + IsRouteSupported bool + IsS2I bool } // Push creates and deletes the required URLs @@ -876,7 +876,7 @@ func Push(client *occlient.Client, kClient *kclient.Client, parameters PushParam // in case the component is a s2i one // kClient will be nil - if parameters.IsExperimentalModeEnabled && kClient != nil { + if !parameters.IsS2I && kClient != nil { urls := parameters.EnvURLS for _, url := range urls { if url.Kind != envinfo.DOCKER { @@ -905,7 +905,7 @@ func Push(client *occlient.Client, kClient *kclient.Client, parameters PushParam } urlCLUSTER := make(map[string]URL) - if parameters.IsExperimentalModeEnabled && kClient != nil { + if !parameters.IsS2I && kClient != nil { urlList, err := ListPushedIngress(kClient, parameters.ComponentName) if err != nil { return err @@ -992,7 +992,7 @@ func Push(client *occlient.Client, kClient *kclient.Client, parameters PushParam secretName: urlInfo.Spec.TLSSecret, urlKind: urlInfo.Spec.Kind, } - host, err := Create(client, kClient, createParameters, parameters.IsRouteSupported, parameters.IsExperimentalModeEnabled) + host, err := Create(client, kClient, createParameters, parameters.IsRouteSupported, parameters.IsS2I) if err != nil { return err } diff --git a/pkg/url/url_test.go b/pkg/url/url_test.go index 59cfc2f7b47..8b627147ce6 100644 --- a/pkg/url/url_test.go +++ b/pkg/url/url_test.go @@ -35,16 +35,16 @@ import ( func TestCreate(t *testing.T) { type args struct { - componentName string - applicationName string - urlName string - portNumber int - secure bool - host string - urlKind envinfo.URLKind - isRouteSupported bool - isExperimentalModeEnabled bool - tlsSecret string + componentName string + applicationName string + urlName string + portNumber int + secure bool + host string + urlKind envinfo.URLKind + isRouteSupported bool + isS2I bool + tlsSecret string } tests := []struct { name string @@ -64,6 +64,7 @@ func TestCreate(t *testing.T) { urlName: "nodejs", portNumber: 8080, isRouteSupported: true, + isS2I: true, urlKind: envinfo.ROUTE, }, returnedRoute: &routev1.Route{ @@ -99,6 +100,7 @@ func TestCreate(t *testing.T) { urlName: "example-url", portNumber: 9100, isRouteSupported: true, + isS2I: true, urlKind: envinfo.ROUTE, }, returnedRoute: &routev1.Route{ @@ -135,6 +137,7 @@ func TestCreate(t *testing.T) { portNumber: 9100, secure: true, isRouteSupported: true, + isS2I: true, urlKind: envinfo.ROUTE, }, returnedRoute: &routev1.Route{ @@ -170,13 +173,12 @@ func TestCreate(t *testing.T) { { name: "Case 4: Create a ingress, with same name as component,instead of route on openshift cluster", args: args{ - componentName: "nodejs", - urlName: "nodejs", - portNumber: 8080, - host: "com", - isRouteSupported: true, - isExperimentalModeEnabled: true, - urlKind: envinfo.INGRESS, + componentName: "nodejs", + urlName: "nodejs", + portNumber: 8080, + host: "com", + isRouteSupported: true, + urlKind: envinfo.INGRESS, }, returnedIngress: fake.GetSingleIngress("nodejs", "nodejs"), want: "http://nodejs.com", @@ -185,13 +187,12 @@ func TestCreate(t *testing.T) { { name: "Case 5: Create a ingress, with different name as component,instead of route on openshift cluster", args: args{ - componentName: "nodejs", - urlName: "example", - portNumber: 8080, - host: "com", - isRouteSupported: true, - isExperimentalModeEnabled: true, - urlKind: envinfo.INGRESS, + componentName: "nodejs", + urlName: "example", + portNumber: 8080, + host: "com", + isRouteSupported: true, + urlKind: envinfo.INGRESS, }, returnedRoute: &routev1.Route{ ObjectMeta: metav1.ObjectMeta{ @@ -222,14 +223,13 @@ func TestCreate(t *testing.T) { { name: "Case 6: Create a secure ingress, instead of route on openshift cluster, default tls exists", args: args{ - componentName: "nodejs", - urlName: "example", - portNumber: 8080, - host: "com", - isRouteSupported: true, - isExperimentalModeEnabled: true, - secure: true, - urlKind: envinfo.INGRESS, + componentName: "nodejs", + urlName: "example", + portNumber: 8080, + host: "com", + isRouteSupported: true, + secure: true, + urlKind: envinfo.INGRESS, }, returnedIngress: fake.GetSingleIngress("example", "nodejs"), defaultTLSExists: true, @@ -239,14 +239,13 @@ func TestCreate(t *testing.T) { { name: "Case 7: Create a secure ingress, instead of route on openshift cluster and default tls doesn't exist", args: args{ - componentName: "nodejs", - urlName: "example", - portNumber: 8080, - host: "com", - isRouteSupported: true, - isExperimentalModeEnabled: true, - secure: true, - urlKind: envinfo.INGRESS, + componentName: "nodejs", + urlName: "example", + portNumber: 8080, + host: "com", + isRouteSupported: true, + secure: true, + urlKind: envinfo.INGRESS, }, returnedIngress: fake.GetSingleIngress("example", "nodejs"), defaultTLSExists: false, @@ -256,15 +255,14 @@ func TestCreate(t *testing.T) { { name: "Case 8: Fail when while creating ingress when user given tls secret doesn't exists", args: args{ - componentName: "nodejs", - urlName: "example", - portNumber: 8080, - host: "com", - isRouteSupported: true, - isExperimentalModeEnabled: true, - secure: true, - tlsSecret: "user-secret", - urlKind: envinfo.INGRESS, + componentName: "nodejs", + urlName: "example", + portNumber: 8080, + host: "com", + isRouteSupported: true, + secure: true, + tlsSecret: "user-secret", + urlKind: envinfo.INGRESS, }, returnedIngress: fake.GetSingleIngress("example", "nodejs"), defaultTLSExists: false, @@ -275,15 +273,14 @@ func TestCreate(t *testing.T) { { name: "Case 9: Create a secure ingress, instead of route on openshift cluster, user tls secret does exists", args: args{ - componentName: "nodejs", - urlName: "example", - portNumber: 8080, - host: "com", - isRouteSupported: true, - isExperimentalModeEnabled: true, - secure: true, - tlsSecret: "user-secret", - urlKind: envinfo.INGRESS, + componentName: "nodejs", + urlName: "example", + portNumber: 8080, + host: "com", + isRouteSupported: true, + secure: true, + tlsSecret: "user-secret", + urlKind: envinfo.INGRESS, }, returnedIngress: fake.GetSingleIngress("example", "nodejs"), defaultTLSExists: false, @@ -295,15 +292,14 @@ func TestCreate(t *testing.T) { { name: "Case 10: invalid url kind", args: args{ - componentName: "nodejs", - urlName: "example", - portNumber: 8080, - host: "com", - isRouteSupported: true, - isExperimentalModeEnabled: true, - secure: true, - tlsSecret: "user-secret", - urlKind: "blah", + componentName: "nodejs", + urlName: "example", + portNumber: 8080, + host: "com", + isRouteSupported: true, + secure: true, + tlsSecret: "user-secret", + urlKind: "blah", }, returnedIngress: fake.GetSingleIngress("example", "nodejs"), defaultTLSExists: false, @@ -314,12 +310,11 @@ func TestCreate(t *testing.T) { { name: "Case 11: route is not supported on the cluster", args: args{ - componentName: "nodejs", - applicationName: "app", - urlName: "example", - isRouteSupported: false, - isExperimentalModeEnabled: true, - urlKind: envinfo.ROUTE, + componentName: "nodejs", + applicationName: "app", + urlName: "example", + isRouteSupported: false, + urlKind: envinfo.ROUTE, }, returnedIngress: fake.GetSingleIngress("example", "nodejs"), defaultTLSExists: false, @@ -330,13 +325,12 @@ func TestCreate(t *testing.T) { { name: "Case 11: secretName used without secure flag", args: args{ - componentName: "nodejs", - applicationName: "app", - urlName: "example", - isRouteSupported: false, - isExperimentalModeEnabled: true, - tlsSecret: "secret", - urlKind: envinfo.ROUTE, + componentName: "nodejs", + applicationName: "app", + urlName: "example", + isRouteSupported: false, + tlsSecret: "secret", + urlKind: envinfo.ROUTE, }, returnedIngress: fake.GetSingleIngress("example", "nodejs"), defaultTLSExists: false, @@ -412,7 +406,7 @@ func TestCreate(t *testing.T) { urlKind: tt.args.urlKind, } - got, err := Create(client, fakeKClient, urlCreateParameters, tt.args.isRouteSupported, tt.args.isExperimentalModeEnabled) + got, err := Create(client, fakeKClient, urlCreateParameters, tt.args.isRouteSupported, tt.args.isS2I) if err == nil && !tt.wantErr { if tt.args.urlKind == envinfo.INGRESS { @@ -741,8 +735,8 @@ func TestGetValidPortNumber(t *testing.T) { func TestPush(t *testing.T) { type args struct { - isRouteSupported bool - isExperimentalModeEnabled bool + isRouteSupported bool + isS2I bool } tests := []struct { name string @@ -761,6 +755,7 @@ func TestPush(t *testing.T) { name: "no urls on local config and cluster", args: args{ isRouteSupported: true, + isS2I: true, }, componentName: "nodejs", applicationName: "app", @@ -770,7 +765,10 @@ func TestPush(t *testing.T) { name: "2 urls on local config and 0 on openshift cluster", componentName: "nodejs", applicationName: "app", - args: args{isRouteSupported: true}, + args: args{ + isRouteSupported: true, + isS2I: true, + }, existingConfigURLs: []config.ConfigURL{ { Name: "example", @@ -811,7 +809,7 @@ func TestPush(t *testing.T) { name: "0 url on local config and 2 on openshift cluster", componentName: "wildfly", applicationName: "app", - args: args{isRouteSupported: true}, + args: args{isRouteSupported: true, isS2I: true}, returnedRoutes: testingutil.GetRouteListWithMultiple("wildfly", "app"), deletedURLs: []URL{ getMachineReadableFormat(testingutil.GetSingleRoute("example-app", 8080, "nodejs", "app")), @@ -822,7 +820,7 @@ func TestPush(t *testing.T) { name: "2 url on local config and 2 on openshift cluster, but they are different", componentName: "nodejs", applicationName: "app", - args: args{isRouteSupported: true}, + args: args{isRouteSupported: true, isS2I: true}, existingConfigURLs: []config.ConfigURL{ { Name: "example-local-0", @@ -867,7 +865,7 @@ func TestPush(t *testing.T) { name: "2 url on local config and openshift cluster are in sync", componentName: "nodejs", applicationName: "app", - args: args{isRouteSupported: true}, + args: args{isRouteSupported: true, isS2I: true}, existingConfigURLs: []config.ConfigURL{ { Name: "example", @@ -888,7 +886,7 @@ func TestPush(t *testing.T) { { name: "0 urls on env file and cluster", componentName: "nodejs", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{}, returnedRoutes: &routev1.RouteList{}, returnedIngress: &extensionsv1.IngressList{}, @@ -896,7 +894,7 @@ func TestPush(t *testing.T) { { name: "2 urls on env file and 0 on openshift cluster", componentName: "nodejs", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example", @@ -943,7 +941,7 @@ func TestPush(t *testing.T) { { name: "0 urls on env file and 2 on openshift cluster", componentName: "nodejs", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{}, returnedRoutes: &routev1.RouteList{}, returnedIngress: fake.GetIngressListWithMultiple("nodejs"), @@ -963,7 +961,7 @@ func TestPush(t *testing.T) { { name: "2 urls on env file and 2 on openshift cluster, but they are different", componentName: "wildfly", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example-local-0", @@ -1022,7 +1020,7 @@ func TestPush(t *testing.T) { { name: "2 urls on env file and openshift cluster are in sync", componentName: "wildfly", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example-0", @@ -1047,7 +1045,7 @@ func TestPush(t *testing.T) { { name: "2 (1 ingress,1 route) urls on env file and 2 on openshift cluster (1 ingress,1 route), but they are different", componentName: "nodejs", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example-local-0", @@ -1104,7 +1102,7 @@ func TestPush(t *testing.T) { { name: "create a ingress on a kubernetes cluster", componentName: "nodejs", - args: args{isRouteSupported: false, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: false}, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example", @@ -1136,7 +1134,9 @@ func TestPush(t *testing.T) { { name: "url with same name exists on env and cluster but with different specs", componentName: "nodejs", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{ + isRouteSupported: true, + }, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example-local-0", @@ -1176,7 +1176,7 @@ func TestPush(t *testing.T) { name: "url with same name exists on config and cluster but with different specs", componentName: "nodejs", applicationName: "app", - args: args{isRouteSupported: true, isExperimentalModeEnabled: false}, + args: args{isRouteSupported: true, isS2I: true}, existingConfigURLs: []config.ConfigURL{ { Name: "example-local-0", @@ -1215,7 +1215,7 @@ func TestPush(t *testing.T) { { name: "create a secure route url", componentName: "nodejs", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example", @@ -1242,7 +1242,7 @@ func TestPush(t *testing.T) { { name: "create a secure ingress url with empty user given tls secret", componentName: "nodejs", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example", @@ -1271,7 +1271,7 @@ func TestPush(t *testing.T) { { name: "create a secure ingress url with user given tls secret", componentName: "nodejs", - args: args{isRouteSupported: true, isExperimentalModeEnabled: true}, + args: args{isRouteSupported: true}, existingEnvInfoURLs: []envinfo.EnvInfoURL{ { Name: "example", @@ -1338,12 +1338,12 @@ func TestPush(t *testing.T) { }) if err := Push(fakeClient, fakeKClient, PushParameters{ - ComponentName: tt.componentName, - ApplicationName: tt.applicationName, - ConfigURLs: tt.existingConfigURLs, - EnvURLS: tt.existingEnvInfoURLs, - IsRouteSupported: tt.args.isRouteSupported, - IsExperimentalModeEnabled: tt.args.isExperimentalModeEnabled, + ComponentName: tt.componentName, + ApplicationName: tt.applicationName, + ConfigURLs: tt.existingConfigURLs, + EnvURLS: tt.existingEnvInfoURLs, + IsRouteSupported: tt.args.isRouteSupported, + IsS2I: tt.args.isS2I, }); (err != nil) != tt.wantErr { t.Errorf("Push() error = %v, wantErr %v", err, tt.wantErr) } else { diff --git a/pkg/util/util.go b/pkg/util/util.go index 7ea1009c583..a162fa56ee6 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -40,8 +40,8 @@ import ( // HTTPRequestTimeout configures timeout of all HTTP requests const ( - HTTPRequestTimeout = 20 * time.Second // HTTPRequestTimeout configures timeout of all HTTP requests - ResponseHeaderTimeout = 10 * time.Second // ResponseHeaderTimeout is the timeout to retrieve the server's response headers + HTTPRequestTimeout = 30 * time.Second // HTTPRequestTimeout configures timeout of all HTTP requests + ResponseHeaderTimeout = 30 * time.Second // ResponseHeaderTimeout is the timeout to retrieve the server's response headers ModeReadWriteFile = 0600 // default Permission for a file CredentialPrefix = "odo-" // CredentialPrefix is the prefix of the credential that uses to access secure registry ) diff --git a/tests/examples/source/devfiles/nodejs/devfile-no-endpoints.yaml b/tests/examples/source/devfiles/nodejs/devfile-no-endpoints.yaml index 7c880570e9e..1dfd9b4c885 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-no-endpoints.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-no-endpoints.yaml @@ -16,7 +16,7 @@ commands: id: devbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -24,7 +24,7 @@ commands: id: devrun component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true \ No newline at end of file diff --git a/tests/examples/source/devfiles/nodejs/devfile-with-debugrun.yaml b/tests/examples/source/devfiles/nodejs/devfile-with-debugrun.yaml index 81536370024..1079d4339d5 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-with-debugrun.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-with-debugrun.yaml @@ -19,7 +19,7 @@ commands: id: devbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -27,7 +27,7 @@ commands: id: devrun component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true @@ -35,7 +35,7 @@ commands: id: debugrun component: runtime commandLine: npm run debug - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: debug isDefault: true diff --git a/tests/examples/source/devfiles/nodejs/devfile-with-multiple-defaults.yaml b/tests/examples/source/devfiles/nodejs/devfile-with-multiple-defaults.yaml index d786c6f1471..32baf07d27e 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-with-multiple-defaults.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-with-multiple-defaults.yaml @@ -19,7 +19,7 @@ commands: id: firstbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -27,7 +27,7 @@ commands: id: secondbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -35,19 +35,19 @@ commands: id: thirdbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} - exec: id: firstrun component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run - exec: id: secondrun component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run - exec: @@ -57,7 +57,7 @@ commands: env: - name: ENV1 value: "test_env_variable" - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run - exec: @@ -69,7 +69,7 @@ commands: value: "test_env_variable1" - name: ENV2 value: "test_env_variable2" - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run - exec: @@ -79,14 +79,14 @@ commands: env: - name: ENV1 value: "env with space" - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run - exec: id: test1 component: runtime commandLine: "mkdir test1" - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: test isDefault: true @@ -94,7 +94,7 @@ commands: id: test2 component: runtime commandLine: "mkdir test2" - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: test isDefault: true diff --git a/tests/examples/source/devfiles/nodejs/devfile-with-parent.yaml b/tests/examples/source/devfiles/nodejs/devfile-with-parent.yaml index 40aad2423a3..4cc1289f879 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-with-parent.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-with-parent.yaml @@ -9,7 +9,7 @@ commands: id: devBuild component: runtime commandLine: touch blah.js - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false \ No newline at end of file diff --git a/tests/examples/source/devfiles/nodejs/devfile-with-post-start.yaml b/tests/examples/source/devfiles/nodejs/devfile-with-post-start.yaml index b6198ae4297..682af4d3af4 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-with-post-start.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-with-post-start.yaml @@ -34,7 +34,7 @@ commands: id: devbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -42,14 +42,14 @@ commands: id: build component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build - exec: id: devrun component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true @@ -57,7 +57,7 @@ commands: id: run component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run events: diff --git a/tests/examples/source/devfiles/nodejs/devfile-with-restart.yaml b/tests/examples/source/devfiles/nodejs/devfile-with-restart.yaml index 0df9e1c9d8b..131a19d27ed 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-with-restart.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-with-restart.yaml @@ -16,7 +16,7 @@ commands: id: devbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -26,7 +26,7 @@ commands: restart: "false" component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true \ No newline at end of file diff --git a/tests/examples/source/devfiles/nodejs/devfile-with-sparseCheckoutDir.yaml b/tests/examples/source/devfiles/nodejs/devfile-with-sparseCheckoutDir.yaml index 58ec345ff75..f470499c869 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-with-sparseCheckoutDir.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-with-sparseCheckoutDir.yaml @@ -26,22 +26,22 @@ commands: - type: exec component: runtime command: npm install - workdir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workdir: ${PROJECTS_ROOT} - name: devbuild actions: - type: exec component: runtime command: npm install - workdir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workdir: ${PROJECTS_ROOT} - name: run actions: - type: exec component: runtime command: npm start - workdir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workdir: ${PROJECTS_ROOT} - name: devrun actions: - type: exec component: runtime command: npm start - workdir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workdir: ${PROJECTS_ROOT} diff --git a/tests/examples/source/devfiles/nodejs/devfile-with-testgroup.yaml b/tests/examples/source/devfiles/nodejs/devfile-with-testgroup.yaml index 0e9bc688f37..5683e6b46e9 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-with-testgroup.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-with-testgroup.yaml @@ -16,7 +16,7 @@ commands: id: devbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -24,7 +24,7 @@ commands: id: devrun component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true @@ -32,7 +32,7 @@ commands: id: test1 component: runtime commandLine: "mkdir test1" - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: test isDefault: true @@ -40,7 +40,7 @@ commands: id: test2 component: runtime commandLine: "mkdir test2" - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: test - composite: diff --git a/tests/examples/source/devfiles/nodejs/devfile-without-devbuild.yaml b/tests/examples/source/devfiles/nodejs/devfile-without-devbuild.yaml index 0d6ac0c54c2..8e4a53e88a7 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-without-devbuild.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-without-devbuild.yaml @@ -22,7 +22,7 @@ commands: id: devrun component: runtime commandLine: "npm install && npm start" - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true diff --git a/tests/examples/source/devfiles/nodejs/devfile-without-devinit.yaml b/tests/examples/source/devfiles/nodejs/devfile-without-devinit.yaml index 61353ade4b1..28cd7920479 100644 --- a/tests/examples/source/devfiles/nodejs/devfile-without-devinit.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile-without-devinit.yaml @@ -22,7 +22,7 @@ commands: id: devbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -30,7 +30,7 @@ commands: id: devrun component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true diff --git a/tests/examples/source/devfiles/nodejs/devfile.yaml b/tests/examples/source/devfiles/nodejs/devfile.yaml index 8387d5e3b4b..23fc1efbd8d 100644 --- a/tests/examples/source/devfiles/nodejs/devfile.yaml +++ b/tests/examples/source/devfiles/nodejs/devfile.yaml @@ -19,7 +19,7 @@ commands: id: devbuild component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -27,14 +27,14 @@ commands: id: build component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build - exec: id: devrun component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true @@ -42,6 +42,6 @@ commands: id: run component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run diff --git a/tests/examples/source/devfiles/nodejs/devfileCompositeCommands.yaml b/tests/examples/source/devfiles/nodejs/devfileCompositeCommands.yaml index 84a5f8d24bd..72e37a9f0d0 100644 --- a/tests/examples/source/devfiles/nodejs/devfileCompositeCommands.yaml +++ b/tests/examples/source/devfiles/nodejs/devfileCompositeCommands.yaml @@ -20,7 +20,7 @@ commands: id: install component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -28,7 +28,7 @@ commands: id: mkdir component: runtime commandLine: mkdir /projects/testfolder - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -36,7 +36,7 @@ commands: id: run component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true diff --git a/tests/examples/source/devfiles/nodejs/devfileCompositeCommandsParallel.yaml b/tests/examples/source/devfiles/nodejs/devfileCompositeCommandsParallel.yaml index 7108d8543a9..172b450a546 100644 --- a/tests/examples/source/devfiles/nodejs/devfileCompositeCommandsParallel.yaml +++ b/tests/examples/source/devfiles/nodejs/devfileCompositeCommandsParallel.yaml @@ -20,7 +20,7 @@ commands: id: install component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -28,7 +28,7 @@ commands: id: mkdir component: runtime commandLine: mkdir /projects/testfolder - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -36,7 +36,7 @@ commands: id: run component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true diff --git a/tests/examples/source/devfiles/nodejs/devfileCompositeInvalidComponent.yaml b/tests/examples/source/devfiles/nodejs/devfileCompositeInvalidComponent.yaml new file mode 100644 index 00000000000..19d9d246b7b --- /dev/null +++ b/tests/examples/source/devfiles/nodejs/devfileCompositeInvalidComponent.yaml @@ -0,0 +1,52 @@ +schemaVersion: 2.0.0 +metadata: + name: nodejs + version: 1.0.0 +projects: + - name: nodejs-starter + git: + location: "https://github.com/odo-devfiles/nodejs-ex.git" +components: + - container: + name: runtime + image: registry.access.redhat.com/ubi8/nodejs-12:1-36 + memoryLimit: 1024Mi + mountSources: true + endpoints: + - name: http-3000 + targetPort: 3000 +commands: + - exec: + id: install + component: runtime + commandLine: npm install + workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + group: + kind: build + isDefault: false + - exec: + id: mkdir + component: fakecomponent + commandLine: mkdir /projects/testfolder + workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + group: + kind: build + isDefault: false + - exec: + id: run + component: runtime + commandLine: npm start + workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + group: + kind: run + isDefault: true + - composite: + id: buildAndMkdir + label: Build and Mkdir + commands: + - mkdir + - install + parallel: false + group: + kind: build + isDefault: true diff --git a/tests/examples/source/devfiles/nodejs/devfileCompositeNonExistent.yaml b/tests/examples/source/devfiles/nodejs/devfileCompositeNonExistent.yaml index a849c4ccca9..769eb2d8734 100644 --- a/tests/examples/source/devfiles/nodejs/devfileCompositeNonExistent.yaml +++ b/tests/examples/source/devfiles/nodejs/devfileCompositeNonExistent.yaml @@ -20,7 +20,7 @@ commands: id: install component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -28,7 +28,7 @@ commands: id: mkdir component: runtime commandLine: mkdir /projects/testfolder - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -36,7 +36,7 @@ commands: id: run component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true diff --git a/tests/examples/source/devfiles/nodejs/devfileCompositeRun.yaml b/tests/examples/source/devfiles/nodejs/devfileCompositeRun.yaml index b75e6aacbf2..fdf19b17ee0 100644 --- a/tests/examples/source/devfiles/nodejs/devfileCompositeRun.yaml +++ b/tests/examples/source/devfiles/nodejs/devfileCompositeRun.yaml @@ -20,7 +20,7 @@ commands: id: install component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -28,7 +28,7 @@ commands: id: mkdir component: runtime commandLine: mkdir /projects/testfolder - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: true @@ -36,7 +36,7 @@ commands: id: run component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: false diff --git a/tests/examples/source/devfiles/nodejs/devfileIndirectNesting.yaml b/tests/examples/source/devfiles/nodejs/devfileIndirectNesting.yaml index b13403b49b0..2a59d250b29 100644 --- a/tests/examples/source/devfiles/nodejs/devfileIndirectNesting.yaml +++ b/tests/examples/source/devfiles/nodejs/devfileIndirectNesting.yaml @@ -16,17 +16,17 @@ commands: id: install component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} - exec: id: echo1 component: runtime commandLine: echo hi - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} - exec: id: run component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true @@ -58,9 +58,9 @@ commands: id: echo2 component: runtime commandLine: echo hello - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} - exec: id: echo3 component: runtime commandLine: echo hellohii - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} diff --git a/tests/examples/source/devfiles/nodejs/devfileNestedCompCommands.yaml b/tests/examples/source/devfiles/nodejs/devfileNestedCompCommands.yaml index f23a1e2d7d2..1666f78ee4d 100644 --- a/tests/examples/source/devfiles/nodejs/devfileNestedCompCommands.yaml +++ b/tests/examples/source/devfiles/nodejs/devfileNestedCompCommands.yaml @@ -20,7 +20,7 @@ commands: id: install component: runtime commandLine: npm install - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -28,7 +28,7 @@ commands: id: mkdir component: runtime commandLine: mkdir /projects/testfolder - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: build isDefault: false @@ -36,7 +36,7 @@ commands: id: run component: runtime commandLine: npm start - workingDir: ${CHE_PROJECTS_ROOT}/nodejs-starter + workingDir: ${PROJECTS_ROOT} group: kind: run isDefault: true diff --git a/tests/examples/source/devfiles/springboot/devfile-with-metadataname-foobar.yaml b/tests/examples/source/devfiles/springboot/devfile-with-metadataname-foobar.yaml index 2b515aeb4c7..77d20f4a5ea 100644 --- a/tests/examples/source/devfiles/springboot/devfile-with-metadataname-foobar.yaml +++ b/tests/examples/source/devfiles/springboot/devfile-with-metadataname-foobar.yaml @@ -37,7 +37,7 @@ commands: id: defaultBuild component: tools commandLine: "/artifacts/bin/build-container-full.sh" - workingDir: /projects/springbootproject + workingDir: /projects group: kind: build isDefault: true diff --git a/tests/examples/source/devfiles/springboot/devfile.yaml b/tests/examples/source/devfiles/springboot/devfile.yaml index d04f6e84acb..05f62e6f483 100644 --- a/tests/examples/source/devfiles/springboot/devfile.yaml +++ b/tests/examples/source/devfiles/springboot/devfile.yaml @@ -37,7 +37,7 @@ commands: id: defaultBuild component: tools commandLine: "/artifacts/bin/build-container-full.sh" - workingDir: /projects/springbootproject + workingDir: /projects group: kind: build isDefault: true diff --git a/tests/helper/helper_kubectl.go b/tests/helper/helper_kubectl.go index 69df0d4d590..aced36be965 100644 --- a/tests/helper/helper_kubectl.go +++ b/tests/helper/helper_kubectl.go @@ -2,7 +2,6 @@ package helper import ( "fmt" - "regexp" "strings" "time" @@ -57,10 +56,9 @@ func (kubectl KubectlRunner) CheckCmdOpInRemoteDevfilePod(podName string, contai // GetRunningPodNameByComponent executes kubectl command and returns the running pod name of a delopyed // devfile component by passing component name as a argument func (kubectl KubectlRunner) GetRunningPodNameByComponent(compName string, namespace string) string { - stdOut := CmdShouldPass(kubectl.path, "get", "pods", "--namespace", namespace, "--show-labels") - re := regexp.MustCompile(`(` + compName + `-\S+)\s+\S+\s+Running.*component=` + compName) - podName := re.FindStringSubmatch(stdOut)[1] - return strings.TrimSpace(podName) + selector := fmt.Sprintf("--selector=component=%s", compName) + stdOut := CmdShouldPass(kubectl.path, "get", "pods", "--namespace", namespace, selector, "-o", "jsonpath={.items[*].metadata.name}") + return strings.TrimSpace(stdOut) } // GetPVCSize executes kubectl command and returns the bound storage size diff --git a/tests/helper/helper_oc.go b/tests/helper/helper_oc.go index 3135a880efd..4760ea7f4a2 100644 --- a/tests/helper/helper_oc.go +++ b/tests/helper/helper_oc.go @@ -373,10 +373,9 @@ func (oc OcRunner) GetRunningPodNameOfComp(compName string, namespace string) st // GetRunningPodNameByComponent executes oc command and returns the running pod name of a delopyed // devfile component by passing component name as a argument func (oc OcRunner) GetRunningPodNameByComponent(compName string, namespace string) string { - stdOut := CmdShouldPass(oc.path, "get", "pods", "--namespace", namespace, "--show-labels") - re := regexp.MustCompile(`(` + compName + `-\S+)\s+\S+\s+Running.*component=` + compName) - podName := re.FindStringSubmatch(stdOut)[1] - return strings.TrimSpace(podName) + selector := fmt.Sprintf("--selector=component=%s", compName) + stdOut := CmdShouldPass(oc.path, "get", "pods", "--namespace", namespace, selector, "-o", "jsonpath={.items[*].metadata.name}") + return strings.TrimSpace(stdOut) } // GetPVCSize executes oc command and returns the bound storage size diff --git a/tests/integration/component.go b/tests/integration/component.go index 2e295a851c3..1f60a14a9ee 100644 --- a/tests/integration/component.go +++ b/tests/integration/component.go @@ -116,7 +116,7 @@ func componentTests(args ...string) { It("should show an error when ref flag is provided with sources except git", func() { outputErr := helper.CmdShouldFail("odo", append(args, "create", "nodejs", "--project", project, "cmp-git", "--ref", "test")...) - Expect(outputErr).To(ContainSubstring("The --ref flag is only valid for --git flag")) + Expect(outputErr).To(ContainSubstring("the --ref flag is only valid for --git flag")) }) It("create component twice fails from same directory", func() { diff --git a/tests/integration/devfile/cmd_devfile_catalog_test.go b/tests/integration/devfile/cmd_devfile_catalog_test.go index f4c81defa94..06413fd2d73 100644 --- a/tests/integration/devfile/cmd_devfile_catalog_test.go +++ b/tests/integration/devfile/cmd_devfile_catalog_test.go @@ -1,6 +1,7 @@ package devfile import ( + "encoding/json" "os" "path/filepath" "time" @@ -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) + }) + }) + }) diff --git a/tests/integration/devfile/cmd_devfile_create_test.go b/tests/integration/devfile/cmd_devfile_create_test.go index eb7032f32ca..41be225ac61 100644 --- a/tests/integration/devfile/cmd_devfile_create_test.go +++ b/tests/integration/devfile/cmd_devfile_create_test.go @@ -1,6 +1,7 @@ package devfile import ( + "encoding/json" "os" "path" "path/filepath" @@ -71,6 +72,22 @@ var _ = Describe("odo devfile create command tests", func() { }) }) + Context("Disabling experimental preference should error out on providing --s2i flag", func() { + JustBeforeEach(func() { + if os.Getenv("KUBERNETES") == "true" { + Skip("Skipping test because s2i image is not supported on Kubernetes cluster") + } + }) + It("checks that the --s2i flag is unrecognised when Experimental is set to false for create", func() { + helper.CmdShouldPass("odo", "preference", "set", "Experimental", "false", "-f") + helper.CopyExample(filepath.Join("source", "nodejs"), context) + + // Check that it will contain the experimental mode output + s2iFlagUnknownMsg := "Error: unknown flag: --s2i" + Expect(helper.CmdShouldFail("odo", "create", "nodejs", "--s2i")).To(ContainSubstring(s2iFlagUnknownMsg)) + }) + }) + Context("When executing odo create with devfile component type argument", func() { It("should successfully create the devfile component", func() { helper.CmdShouldPass("odo", "create", "java-openliberty") @@ -113,6 +130,91 @@ var _ = Describe("odo devfile create command tests", func() { }) }) + Context("When executing odo create with component type argument and --s2i flag", func() { + + JustBeforeEach(func() { + if os.Getenv("KUBERNETES") == "true" { + Skip("Skipping test because s2i image is not supported on Kubernetes cluster") + } + }) + + componentType := "nodejs" + + It("should successfully create the localconfig component", func() { + componentName := helper.RandString(6) + helper.CopyExample(filepath.Join("source", componentType), context) + helper.CmdShouldPass("odo", "create", componentType, componentName, "--s2i") + helper.ValidateLocalCmpExist(context, "Type,nodejs", "Name,"+componentName, "Application,app") + helper.CmdShouldPass("odo", "push", "--context", context, "-v4") + + // clean up + helper.CmdShouldPass("odo", "app", "delete", "app", "-f") + helper.CmdShouldFail("odo", "app", "delete", "app", "-f") + helper.CmdShouldFail("odo", "delete", componentName, "-f") + + }) + + It("should successfully create the localconfig component with --git flag", func() { + componentName := "cmp-git" + helper.CmdShouldPass("odo", "create", componentType, "--git", "https://github.com/openshift/nodejs-ex", "--context", context, "--s2i", "true", "--app", "testing") + helper.CmdShouldPass("odo", "push", "--context", context, "-v4") + + // clean up + helper.CmdShouldPass("odo", "app", "delete", "testing", "-f") + helper.CmdShouldFail("odo", "app", "delete", "testing", "-f") + helper.CmdShouldFail("odo", "delete", componentName, "-f") + }) + + It("should fail to create the devfile component which doesn't have an s2i component of same name", func() { + componentName := helper.RandString(6) + + output := helper.CmdShouldPass("odo", "catalog", "list", "components", "-o", "json") + + wantOutput := []string{"java-openliberty"} + + 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.DontMatchAllInOutput(output, wantOutput) + + outputBytes, err = json.Marshal(data["devfileItems"]) + if err == nil { + output = string(outputBytes) + } + + helper.MatchAllInOutput(output, wantOutput) + + helper.CmdShouldFail("odo", "create", "java-openliberty", componentName, "--s2i") + }) + + It("should fail the create command as --git flag, which is specific to s2i component creation, is used without --s2i flag", func() { + output := helper.CmdShouldFail("odo", "create", "nodejs", "cmp-git", "--git", "https://github.com/openshift/nodejs-ex", "--context", context, "--app", "testing") + Expect(output).Should(ContainSubstring("flag --git, requires --s2i flag to be set, when in experimental mode.")) + }) + + It("should fail the create command as --binary flag, which is specific to s2i component creation, is used without --s2i flag", func() { + helper.CopyExample(filepath.Join("binary", "java", "openjdk"), context) + + output := helper.CmdShouldFail("odo", "create", "java:8", "sb-jar-test", "--binary", filepath.Join(context, "sb.jar"), "--context", context) + Expect(output).Should(ContainSubstring("flag --binary, requires --s2i flag to be set, when in experimental mode.")) + }) + + It("should fail the create command as --now flag, which is specific to s2i component creation, is used without --s2i flag", func() { + componentName := helper.RandString(6) + output := helper.CmdShouldFail("odo", "create", "nodejs", componentName, "--now") + Expect(output).Should(ContainSubstring("flag --now, requires --s2i flag to be set, when in experimental mode.")) + }) + }) + Context("When executing odo create with devfile component type argument and --project flag", func() { It("should successfully create the devfile component", func() { componentNamespace := helper.RandString(6) diff --git a/tests/integration/devfile/cmd_devfile_push_test.go b/tests/integration/devfile/cmd_devfile_push_test.go index 96ac93400b2..4515698ca33 100644 --- a/tests/integration/devfile/cmd_devfile_push_test.go +++ b/tests/integration/devfile/cmd_devfile_push_test.go @@ -16,7 +16,7 @@ import ( var _ = Describe("odo devfile push command tests", func() { var namespace, context, cmpName, currentWorkingDirectory, originalKubeconfig string - var sourcePath = "/projects/nodejs-starter" + var sourcePath = "/projects" // Using program commmand according to cliRunner in devfile cliRunner := helper.GetCliRunner() @@ -241,6 +241,17 @@ var _ = Describe("odo devfile push command tests", func() { Expect(output).To(ContainSubstring("cannot indirectly reference itself")) }) + It("should throw a validation error for composite command that has invalid exec subcommand", func() { + helper.CmdShouldPass("odo", "create", "nodejs", "--project", namespace, cmpName) + + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeInvalidComponent.yaml"), filepath.Join(context, "devfile.yaml")) + + // Verify odo push failed + output := helper.CmdShouldFail("odo", "push", "--context", context) + Expect(output).To(ContainSubstring("references an invalid command")) + }) + It("checks that odo push works outside of the context directory", func() { helper.Chdir(currentWorkingDirectory) @@ -311,7 +322,7 @@ var _ = Describe("odo devfile push command tests", func() { podName, "", namespace, - []string{"stat", "/projects/nodejs-starter/server.js"}, + []string{"stat", "/projects/server.js"}, func(cmdOp string, err error) bool { statErr = err return true @@ -325,14 +336,14 @@ var _ = Describe("odo devfile push command tests", func() { podName, "", namespace, - []string{"stat", "/projects/nodejs-starter/server.js"}, + []string{"stat", "/projects/server.js"}, func(cmdOp string, err error) bool { statErr = err return true }, ) Expect(statErr).To(HaveOccurred()) - Expect(statErr.Error()).To(ContainSubstring("cannot stat '/projects/nodejs-starter/server.js': No such file or directory")) + Expect(statErr.Error()).To(ContainSubstring("cannot stat '/projects/server.js': No such file or directory")) }) It("should build when no changes are detected in the directory and force flag is enabled", func() { diff --git a/tests/integration/devfile/cmd_devfile_test_test.go b/tests/integration/devfile/cmd_devfile_test_test.go index 92b62035665..55dbfd095bf 100644 --- a/tests/integration/devfile/cmd_devfile_test_test.go +++ b/tests/integration/devfile/cmd_devfile_test_test.go @@ -12,7 +12,7 @@ import ( var _ = Describe("odo devfile test command tests", func() { var namespace, context, cmpName, currentWorkingDirectory, originalKubeconfig string - var sourcePath = "/projects/nodejs-starter" + var sourcePath = "/projects" // Using program commmand according to cliRunner in devfile cliRunner := helper.GetCliRunner() diff --git a/tests/integration/devfile/docker/cmd_docker_devfile_push_test.go b/tests/integration/devfile/docker/cmd_docker_devfile_push_test.go index 73eeb0b0e1c..07d887621db 100644 --- a/tests/integration/devfile/docker/cmd_docker_devfile_push_test.go +++ b/tests/integration/devfile/docker/cmd_docker_devfile_push_test.go @@ -14,7 +14,7 @@ import ( var _ = Describe("odo docker devfile push command tests", func() { var context, currentWorkingDirectory, cmpName string - var sourcePath = "/projects/nodejs-starter" + var sourcePath = "/projects" dockerClient := helper.NewDockerRunner("docker") diff --git a/tests/integration/devfile/docker/cmd_docker_devfile_test_test.go b/tests/integration/devfile/docker/cmd_docker_devfile_test_test.go index 405e33789f8..7430787cdf0 100644 --- a/tests/integration/devfile/docker/cmd_docker_devfile_test_test.go +++ b/tests/integration/devfile/docker/cmd_docker_devfile_test_test.go @@ -13,7 +13,7 @@ import ( var _ = Describe("odo docker devfile test command tests", func() { var context, currentWorkingDirectory, cmpName string - var sourcePath = "/projects/nodejs-starter" + var sourcePath = "/projects" dockerClient := helper.NewDockerRunner("docker") diff --git a/tests/integration/devfile/utils/utils.go b/tests/integration/devfile/utils/utils.go index f93bfedc405..0eb46b788b8 100644 --- a/tests/integration/devfile/utils/utils.go +++ b/tests/integration/devfile/utils/utils.go @@ -486,7 +486,7 @@ func validateContainerExecListDir(odoV1Watch OdoV1Watch, odoV2Watch OdoV2Watch, if isDevfileTest { cliRunner := runner.(helper.CliRunner) podName := cliRunner.GetRunningPodNameByComponent(odoV2Watch.CmpName, project) - stdOut = cliRunner.ExecListDir(podName, project, "/projects/nodejs-starter") + stdOut = cliRunner.ExecListDir(podName, project, "/projects") } else { ocRunner := runner.(helper.OcRunner) podName := ocRunner.GetRunningPodNameOfComp(odoV1Watch.SrcType+"-app", project) @@ -498,7 +498,7 @@ func validateContainerExecListDir(odoV1Watch OdoV1Watch, odoV2Watch OdoV2Watch, dockerRunner := runner.(helper.DockerRunner) containers := dockerRunner.GetRunningContainersByCompAlias(odoV2Watch.CmpName, "runtime") Expect(len(containers)).To(Equal(1)) - stdOut = dockerRunner.ExecContainer(containers[0], "ls -la /projects/nodejs-starter") + stdOut = dockerRunner.ExecContainer(containers[0], "ls -la /projects") default: return fmt.Errorf("Platform %s is not supported", platform) }