Create cleanup (#5589)

* Cleanup create code

* Add missing integration tests from odo create to odo init

* Add CreateLocalEnv to create .odo/env/env.yaml file for commands that require it but cannot create it

* Remove 'odo create'  usages from integration tests

* Remove create reference from the Makefile

* REmove create doc from v3

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Remove test files

* Fix CI failure

Signed-off-by: Parthvi Vala <pvala@redhat.com>
This commit is contained in:
Parthvi Vala
2022-03-25 18:07:52 +05:30
committed by GitHub
parent bad34a9f18
commit 80d7cfde30
29 changed files with 222 additions and 3602 deletions

View File

@@ -187,9 +187,9 @@ test-plugin-handler: install ## Run odo plugin handler tests
test-cmd-devfile-list: install ## Run odo list devfile command tests
$(RUN_GINKGO) $(GINKGO_FLAGS) -focus="odo list with devfile" tests/integration/devfile/
.PHONY: test-cmd-devfile-create
test-cmd-devfile-create: install ## Run odo create devfile command tests
$(RUN_GINKGO) $(GINKGO_FLAGS) -focus="odo devfile create command tests" tests/integration/devfile/
.PHONY: test-cmd-devfile-init
test-cmd-devfile-init: install ## Run odo init devfile command tests
$(RUN_GINKGO) $(GINKGO_FLAGS) -focus="odo devfile init command tests" tests/integration/devfile/
.PHONY: test-cmd-devfile-push
test-cmd-devfile-push: install ## Run odo push devfile command tests

View File

@@ -1,92 +0,0 @@
---
title: odo create
sidebar_position: 3
---
odo uses the [_devfile_](https://devfile.io) to store the configuration of and describe the resources like storage, services, etc. of a component. The _odo create_ command allows you to generate this file.
## Creating a component
To create a _devfile_ for an existing project, you can execute `odo create` with the name and type of your component (for example, nodejs or go):
```
odo create nodejs mynodejs
```
Here `nodejs` is the type of the component and `mynodejs` is the name of the component odo creates for you.
> Note: for a list of all the supported component types, run `odo catalog list components`.
If your source code exists outside the current directory, the `--context` flag can be used to specify the path. For example, if the source for the nodejs component was in a folder called `node-backend` relative to the current working directory, you could run:
```
odo create nodejs mynodejs --context ./node-backend
```
Both relative and absolute paths are supported.
To specify the project or app of where your component will be deployed, you can use the `--project` and `--app` flags.
For example, to create a component that is a part of the `myapp` app inside the `backend` project:
```
odo create nodejs --app myapp --project backend
```
> Note: if these are not specified, they will default to the active app and project
## Starter projects
If you do not have existing source code but wish to get up and running quickly to experiment with devfiles and components, you could use the starter projects to get started. To use a starter project, include the `--starter` flag in your `odo create` command.
To get a list of available starter projects for a component type, you can use the `odo catalog describe component` command. For example, to get all available starter projects for the nodejs component type, run:
```
odo catalog describe component nodejs
```
Then specify the desired project with the `--starter` flag:
```
odo create nodejs --starter nodejs-starter
```
This will download the example template corresponding to the chosen component type (in the example above, `nodejs`) in your current directory (or the path provided with the `--context` flag).
If a starter project has its own devfile, then this devfile will be preserved.
## Using an existing devfile
If you want to create a new component from an existing devfile, you can do so by specifying the path to the devfile with the `--devfile` flag.
For example, the following command will create a component called `mynodejs`, based on the devfile from GitHub:
```
odo create mynodejs --devfile https://raw.githubusercontent.com/odo-devfiles/registry/master/devfiles/nodejs/devfile.yaml
```
## Interactive creation
The `odo create` command can also be run interactively. Execute `odo create`, which will guide you through a list of steps to create a component:
```sh
odo create
? Which devfile component type do you wish to create go
? What do you wish to name the new devfile component go-api
? What project do you want the devfile component to be created in default
Devfile Object Validation
✓ Checking devfile existence [164258ns]
✓ Creating a devfile component from registry: DefaultDevfileRegistry [246051ns]
Validation
✓ Validating if devfile name is correct [92255ns]
? Do you want to download a starter project Yes
Starter Project
✓ Downloading starter project go-starter from https://github.com/devfile-samples/devfile-stack-go.git [429ms]
Please use `odo push` command to create the component with source deployed
```
You will be prompted to choose the component type, name and the project for the component. You can also choose whether or not to download a starter project. Once finished, a new `devfile.yaml` file should be created in the working directory.
To deploy these resources to your cluster, run `odo push`.

View File

@@ -13,85 +13,25 @@ import (
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/api/v2/pkg/devfile"
"github.com/devfile/library/pkg/devfile/parser"
parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common"
dfutil "github.com/devfile/library/pkg/util"
"github.com/redhat-developer/odo/pkg/devfile/location"
servicebinding "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
applabels "github.com/redhat-developer/odo/pkg/application/labels"
componentlabels "github.com/redhat-developer/odo/pkg/component/labels"
"github.com/redhat-developer/odo/pkg/envinfo"
"github.com/redhat-developer/odo/pkg/kclient"
"github.com/redhat-developer/odo/pkg/localConfigProvider"
"github.com/redhat-developer/odo/pkg/preference"
"github.com/redhat-developer/odo/pkg/service"
urlpkg "github.com/redhat-developer/odo/pkg/url"
"github.com/redhat-developer/odo/pkg/util"
servicebinding "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/klog"
)
const componentRandomNamePartsMaxLen = 12
const componentNameMaxRetries = 3
const componentNameMaxLen = -1
const NotAvailable = "Not available"
const apiVersion = "odo.dev/v1alpha1"
// GetComponentDir returns source repo name
// Parameters:
// path: source path
// Returns: directory name
func GetComponentDir(path string) (string, error) {
retVal := ""
if path != "" {
retVal = filepath.Base(path)
} else {
currDir, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("unable to generate a random name as getting current directory failed: %w", err)
}
retVal = filepath.Base(currDir)
}
retVal = strings.TrimSpace(util.GetDNS1123Name(strings.ToLower(retVal)))
return retVal, nil
}
// GetDefaultComponentName generates a unique component name
// Parameters: desired default component name(w/o prefix) and slice of existing component names
// Returns: Unique component name and error if any
func GetDefaultComponentName(cfg preference.Client, componentPath string, componentType string, existingComponentList ComponentList) (string, error) {
var prefix string
var err error
// Get component names from component list
var existingComponentNames []string
for _, component := range existingComponentList.Items {
existingComponentNames = append(existingComponentNames, component.Name)
}
// Create a random generated name for the component to use within Kubernetes
prefix, err = GetComponentDir(componentPath)
if err != nil {
return "", fmt.Errorf("unable to generate random component name: %w", err)
}
prefix = util.TruncateString(prefix, componentRandomNamePartsMaxLen)
// Generate unique name for the component using prefix and unique random suffix
componentName, err := dfutil.GetRandomName(
fmt.Sprintf("%s-%s", componentType, prefix),
componentNameMaxLen,
existingComponentNames,
componentNameMaxRetries,
)
if err != nil {
return "", fmt.Errorf("unable to generate random component name: %w", err)
}
return util.GetDNS1123Name(componentName), nil
}
// ApplyConfig applies the component config onto component deployment
// Parameters:
@@ -160,34 +100,6 @@ func List(client kclient.ClientInterface, applicationSelector string) (Component
return newComponentList(devfileList.Items), nil
}
// GetComponentFromDevfile extracts component's metadata from the specified env info if it exists
func GetComponentFromDevfile(info *envinfo.EnvSpecificInfo) (Component, parser.DevfileObj, error) {
if info.Exists() {
devfile, err := parser.Parse(info.GetDevfilePath())
if err != nil {
return Component{}, parser.DevfileObj{}, err
}
component, err := getComponentFrom(info, GetComponentTypeFromDevfileMetadata(devfile.Data.GetMetadata()))
if err != nil {
return Component{}, parser.DevfileObj{}, err
}
components, err := devfile.Data.GetComponents(parsercommon.DevfileOptions{})
if err != nil {
return Component{}, parser.DevfileObj{}, err
}
for _, cmp := range components {
if cmp.Container != nil {
for _, env := range cmp.Container.Env {
component.Spec.Env = append(component.Spec.Env, corev1.EnvVar{Name: env.Name, Value: env.Value})
}
}
}
return component, devfile, nil
}
return Component{}, parser.DevfileObj{}, nil
}
// GetComponentTypeFromDevfileMetadata returns component type from the devfile metadata;
// it could either be projectType or language, if neither of them are set, return 'Not available'
func GetComponentTypeFromDevfileMetadata(metadata devfile.DevfileMetadata) string {
@@ -224,34 +136,6 @@ func GetLanguageFromDevfileMetadata(metadata devfile.DevfileMetadata) string {
return language
}
func getComponentFrom(info localConfigProvider.LocalConfigProvider, componentType string) (Component, error) {
if info.Exists() {
component := newComponentWithType(info.GetName(), componentType)
component.Namespace = info.GetNamespace()
component.Spec = ComponentSpec{
App: info.GetApplication(),
Type: componentType,
Ports: []string{fmt.Sprintf("%d", info.GetDebugPort())},
}
urls, err := info.ListURLs()
if err != nil {
return Component{}, err
}
if len(urls) > 0 {
for _, url := range urls {
component.Spec.URL = append(component.Spec.URL, url.Name)
}
}
return component, nil
}
return Component{}, nil
}
func ListDevfileStacksInPath(client kclient.ClientInterface, paths []string) ([]Component, error) {
var components []Component
var err error
@@ -324,18 +208,6 @@ func Exists(client kclient.ClientInterface, componentName, applicationName strin
return false, nil
}
func GetComponentState(client kclient.ClientInterface, componentName, applicationName string) string {
// Check to see if the deployment has been pushed or not
c, err := GetPushedComponent(client, componentName, applicationName)
if err != nil {
return StateTypeUnknown
}
if c != nil {
return StateTypePushed
}
return StateTypeNotPushed
}
// GetComponent provides component definition
func GetComponent(client kclient.ClientInterface, componentName string, applicationName string, projectName string) (component Component, err error) {
return getRemoteComponentMetadata(client, componentName, applicationName, true, true)

View File

@@ -1,203 +0,0 @@
package component
import (
"encoding/json"
v1 "k8s.io/api/apps/v1"
"github.com/redhat-developer/odo/pkg/kclient"
"github.com/redhat-developer/odo/pkg/localConfigProvider"
"github.com/redhat-developer/odo/pkg/service"
devfileParser "github.com/devfile/library/pkg/devfile/parser"
"github.com/redhat-developer/odo/pkg/envinfo"
"github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/odo/pkg/storage"
urlpkg "github.com/redhat-developer/odo/pkg/url"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ComponentFullDescriptionSpec represents the complete description of the component
type ComponentFullDescriptionSpec struct {
App string `json:"app,omitempty"`
Type string `json:"type,omitempty"`
Source string `json:"source,omitempty"`
URL urlpkg.URLList `json:"urls,omitempty"`
Storage storage.StorageList `json:"storages,omitempty"`
Env []corev1.EnvVar `json:"env,omitempty"`
Ports []string `json:"ports,omitempty"`
}
// ComponentFullDescription describes a component fully
type ComponentFullDescription struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ComponentFullDescriptionSpec `json:"spec,omitempty"`
Status ComponentStatus `json:"status,omitempty"`
}
// copyFromComponentDescription copies over all fields from Component that can be copied
func (cfd *ComponentFullDescription) copyFromComponentDesc(component *Component) error {
d, err := json.Marshal(component)
if err != nil {
return err
}
return json.Unmarshal(d, cfd)
}
// fillEmptyFields fills any fields that are empty in the ComponentFullDescription
func (cfd *ComponentFullDescription) fillEmptyFields(componentDesc Component, componentName string, applicationName string, projectName string) {
// fix missing names in case it is in not in description
if len(cfd.Name) <= 0 {
cfd.Name = componentName
}
if len(cfd.Namespace) <= 0 {
cfd.Namespace = projectName
}
if len(cfd.Kind) <= 0 {
cfd.Kind = "Component"
}
if len(cfd.APIVersion) <= 0 {
cfd.APIVersion = apiVersion
}
if len(cfd.Spec.App) <= 0 {
cfd.Spec.App = applicationName
}
cfd.Spec.Ports = componentDesc.Spec.Ports
}
// NewComponentFullDescriptionFromClientAndLocalConfigProvider gets the complete description of the component from cluster
func NewComponentFullDescriptionFromClientAndLocalConfigProvider(client kclient.ClientInterface, envInfo *envinfo.EnvSpecificInfo, componentName string, applicationName string, projectName string, context string) (*ComponentFullDescription, error) {
cfd := &ComponentFullDescription{}
var state string
if client == nil {
state = StateTypeUnknown
} else {
state = GetComponentState(client, componentName, applicationName)
}
var componentDesc Component
var devfile devfileParser.DevfileObj
var err error
var configLinks []string
componentDesc, devfile, err = GetComponentFromDevfile(envInfo)
if err != nil {
return cfd, err
}
configLinks, err = service.ListDevfileLinks(devfile, context)
if err != nil {
return cfd, err
}
err = cfd.copyFromComponentDesc(&componentDesc)
if err != nil {
return cfd, err
}
cfd.Status.State = state
var deployment *v1.Deployment
if state == StateTypePushed {
componentDescFromCluster, e := getRemoteComponentMetadata(client, componentName, applicationName, false, false)
if e != nil {
return cfd, e
}
cfd.Spec.Env = componentDescFromCluster.Spec.Env
cfd.Spec.Type = componentDescFromCluster.Spec.Type
cfd.Status.LinkedServices = componentDescFromCluster.Status.LinkedServices
// Obtain the deployment to correctly instantiate the Storage and URL client and get information
deployment, err = client.GetOneDeployment(componentName, applicationName)
if err != nil {
// ideally the error should not occur since we have already established that the component exists on the cluster
return cfd, err
}
}
for _, link := range configLinks {
found := false
for _, linked := range cfd.Status.LinkedServices {
if linked.ServiceName == link {
found = true
break
}
}
if !found {
cfd.Status.LinkedServices = append(cfd.Status.LinkedServices, SecretMount{
ServiceName: link,
})
}
}
cfd.fillEmptyFields(componentDesc, componentName, applicationName, projectName)
var urls urlpkg.URLList
var routeSupported bool
var e error
if client == nil {
routeSupported = false
} else {
routeSupported, e = client.IsRouteSupported()
if e != nil {
// we assume if there was an error then the cluster is not connected
routeSupported = false
}
}
var configProvider localConfigProvider.LocalConfigProvider
envInfo.SetDevfileObj(devfile)
configProvider = envInfo
urlClient := urlpkg.NewClient(urlpkg.ClientOptions{
LocalConfigProvider: configProvider,
Client: client,
IsRouteSupported: routeSupported,
Deployment: deployment,
})
urls, err = urlClient.List()
if err != nil {
log.Warningf("URLs couldn't not be retrieved: %v", err)
}
cfd.Spec.URL = urls
// Obtain Storage information
var storages storage.StorageList
storageClient := storage.NewClient(storage.ClientOptions{
Client: client,
LocalConfigProvider: envInfo,
Deployment: deployment,
})
// We explicitly do not fill in Spec.Storage for describe because,
// it is essentially a list of Storage names, which seems redundant when the detailed StorageSpec is available
storages, err = storageClient.List()
if err != nil {
log.Warningf("Storages couldn't not be retrieved: %v", err)
}
cfd.Spec.Storage = storages
return cfd, nil
}
// GetComponent returns a component representation
func (cfd *ComponentFullDescription) GetComponent() Component {
cmp := NewComponent(cfd.Name)
cmp.Spec.App = cfd.Spec.App
cmp.Spec.Ports = cfd.Spec.Ports
cmp.Spec.Type = cfd.Spec.Type
cmp.Spec.StorageSpec = cfd.Spec.Storage.Items
cmp.Spec.URLSpec = cfd.Spec.URL.Items
for _, url := range cfd.Spec.URL.Items {
cmp.Spec.URL = append(cmp.Spec.URL, url.Name)
}
for _, storage := range cfd.Spec.Storage.Items {
cmp.Spec.Storage = append(cmp.Spec.URL, storage.Name)
}
cmp.ObjectMeta.Namespace = cfd.ObjectMeta.Namespace
cmp.Status = cfd.Status
cmp.Spec.Env = cfd.Spec.Env
return cmp
}

View File

@@ -3,7 +3,6 @@ package component
import (
"fmt"
"reflect"
"regexp"
"testing"
devfilepkg "github.com/devfile/api/v2/pkg/devfile"
@@ -12,12 +11,9 @@ import (
v1 "k8s.io/api/apps/v1"
"github.com/devfile/library/pkg/util"
"github.com/golang/mock/gomock"
applabels "github.com/redhat-developer/odo/pkg/application/labels"
componentlabels "github.com/redhat-developer/odo/pkg/component/labels"
"github.com/redhat-developer/odo/pkg/kclient"
"github.com/redhat-developer/odo/pkg/localConfigProvider"
"github.com/redhat-developer/odo/pkg/preference"
"github.com/redhat-developer/odo/pkg/testingutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -25,122 +21,6 @@ import (
ktesting "k8s.io/client-go/testing"
)
func TestGetComponentFrom(t *testing.T) {
type cmpSetting struct {
componentName string
project string
applicationName string
debugPort int
}
tests := []struct {
name string
isEnvInfo bool
componentType string
envURL []localConfigProvider.LocalURL
cmpSetting cmpSetting
want Component
wantErr bool
}{
{
name: "Case 1: Get component when env info file exists",
isEnvInfo: true,
componentType: "nodejs",
envURL: []localConfigProvider.LocalURL{
{
Name: "url1",
},
},
cmpSetting: cmpSetting{
componentName: "frontend",
project: "project1",
applicationName: "testing",
debugPort: 1234,
},
want: Component{
TypeMeta: metav1.TypeMeta{
Kind: "Component",
APIVersion: "odo.dev/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "frontend",
},
Spec: ComponentSpec{
Type: "nodejs",
},
Status: ComponentStatus{},
},
},
{
name: "Case 2: Get component when env info file does not exists",
isEnvInfo: false,
componentType: "nodejs",
envURL: []localConfigProvider.LocalURL{
{
Name: "url2",
},
},
cmpSetting: cmpSetting{
componentName: "backend",
project: "project2",
applicationName: "app1",
debugPort: 5896,
},
want: Component{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockLocalConfigProvider := localConfigProvider.NewMockLocalConfigProvider(ctrl)
mockLocalConfigProvider.EXPECT().Exists().Return(tt.isEnvInfo)
if tt.isEnvInfo {
mockLocalConfigProvider.EXPECT().GetName().Return(tt.cmpSetting.componentName)
component := newComponentWithType(tt.cmpSetting.componentName, tt.componentType)
mockLocalConfigProvider.EXPECT().GetNamespace().Return(tt.cmpSetting.project)
component.Namespace = tt.cmpSetting.project
mockLocalConfigProvider.EXPECT().GetApplication().Return(tt.cmpSetting.applicationName)
mockLocalConfigProvider.EXPECT().GetDebugPort().Return(tt.cmpSetting.debugPort)
component.Spec = ComponentSpec{
App: tt.cmpSetting.applicationName,
Type: tt.componentType,
Ports: []string{fmt.Sprintf("%d", tt.cmpSetting.debugPort)},
}
mockLocalConfigProvider.EXPECT().ListURLs().Return(tt.envURL, nil)
if len(tt.envURL) > 0 {
for _, url := range tt.envURL {
component.Spec.URL = append(component.Spec.URL, url.Name)
}
}
tt.want = component
}
got, err := getComponentFrom(mockLocalConfigProvider, tt.componentType)
if (err != nil) != tt.wantErr {
t.Errorf("getComponentFrom() error = %v, wantErr %v", err, tt.wantErr)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("getComponentFrom() = %v, want %v", got, tt.want)
}
})
}
}
func TestList(t *testing.T) {
deploymentList := v1.DeploymentList{Items: []v1.Deployment{
*testingutil.CreateFakeDeployment("comp0"),
@@ -231,90 +111,6 @@ func TestList(t *testing.T) {
}
}
func TestGetDefaultComponentName(t *testing.T) {
tests := []struct {
testName string
componentType string
componentPath string
existingComponents ComponentList
wantErr bool
wantRE string
needPrefix bool
}{
{
testName: "Case: App prefix configured",
componentType: "nodejs",
componentPath: "./testing",
existingComponents: ComponentList{},
wantErr: false,
wantRE: "nodejs-testing-*",
needPrefix: true,
},
}
for _, tt := range tests {
t.Log("Running test: ", tt.testName)
t.Run(tt.testName, func(t *testing.T) {
ctrl := gomock.NewController(t)
cfg := preference.NewMockClient(ctrl)
name, err := GetDefaultComponentName(cfg, tt.componentPath, tt.componentType, tt.existingComponents)
if (err != nil) != tt.wantErr {
t.Errorf("expected err: %v, but err is %v", tt.wantErr, err)
}
r, _ := regexp.Compile(tt.wantRE)
match := r.MatchString(name)
if !match {
t.Errorf("randomly generated application name %s does not match regexp %s", name, tt.wantRE)
}
})
}
}
func TestGetComponentDir(t *testing.T) {
type args struct {
path string
}
tests := []struct {
testName string
args args
want string
wantErr bool
}{
{
testName: "Case: Source Path",
args: args{
path: "./testing",
},
wantErr: false,
want: "testing",
},
{
testName: "Case: No clue of any component",
args: args{
path: "",
},
wantErr: false,
want: "component",
},
}
for _, tt := range tests {
t.Log("Running test: ", tt.testName)
t.Run(tt.testName, func(t *testing.T) {
name, err := GetComponentDir(tt.args.path)
if (err != nil) != tt.wantErr {
t.Errorf("expected err: %v, but err is %v", tt.wantErr, err)
}
if name != tt.want {
t.Errorf("received name %s which does not match %s", name, tt.want)
}
})
}
}
func Test_getMachineReadableFormat(t *testing.T) {
type args struct {
componentName string

View File

@@ -6,24 +6,19 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/redhat-developer/odo/pkg/devfile/location"
"github.com/redhat-developer/odo/pkg/log"
registryUtil "github.com/redhat-developer/odo/pkg/odo/cli/preference/registry/util"
"github.com/redhat-developer/odo/pkg/util"
)
const (
defaultStarterProjectName = "devfile-starter-project-name"
)
func checkoutProject(subDir, zipURL, path, starterToken string) error {
if subDir == "" {
@@ -36,43 +31,6 @@ func checkoutProject(subDir, zipURL, path, starterToken string) error {
return nil
}
// GetStarterProject gets starter project value from flag --starter.
func GetStarterProject(projects []devfilev1.StarterProject, projectPassed string) (project *devfilev1.StarterProject, err error) {
nOfProjects := len(projects)
if nOfProjects == 0 {
return nil, fmt.Errorf("no starter project found in devfile.")
}
// Determine what project to be used
if nOfProjects == 1 && projectPassed == defaultStarterProjectName {
project = &projects[0]
} else if nOfProjects > 1 && projectPassed == defaultStarterProjectName {
project = &projects[0]
log.Warning("There are multiple projects in this devfile but none have been specified in --starter. Downloading the first: " + project.Name)
} else { //If the user has specified a project
var availableNames []string
projectFound := false
for indexOfProject, projectInfo := range projects {
availableNames = append(availableNames, projectInfo.Name)
if projectInfo.Name == projectPassed { //Get the index
project = &projects[indexOfProject]
projectFound = true
}
}
if !projectFound {
availableNamesString := strings.Join(availableNames, ",")
return nil, fmt.Errorf("the project: %s specified in --starter does not exist, available projects: %s", projectPassed, availableNamesString)
}
}
return project, err
}
// DownloadStarterProject downloads a starter project referenced in devfile
// This will first remove the content of the contextDir
func DownloadStarterProject(starterProject *devfilev1.StarterProject, decryptedToken string, contextDir string, verbose bool) error {

View File

@@ -178,7 +178,6 @@ func odoRootCmd(name, fullName string) *cobra.Command {
rootCmdList := append([]*cobra.Command{},
component.NewCmdComponent(component.RecommendedCommandName, util.GetFullName(fullName, component.RecommendedCommandName)),
component.NewCmdCreate(component.CreateRecommendedCommandName, util.GetFullName(fullName, component.CreateRecommendedCommandName)),
component.NewCmdList(component.ListRecommendedCommandName, util.GetFullName(fullName, component.ListRecommendedCommandName)),
component.NewCmdPush(component.PushRecommendedCommandName, util.GetFullName(fullName, component.PushRecommendedCommandName)),
login.NewCmdLogin(login.RecommendedCommandName, util.GetFullName(fullName, login.RecommendedCommandName)),

View File

@@ -3,10 +3,11 @@ package component
import (
"fmt"
"github.com/spf13/cobra"
"github.com/redhat-developer/odo/pkg/odo/cmdline"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
"github.com/spf13/cobra"
)
// RecommendedCommandName is the recommended component command name
@@ -39,7 +40,6 @@ func (co *ComponentOptions) Complete(cmdline cmdline.Cmdline, args []string) (er
func NewCmdComponent(name, fullName string) *cobra.Command {
componentGetCmd := NewCmdGet(GetRecommendedCommandName, odoutil.GetFullName(fullName, GetRecommendedCommandName))
createCmd := NewCmdCreate(CreateRecommendedCommandName, odoutil.GetFullName(fullName, CreateRecommendedCommandName))
listCmd := NewCmdList(ListRecommendedCommandName, odoutil.GetFullName(fullName, ListRecommendedCommandName))
pushCmd := NewCmdPush(PushRecommendedCommandName, odoutil.GetFullName(fullName, PushRecommendedCommandName))
@@ -47,8 +47,8 @@ func NewCmdComponent(name, fullName string) *cobra.Command {
var componentCmd = &cobra.Command{
Use: name,
Short: "Manage components",
Example: fmt.Sprintf("%s\n%s\n\n See sub-commands individually for more examples",
fullName, CreateRecommendedCommandName),
Example: fmt.Sprintf("%s\n\n See sub-commands individually for more examples",
fullName),
// `odo component set/get` and `odo get/set` are respectively deprecated as per the new workflow
Run: func(cmd *cobra.Command, args []string) {
},
@@ -57,7 +57,7 @@ func NewCmdComponent(name, fullName string) *cobra.Command {
// add flags from 'get' to component command
componentCmd.Flags().AddFlagSet(componentGetCmd.Flags())
componentCmd.AddCommand(componentGetCmd, createCmd, listCmd, pushCmd)
componentCmd.AddCommand(componentGetCmd, listCmd, pushCmd)
// Add a defined annotation in order to appear in the help menu
componentCmd.Annotations = map[string]string{"command": "main"}

View File

@@ -1,425 +0,0 @@
package component
import (
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"github.com/zalando/go-keyring"
odoDevfile "github.com/redhat-developer/odo/pkg/devfile"
"github.com/redhat-developer/odo/pkg/devfile/location"
"github.com/redhat-developer/odo/pkg/envinfo"
"github.com/redhat-developer/odo/pkg/log"
registryUtil "github.com/redhat-developer/odo/pkg/odo/cli/preference/registry/util"
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
"github.com/redhat-developer/odo/pkg/odo/cmdline"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"github.com/redhat-developer/odo/pkg/registry"
scontext "github.com/redhat-developer/odo/pkg/segment/context"
"github.com/redhat-developer/odo/pkg/util"
"github.com/devfile/library/pkg/devfile"
"github.com/devfile/library/pkg/devfile/parser"
dfutil "github.com/devfile/library/pkg/util"
ktemplates "k8s.io/kubectl/pkg/util/templates"
)
// CreateOptions encapsulates create options
type CreateOptions struct {
// Push context
*PushOptions
// Flags
contextFlag string
portFlag []string
envFlag []string
nowFlag bool
appFlag string
interactive bool
// devfileName stores the "componentType" passed by user irrespective of it being a valid componentType
// we use it for telemetry
devfileName string
createMethod CreateMethod
devfileMetadata DevfileMetadata
stackName string
}
// Path of user's own devfile, user specifies the path via --devfile flag
type devfilePath struct {
protocol string
value string
}
// DevfileMetadata includes devfile component metadata
type DevfileMetadata struct {
componentType string
componentName string
componentNamespace string
devfileLink string
devfileRegistry registry.Registry
devfilePath devfilePath
userCreatedDevfile bool
starter string
token string
starterToken string
}
// CreateRecommendedCommandName is the recommended watch command name
const CreateRecommendedCommandName = "create"
// LocalDirectoryDefaultLocation is the default location of where --local files should always be..
// since the application will always be in the same directory as `.odo`, we will always set this as: ./
const LocalDirectoryDefaultLocation = "./"
var (
EnvYAMLFilePath = filepath.Join(".odo", "env", "env.yaml")
EnvDirectory = filepath.Join(".odo", "env")
)
var createLongDesc = ktemplates.LongDesc(`Create a configuration describing a component.`)
var createExample = ktemplates.Examples(`# Create a new Node.JS component with existing sourcecode as well as specifying a name
%[1]s nodejs mynodejs
# Name is not required and will be automatically generated if not passed
%[1]s nodejs
# List all available components before deploying
odo catalog list components
%[1]s java-quarkus
# Download an example devfile and application before deploying
%[1]s nodejs --starter
# Using a specific devfile
%[1]s mynodejs --devfile ./devfile.yaml
%[1]s mynodejs --devfile https://raw.githubusercontent.com/odo-devfiles/registry/master/devfiles/nodejs/devfile.yaml
# Create new Node.js component named 'frontend' with the source in './frontend' directory
%[1]s nodejs frontend --context ./frontend
# Create new Node.js component with custom ports and environment variables
%[1]s nodejs --port 8080,8100/tcp,9100/udp --env key=value,key1=value1
# Create a new Node.js component that is a part of 'myapp' app inside the 'myproject' project
%[1]s nodejs --app myapp --project myproject`)
// NewCreateOptions returns new instance of CreateOptions
func NewCreateOptions() *CreateOptions {
return &CreateOptions{
PushOptions: NewPushOptions(),
}
}
func (co *CreateOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
// GETTERS
// Get context
co.Context, err = getContext(co.nowFlag, cmdline)
if err != nil {
return err
}
// Get the app name
co.appFlag = genericclioptions.ResolveAppFlag(cmdline)
// Get the project name
co.devfileMetadata.componentNamespace = co.Context.GetProject()
// Get DevfilePath
co.DevfilePath = location.DevfileLocation(co.contextFlag)
//Check whether the directory already contains a devfile, this check should happen early
co.devfileMetadata.userCreatedDevfile = util.CheckPathExists(co.DevfilePath)
// EnvFilePath is the path of env file for devfile component
envFilePath := getEnvFilePath(co.contextFlag)
// This is required so that .odo is created in the correct context
co.PushOptions.componentContext = co.contextFlag
// Use Interactive mode if: 1) no args are passed || 2) the devfile exists || 3) --devfile is used
if len(args) == 0 && !util.CheckPathExists(co.DevfilePath) && co.devfileMetadata.devfilePath.value == "" {
co.interactive = true
}
// CONFLICT CHECK
// Check if a component exists
if util.CheckPathExists(co.DevfilePath) {
if util.CheckPathExists(envFilePath) {
return errors.New("this directory already contains a component")
} else if co.devfileMetadata.devfilePath.value != "" && !dfutil.PathEqual(co.DevfilePath, co.devfileMetadata.devfilePath.value) {
//Check if the directory already contains a devfile when --devfile flag is passed
return errors.New("this directory already contains a devfile, you can't specify devfile via --devfile")
} else if co.devfileMetadata.starter != "" && len(args) == 0 {
//if devfile already exists, then don't allow --starter
return fmt.Errorf("this directory already has a devfile so you cannot provide a starter. Please remove exisiting devfile and re-create")
}
}
// Check if there is a dangling env file; delete the env file if found
if util.CheckPathExists(envFilePath) && !util.CheckPathExists(co.DevfilePath) {
log.Warningf("Found a dangling env file without a devfile, overwriting it")
// Note: if the IF condition seems to have a side-effect, it is better to do the condition check separately, like below
err = dfutil.DeletePath(envFilePath)
if err != nil {
return err
}
}
// Initialize envinfo
err = co.InitEnvInfoFromContext()
if err != nil {
return err
}
// Fetch the necessary devfile and create the component
log.Info("Devfile Object Creation")
switch {
case co.devfileMetadata.userCreatedDevfile:
co.createMethod = UserCreatedDevfileMethod{}
case co.devfileMetadata.devfilePath.value != "":
//co.devfileName = "" for user provided devfile
fileErr := dfutil.ValidateFile(co.devfileMetadata.devfilePath.value)
urlErr := util.ValidateURL(co.devfileMetadata.devfilePath.value)
if fileErr != nil && urlErr != nil {
return fmt.Errorf("the devfile path you specify is invalid with either file error %q or url error %q", fileErr, urlErr)
} else if fileErr == nil {
co.createMethod = FileCreateMethod{}
} else if urlErr == nil {
co.createMethod = HTTPCreateMethod{}
}
case co.interactive:
co.createMethod = InteractiveCreateMethod{}
default:
co.createMethod = DirectCreateMethod{}
}
err = co.createMethod.CheckConflicts(co, args)
if err != nil {
return err
}
err = co.createMethod.FetchDevfileAndCreateComponent(co, cmdline, args)
if err != nil {
co.createMethod.Rollback(co.DevfilePath, co.contextFlag)
return err
}
// From this point forward, rollback should be triggered if an error is encountered; rollback should delete all the files that were created by odo
defer func() {
if err != nil {
co.createMethod.Rollback(co.DevfilePath, co.contextFlag)
}
}()
// Set the starter project token if required
if co.devfileMetadata.starter != "" {
secure := registryUtil.IsSecure(co.clientset.PreferenceClient, co.devfileMetadata.devfileRegistry.Name)
if co.devfileMetadata.starterToken == "" && secure {
var token string
token, err = keyring.Get(fmt.Sprintf("%s%s", dfutil.CredentialPrefix, co.devfileMetadata.devfileRegistry.Name), registryUtil.RegistryUser)
if err != nil {
return fmt.Errorf("unable to get secure registry credential from keyring: %w", err)
}
co.devfileMetadata.starterToken = token
}
}
scontext.SetDevfileName(cmdline.Context(), co.devfileName)
// Adding component type to telemetry data
scontext.SetComponentType(cmdline.Context(), co.devfileMetadata.componentType)
if len(args) > 0 {
co.stackName = args[0]
}
return nil
}
func (co *CreateOptions) Validate() (err error) {
defer func() {
if err != nil {
co.createMethod.Rollback(co.DevfilePath, co.contextFlag)
}
}()
log.Info("Validation")
// Validate if the devfile component name that user wants to create adheres to the k8s naming convention
spinner := log.Spinner("Validating if devfile name is correct")
defer spinner.End(false)
err = dfutil.ValidateK8sResourceName("component name", co.devfileMetadata.componentName)
if err != nil {
return err
}
spinner.End(true)
// Validate if the devfile is compatible with odo; this checks the resolved/flattened version of devfile
spinner = log.Spinner("Validating the devfile for odo")
defer spinner.End(false)
_, err = odoDevfile.ParseAndValidateFromFile(co.DevfilePath)
if err != nil {
return err
}
spinner.End(true)
return nil
}
func (co *CreateOptions) Run() (err error) {
defer func() {
if err != nil {
co.createMethod.Rollback(co.DevfilePath, co.contextFlag)
}
}()
devObj, err := devfileParseFromFile(co.DevfilePath, false)
if err != nil {
return errors.New("Failed to parse the devfile")
}
devfileData, err := ioutil.ReadFile(co.DevfilePath)
if err != nil {
return err
}
// WARN: Starter Project uses go-git that overrides the directory content, there by deleting the existing devfile.
err = decideAndDownloadStarterProject(devObj, co.devfileMetadata.starter, co.devfileMetadata.starterToken, co.interactive, co.contextFlag)
if err != nil {
return fmt.Errorf("failed to download project for devfile component: %w", err)
}
//Check if the directory already contains a devfile when starter project is downloaded
if co.devfileMetadata.starter != "" && len(co.stackName) > 0 && !(util.CheckPathExists(co.DevfilePath) && co.devfileMetadata.devfilePath.value != "" && !dfutil.PathEqual(co.DevfilePath, co.devfileMetadata.devfilePath.value)) {
// TODO: We should not have to rewrite to the file. Fix the starter project.
err = ioutil.WriteFile(co.DevfilePath, devfileData, 0644) // #nosec G306
if err != nil {
return err
}
}
// If user provided a custom name, re-write the devfile
// ENSURE: co.devfileMetadata.componentName != ""
if co.devfileMetadata.componentName != devObj.GetMetadataName() {
spinner := log.Spinnerf("Updating the devfile with component name %q", co.devfileMetadata.componentName)
defer spinner.End(false)
// WARN: SetMetadataName will rewrite to the devfile
err = devObj.SetMetadataName(co.devfileMetadata.componentName)
if err != nil {
return errors.New("Failed to update the devfile")
}
spinner.End(true)
}
// Generate env file
err = co.EnvSpecificInfo.SetComponentSettings(envinfo.ComponentSettings{
Name: co.devfileMetadata.componentName,
Project: co.devfileMetadata.componentNamespace,
AppName: co.appFlag,
UserCreatedDevfile: co.devfileMetadata.userCreatedDevfile,
})
if err != nil {
return fmt.Errorf("failed to create env file for devfile component: %w", err)
}
// Prepare .gitignore file
sourcePath, err := dfutil.GetAbsPath(co.contextFlag)
if err != nil {
return fmt.Errorf("unable to get source path: %w", err)
}
ignoreFile, err := util.TouchGitIgnoreFile(sourcePath)
if err != nil {
return err
}
err = dfutil.AddFileToIgnoreFile(ignoreFile, filepath.Join(co.contextFlag, EnvDirectory))
if err != nil {
return err
}
if co.nowFlag {
err = co.DevfilePush()
if err != nil {
return fmt.Errorf("failed to push changes: %w", err)
}
} else {
log.Italic("\nPlease use `odo push` command to create the component with source deployed")
}
if log.IsJSON() {
return co.DevfileJSON()
}
return nil
}
// NewCmdCreate implements the create odo command
func NewCmdCreate(name, fullName string) *cobra.Command {
co := NewCreateOptions()
var componentCreateCmd = &cobra.Command{
Use: fmt.Sprintf("%s <component_type> [component_name] [flags]", name),
Short: "Create a new component",
Long: createLongDesc,
Example: fmt.Sprintf(createExample, fullName),
Args: cobra.RangeArgs(0, 2),
Annotations: map[string]string{"machineoutput": "json", "command": "component"},
Run: func(cmd *cobra.Command, args []string) {
genericclioptions.GenericRun(co, cmd, args)
},
}
clientset.Add(componentCreateCmd, clientset.PROJECT, clientset.PREFERENCE)
odoutil.AddContextFlag(componentCreateCmd, &co.contextFlag)
componentCreateCmd.Flags().StringSliceVarP(&co.portFlag, "port", "p", []string{}, "Ports to be used when the component is created (ex. 8080,8100/tcp,9100/udp)")
componentCreateCmd.Flags().StringSliceVar(&co.envFlag, "env", []string{}, "Environmental variables for the component. For example --env VariableName=Value")
componentCreateCmd.Flags().StringVar(&co.devfileMetadata.starter, "starter", "", "Download a project specified in the devfile")
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 specified 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.starterToken, "starter-token", "", "Token to be used when downloading starter project")
componentCreateCmd.SetFlagErrorFunc(func(command *cobra.Command, err error) error {
if strings.Contains(err.Error(), "flag needs an argument: --starter") {
return fmt.Errorf("%w: you can get the list of possible values with the command `odo catalog describe component <type>`", err)
}
return err
})
componentCreateCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
// Adding `--now` flag
odoutil.AddNowFlag(componentCreateCmd, &co.nowFlag)
//Adding `--project` flag
projectCmd.AddProjectFlag(componentCreateCmd)
completion.RegisterCommandHandler(componentCreateCmd, completion.CreateCompletionHandler)
completion.RegisterCommandFlagHandler(componentCreateCmd, "context", completion.FileCompletionHandler)
completion.RegisterCommandFlagHandler(componentCreateCmd, "binary", completion.FileCompletionHandler)
return componentCreateCmd
}
func getContext(now bool, cmdline cmdline.Cmdline) (*genericclioptions.Context, error) {
params := genericclioptions.NewCreateParameters(cmdline)
if now {
params = params.CreateAppIfNeeded()
} else {
params = params.IsOffline()
}
return genericclioptions.New(params)
}
func getEnvFilePath(componentContext string) string {
if componentContext != "" {
return filepath.Join(componentContext, EnvYAMLFilePath)
}
return filepath.Join(LocalDirectoryDefaultLocation, EnvYAMLFilePath)
}
// DevfileParseFromFile reads, parses and validates a devfile from a file without flattening it
func devfileParseFromFile(devfilePath string, resolved bool) (parser.DevfileObj, error) {
devObj, _, err := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: &resolved})
if err != nil {
return parser.DevfileObj{}, err
}
return devObj, nil
}

View File

@@ -1,62 +0,0 @@
package component
import (
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/library/pkg/devfile/parser"
parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/envinfo"
"github.com/redhat-developer/odo/pkg/kclient"
"github.com/redhat-developer/odo/pkg/machineoutput"
)
// decideAndDownloadStarterProject decides the starter project from the value passed by the user and
// downloads it
func decideAndDownloadStarterProject(devObj parser.DevfileObj, projectPassed string, token string, interactive bool, contextDir string) error {
if projectPassed == "" && !interactive {
return nil
}
// Retrieve starter projects
starterProjects, err := devObj.Data.GetStarterProjects(parsercommon.DevfileOptions{})
if err != nil {
return err
}
var starterProject *devfilev1.StarterProject
if interactive {
starterProject = getStarterProjectInteractiveMode(starterProjects)
} else {
starterProject, err = component.GetStarterProject(starterProjects, projectPassed)
if err != nil {
return err
}
}
if starterProject == nil {
return nil
}
return component.DownloadStarterProject(starterProject, token, contextDir, true)
}
// DevfileJSON creates the full json description of a devfile component is prints it
func (co *CreateOptions) DevfileJSON() error {
envInfo, err := envinfo.NewEnvSpecificInfo(co.contextFlag)
if err != nil {
return err
}
// Ignore the error as we want other information if connection to cluster is not possible
var c kclient.ClientInterface
client, _ := kclient.New()
if client != nil {
c = client
}
cfd, err := component.NewComponentFullDescriptionFromClientAndLocalConfigProvider(c, envInfo, envInfo.GetName(), envInfo.GetApplication(), co.GetProject(), co.GetComponentContext())
if err != nil {
return err
}
machineoutput.OutputSuccess(cfd.GetComponent())
return nil
}

View File

@@ -1,427 +0,0 @@
package component
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/zalando/go-keyring"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/kclient"
"github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/odo/pkg/odo/cli/component/ui"
registryUtil "github.com/redhat-developer/odo/pkg/odo/cli/preference/registry/util"
"github.com/redhat-developer/odo/pkg/odo/cmdline"
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/preference"
"github.com/redhat-developer/odo/pkg/registry"
"github.com/redhat-developer/odo/pkg/segment"
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
"github.com/redhat-developer/odo/pkg/util"
dfutil "github.com/devfile/library/pkg/util"
registryLibrary "github.com/devfile/registry-support/registry-library/library"
"k8s.io/klog"
)
type CreateMethod interface {
// CheckConflicts checks for conflicts specific to a create method
CheckConflicts(co *CreateOptions, args []string) error
// FetchDevfileAndCreateComponent fetches devfile from registry, or a remote location, or a local file system, and creates a component
// This method also updates the CreateOptions structure with co.devfileMetadata
FetchDevfileAndCreateComponent(co *CreateOptions, cmdline cmdline.Cmdline, args []string) error
// Rollback cleans the component context of any files that were created by odo (devfile.yaml, .odo e.g.)
Rollback(devfile, componentContext string)
}
// InteractiveCreateMethod is used while creating a component interactively
type InteractiveCreateMethod struct{}
func (icm InteractiveCreateMethod) CheckConflicts(co *CreateOptions, args []string) error {
return nil
}
func (icm InteractiveCreateMethod) FetchDevfileAndCreateComponent(co *CreateOptions, cmdline cmdline.Cmdline, args []string) error {
catalogDevfileList, err := validateAndFetchRegistry(co.devfileMetadata.devfileRegistry.Name)
if err != nil {
return err
}
//SET METADATA
// Component type: We provide devfile component list to let user choose
componentType := ui.SelectDevfileComponentType(catalogDevfileList.Items)
// Component name: User needs to specify the component name, by default it is component type that user chooses
componentName := ui.EnterDevfileComponentName(componentType)
// Component namespace: User needs to specify component namespace, by default it is the current active namespace
var componentNamespace string
if cmdline.IsFlagSet("project") {
componentNamespace, err = cmdline.FlagValue("project")
if err != nil {
return err
}
} else {
var currentNamespace string
if co.KClient != nil {
// Get the current project if no --project is passed, but --now is passed
currentNamespace = co.KClient.GetCurrentProjectName()
} else {
var client kclient.ClientInterface
client, err = kclient.New()
// if err != nil, currentNamespace will be an empty string
if err == nil && client != nil {
// Get the current project if no --project is passed and using offline context
currentNamespace = client.GetCurrentProjectName()
}
}
componentNamespace = ui.EnterDevfileComponentProject(currentNamespace)
}
co.devfileMetadata.componentType = componentType
co.devfileName = componentType
co.devfileMetadata.componentName = componentName
co.devfileMetadata.componentNamespace = componentNamespace
co.devfileMetadata.devfileLink, co.devfileMetadata.devfileRegistry, err = findDevfileFromRegistry(catalogDevfileList, co.devfileMetadata.devfileRegistry.Name, co.devfileMetadata.componentType)
if err != nil {
return err
}
return fetchDevfileFromRegistry(co.devfileMetadata.devfileRegistry, co.devfileMetadata.devfileLink, co.DevfilePath, co.devfileMetadata.componentType, co.contextFlag, co.clientset.PreferenceClient)
}
func (icm InteractiveCreateMethod) Rollback(devfile, componentContext string) {
deleteDevfile(devfile)
deleteOdoDir(componentContext)
}
// DirectCreateMethod is used with the basic odo create; `odo create nodejs mynode`
type DirectCreateMethod struct{}
func (dcm DirectCreateMethod) CheckConflicts(co *CreateOptions, args []string) error {
return nil
}
func (dcm DirectCreateMethod) FetchDevfileAndCreateComponent(co *CreateOptions, cmdline cmdline.Cmdline, args []string) error {
catalogDevfileList, err := validateAndFetchRegistry(co.devfileMetadata.devfileRegistry.Name)
if err != nil {
return err
}
// SET METADATA
// The first argument passed will always be considered as component type
co.devfileMetadata.componentType = args[0]
co.devfileName = args[0]
var componentName string
if len(args) == 2 {
// If more than one argument is passed, then the second one will be considered as component name
componentName = args[1]
} else {
// If only component type is passed, then the component name will be created by odo
componentName, err = createDefaultComponentName(
co.devfileMetadata.componentType,
co.contextFlag,
co.clientset.PreferenceClient,
)
if err != nil {
return err
}
}
co.devfileMetadata.componentName = componentName
co.devfileMetadata.devfileLink, co.devfileMetadata.devfileRegistry, err = findDevfileFromRegistry(catalogDevfileList, co.devfileMetadata.devfileRegistry.Name, co.devfileMetadata.componentType)
if err != nil {
return err
}
return fetchDevfileFromRegistry(co.devfileMetadata.devfileRegistry, co.devfileMetadata.devfileLink, co.DevfilePath, co.devfileMetadata.componentType, co.contextFlag, co.clientset.PreferenceClient)
}
func (dcm DirectCreateMethod) Rollback(devfile, componentContext string) {
deleteDevfile(devfile)
deleteOdoDir(componentContext)
}
// UserCreatedDevfileMethod is used when a devfile is present in the context directory
type UserCreatedDevfileMethod struct{}
func (ucdm UserCreatedDevfileMethod) CheckConflicts(co *CreateOptions, args []string) error {
// More than one arguments should not be allowed when a devfile exists
if len(args) > 1 {
return &DevfileExistsExtraArgsError{len(args)}
}
//Check if the directory already contains a devfile when --devfile flag is passed
if co.devfileMetadata.devfilePath.value != "" && !dfutil.PathEqual(co.DevfilePath, co.devfileMetadata.devfilePath.value) {
return &DevfileExistsDevfileFlagError{}
}
return nil
}
func (ucdm UserCreatedDevfileMethod) FetchDevfileAndCreateComponent(co *CreateOptions, cmdline cmdline.Cmdline, args []string) error {
// Existing devfile Mode; co.devfileName = ""
devfileAbsolutePath, err := filepath.Abs(co.DevfilePath)
if err != nil {
return err
}
devfileSpinner := log.Spinnerf("odo will create a devfile component from the existing devfile: %s", devfileAbsolutePath)
defer devfileSpinner.End(true)
co.devfileMetadata.componentName, co.devfileMetadata.componentType, err = getMetadataForExistingDevfile(co, args)
return err
}
func (ucdm UserCreatedDevfileMethod) Rollback(devfile, componentContext string) {
deleteOdoDir(componentContext)
}
// HTTPCreateMethod is used when --devfile flag is used with a remote file; `odo create --devfile https://example.com/devfile.yaml`
type HTTPCreateMethod struct{}
func (hcm HTTPCreateMethod) CheckConflicts(co *CreateOptions, args []string) error {
return conflictCheckForDevfileFlag(args, co.devfileMetadata.devfileRegistry.Name)
}
func (hcm HTTPCreateMethod) FetchDevfileAndCreateComponent(co *CreateOptions, cmdline cmdline.Cmdline, args []string) error {
devfileSpinner := log.Spinnerf("Creating a devfile component from devfile path: %s", co.devfileMetadata.devfilePath.value)
defer devfileSpinner.End(false)
params := dfutil.HTTPRequestParams{
URL: co.devfileMetadata.devfilePath.value,
Token: co.devfileMetadata.token,
}
devfileData, err := util.DownloadFileInMemory(params)
if err != nil {
return fmt.Errorf("failed to download devfile for devfile component from %s: %w", co.devfileMetadata.devfilePath.value, err)
}
err = ioutil.WriteFile(co.DevfilePath, devfileData, 0644) // #nosec G306
if err != nil {
return fmt.Errorf("unable to save devfile to %s: %w", co.DevfilePath, err)
}
devfileSpinner.End(true)
// SET METADATA
co.devfileMetadata.devfilePath.protocol = "http(s)"
co.devfileMetadata.componentName, co.devfileMetadata.componentType, err = getMetadataForExistingDevfile(co, args)
return err
}
func (hcm HTTPCreateMethod) Rollback(devfile, componentContext string) {
deleteDevfile(devfile)
deleteOdoDir(componentContext)
}
// FileCreateMethod is used when --devfile flag is used with a local file; `odo create --devfile /tmp/comp/devfile.yaml`
type FileCreateMethod struct{}
func (fcm FileCreateMethod) CheckConflicts(co *CreateOptions, args []string) error {
return conflictCheckForDevfileFlag(args, co.devfileMetadata.devfileRegistry.Name)
}
func (fcm FileCreateMethod) FetchDevfileAndCreateComponent(co *CreateOptions, cmdline cmdline.Cmdline, args []string) error {
devfileAbsolutePath, err := filepath.Abs(co.devfileMetadata.devfilePath.value)
if err != nil {
return err
}
devfileSpinner := log.Spinnerf("Creating a devfile component from devfile path: %s", devfileAbsolutePath)
defer devfileSpinner.End(false)
devfileData, err := ioutil.ReadFile(co.devfileMetadata.devfilePath.value)
if err != nil {
return fmt.Errorf("failed to read devfile from %s: %w", co.devfileMetadata.devfilePath, err)
}
err = ioutil.WriteFile(co.DevfilePath, devfileData, 0644) // #nosec G306
if err != nil {
return fmt.Errorf("unable to save devfile to %s: %w", co.DevfilePath, err)
}
devfileSpinner.End(true)
// SET METADATA
co.devfileMetadata.devfilePath.protocol = "file"
co.devfileMetadata.componentName, co.devfileMetadata.componentType, err = getMetadataForExistingDevfile(co, args)
return err
}
func (fcm FileCreateMethod) Rollback(devfile, componentContext string) {
deleteDevfile(devfile)
deleteOdoDir(componentContext)
}
// conflictCheckForDevfileFlag checks for the common conflicts while using --devfile flag
func conflictCheckForDevfileFlag(args []string, registryName string) error {
// More than one arguments should not be allowed when --devfile is used
if len(args) > 1 {
return &DevfileExistsExtraArgsError{len(args)}
}
// Check if both --devfile and --registry flag are used, in which case raise an error
if registryName != "" {
return &DevfileFlagWithRegistryFlagError{}
}
return nil
}
// validateAndFetchRegistry validates if the provided registryName exists and returns the devfile listed in the registy;
// if the registryName is "", then it returns devfiles of all the available registries
func validateAndFetchRegistry(registryName string) (registry.DevfileStackList, error) {
// TODO(feloy) Get from DI
prefClient, err := preference.NewClient()
if err != nil {
odoutil.LogErrorAndExit(err, "unable to set preference, something is wrong with odo, kindly raise an issue at https://github.com/redhat-developer/odo/issues/new?template=Bug.md")
}
registryClient := registry.NewRegistryClient(filesystem.DefaultFs{}, prefClient)
// Validate if the component type is available
if registryName != "" {
registryExistSpinner := log.Spinnerf("Checking if the registry %q exists", registryName)
defer registryExistSpinner.End(false)
registryList, e := registryClient.GetDevfileRegistries(registryName)
if e != nil {
return registry.DevfileStackList{}, fmt.Errorf("failed to get registry: %w", e)
}
if len(registryList) == 0 {
return registry.DevfileStackList{}, fmt.Errorf("registry %s doesn't exist, please specify a valid registry via --registry", registryName)
}
registryExistSpinner.End(true)
}
klog.V(4).Infof("Fetching the available devfile components")
// Get available devfile components for checking devfile compatibility
catalogDevfileList, err := registryClient.ListDevfileStacks(registryName)
if err != nil {
return registry.DevfileStackList{}, err
}
if registryName != "" && catalogDevfileList.Items == nil {
return registry.DevfileStackList{}, fmt.Errorf("can't create devfile component from registry %s", registryName)
}
if len(catalogDevfileList.DevfileRegistries) == 0 {
return registry.DevfileStackList{}, errors.New("Registry is empty, please run `odo registry add <registry name> <registry URL>` to add a registry\n")
}
return catalogDevfileList, nil
}
// findDevfileFromRegistry finds the devfile and returns necessary information related to it
func findDevfileFromRegistry(catalogDevfileList registry.DevfileStackList, registryName, componentType string) (devfileLink string, devfileRegistry registry.Registry, err error) {
devfileExistSpinner := log.Spinnerf("Checking if the devfile for %q exists on available registries", componentType)
defer devfileExistSpinner.End(false)
if registryName != "" {
devfileExistSpinner = log.Spinnerf("Checking if the devfile for %q exists on registry %q", componentType, registryName)
}
// Find the request devfile from the registry
for _, devfileComponent := range catalogDevfileList.Items {
if componentType == devfileComponent.Name {
devfileExistSpinner.End(true)
return devfileComponent.Link, devfileComponent.Registry, nil
}
}
return "", registry.Registry{}, fmt.Errorf("devfile component type %q is not supported, please run `odo catalog list components` for a list of supported devfile component types", componentType)
}
// fetchDevfileFromRegistry fetches the required devfile from the list catalogDevfileList
func fetchDevfileFromRegistry(registry registry.Registry, devfileLink, devfilePath, componentType, componentContext string, prefClient preference.Client) (err error) {
// Download devfile from registry
registrySpinner := log.Spinnerf("Creating a devfile component from registry %q", registry.Name)
defer registrySpinner.End(false)
// For GitHub based registries
if registryUtil.IsGitBasedRegistry(registry.URL) {
registryUtil.PrintGitRegistryDeprecationWarning()
params := dfutil.HTTPRequestParams{
URL: registry.URL + devfileLink,
}
secure := registryUtil.IsSecure(prefClient, registry.Name)
if secure {
var token string
token, err = keyring.Get(fmt.Sprintf("%s%s", dfutil.CredentialPrefix, registry.Name), registryUtil.RegistryUser)
if err != nil {
return fmt.Errorf("unable to get secure registry credential from keyring: %w", err)
}
params.Token = token
}
devfileData, err := util.DownloadFileInMemoryWithCache(params, prefClient.GetRegistryCacheTime())
if err != nil {
return err
}
err = ioutil.WriteFile(devfilePath, devfileData, 0644) // #nosec G306
if err != nil {
return err
}
} else {
err := registryLibrary.PullStackFromRegistry(registry.URL, componentType, componentContext, segment.GetRegistryOptions())
if err != nil {
return err
}
}
registrySpinner.End(true)
return nil
}
// getMetadataForExistingDevfile sets metadata for a user provided devfile; UserCreatedDevfileCreateMethod, HTTPCreateMethod, and FileCreateMethod
func getMetadataForExistingDevfile(co *CreateOptions, args []string) (componentName, componentType string, err error) {
devObj, err := devfileParseFromFile(co.DevfilePath, false)
if err != nil {
return "", "", err
}
componentType = component.GetComponentTypeFromDevfileMetadata(devObj.Data.GetMetadata())
// Set component name
if len(args) > 0 {
// user provided name: `odo create mynode`
componentName = args[0]
} else {
if devObj.GetMetadataName() != "" {
// devfile provided name: `.metadata.name`
componentName = devObj.GetMetadataName()
} else {
// default name
componentName, err = createDefaultComponentName(co.devfileMetadata.componentType, co.contextFlag, co.clientset.PreferenceClient)
if err != nil {
return "", "", err
}
}
}
return
}
// createDefaultComponentName creates a default unique component name with the help of component context
func createDefaultComponentName(componentType string, sourcePath string, prefClient preference.Client) (string, error) {
var finalSourcePath string
var err error
if sourcePath != "" {
finalSourcePath, err = filepath.Abs(sourcePath)
} else {
finalSourcePath, err = os.Getwd()
}
if err != nil {
return "", err
}
return component.GetDefaultComponentName(
prefClient,
finalSourcePath,
componentType,
component.ComponentList{},
)
}
// deleteDevfile deletes the devfile if it exists in case of rollback
func deleteDevfile(devfile string) {
if util.CheckPathExists(devfile) {
_ = os.Remove(devfile)
}
}
//deleteOdoDir deletes the .odo directory in case of rollback
func deleteOdoDir(componentContext string) {
odoDir := filepath.Join(componentContext, ".odo")
if util.CheckPathExists(odoDir) {
_ = dfutil.DeletePath(odoDir)
}
}

View File

@@ -1,27 +0,0 @@
package component
import (
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/redhat-developer/odo/pkg/odo/cli/component/ui"
)
// getStarterProjectInteractiveMode gets starter project value by asking user in interactive mode.
func getStarterProjectInteractiveMode(projects []devfilev1.StarterProject) *devfilev1.StarterProject {
projectName := ui.SelectStarterProject(projects)
// if user do not wish to download starter project or there are no projects in devfile, project name would be empty
if projectName == "" {
return nil
}
var project devfilev1.StarterProject
for _, value := range projects {
if value.Name == projectName {
project = value
break
}
}
return &project
}

View File

@@ -37,7 +37,10 @@ var _ = Describe("odo devfile supported tests", func() {
})
createStarterProjAndSetDebug := func(component, starter, debugLocalPort string) {
helper.Cmd("odo", "create", component, "--starter", starter, "--project", commonVar.Project, componentName, "--context", projectDirPath).ShouldPass()
workingDir := helper.Getwd()
defer helper.Chdir(workingDir)
helper.Chdir(projectDirPath)
helper.Cmd("odo", "init", "--name", componentName, "--devfile", component, "--starter", starter).ShouldPass()
helper.Cmd("odo", "push", "--context", projectDirPath).ShouldPass()
helper.Cmd("odo", "push", "--debug", "--context", projectDirPath).ShouldPass()

View File

@@ -1,46 +0,0 @@
apiVersion: 1.0.0
metadata:
name: nodejs
starterProjects:
- name: nodejs-starter
subDir: /app/
git:
remotes:
origin: "https://github.com/che-samples/web-nodejs-sample.git"
components:
- type: dockerimage
image: registry.access.redhat.com/ubi8/nodejs-12:1-36
endpoints:
- name: "3000-tcp"
port: 3000
alias: runtime
env:
- name: FOO
value: "bar"
memoryLimit: 1024Mi
mountSources: true
commands:
- name: build
actions:
- type: exec
component: runtime
command: npm install
workdir: ${PROJECTS_ROOT}
- name: devbuild
actions:
- type: exec
component: runtime
command: npm install
workdir: ${PROJECTS_ROOT}
- name: run
actions:
- type: exec
component: runtime
command: npm start
workdir: ${PROJECTS_ROOT}
- name: devrun
actions:
- type: exec
component: runtime
command: npm start
workdir: ${PROJECTS_ROOT}

View File

@@ -0,0 +1,50 @@
schemaVersion: 2.0.0
metadata:
name: nodejs
starterProjects:
- name: nodejs-starter
git:
remotes:
origin: "https://github.com/odo-devfiles/nodejs-ex.git"
checkoutFrom:
revision: 0.0.1
components:
- name: runtime
container:
image: registry.access.redhat.com/ubi8/nodejs-12:1-36
memoryLimit: 1024Mi
endpoints:
- name: "3000-tcp"
targetPort: 3000
mountSources: true
commands:
- id: devbuild
exec:
component: runtime
commandLine: npm install
workingDir: ${PROJECTS_ROOT}
group:
kind: build
isDefault: true
- id: build
exec:
component: runtime
commandLine: npm install
workingDir: ${PROJECTS_ROOT}
group:
kind: build
- id: devrun
exec:
component: runtime
commandLine: npm start
workingDir: ${PROJECTS_ROOT}
group:
kind: run
isDefault: true
- id: run
exec:
component: runtime
commandLine: npm start
workingDir: ${PROJECTS_ROOT}
group:
kind: run

View File

@@ -1,9 +1,12 @@
package helper
import (
"fmt"
"io/ioutil"
"path/filepath"
. "github.com/onsi/gomega"
"github.com/redhat-developer/odo/pkg/envinfo"
)
@@ -17,3 +20,17 @@ func LocalEnvInfo(context string) *envinfo.EnvSpecificInfo {
}
return info
}
// CreateLocalEnv creates a .odo/env/env.yaml file
// Useful for commands that require this file and cannot create one on their own, for e.g. url, list
func CreateLocalEnv(context, compName, projectName string) {
var config = fmt.Sprintf(`
ComponentSettings:
Name: %s
Project: %s
AppName: app
`, compName, projectName)
dir := filepath.Join(context, ".odo", "env")
MakeDir(dir)
Expect(ioutil.WriteFile(filepath.Join(dir, "env.yaml"), []byte(config), 0600)).To(BeNil())
}

View File

@@ -274,18 +274,6 @@ func VerifyFileExists(filename string) bool {
return !info.IsDir()
}
// VerifyFilesExist receives an array of paths to files, and returns whether
// or not they all exist. If any one of the expected files doesn't exist, it
// returns false
func VerifyFilesExist(path string, files []string) bool {
for _, f := range files {
if !VerifyFileExists(filepath.Join(path, f)) {
return false
}
}
return true
}
// ReplaceDevfileField replaces the value of a given field in a specified
// devfile.
// Currently only the first match of the field is replaced. i.e if the

View File

@@ -457,27 +457,8 @@ type ResourceInfo struct {
Namespace string
}
func VerifyResourcesDeleted(runner CliRunner, resources []ResourceInfo) {
for _, item := range resources {
runner.VerifyResourceDeleted(item)
}
}
func VerifyResourcesToBeDeleted(runner CliRunner, resources []ResourceInfo) {
for _, item := range resources {
runner.VerifyResourceToBeDeleted(item)
}
}
func SetDefaultDevfileRegistryAsStaging() {
const registryName string = "DefaultDevfileRegistry"
const addRegistryURL string = "https://registry.stage.devfile.io"
Cmd("odo", "preference", "registry", "update", registryName, addRegistryURL, "-f").ShouldPass()
}
// CopyAndCreate copies required source code and devfile to the given context directory, and creates a component
func CopyAndCreate(sourcePath, devfilePath, contextDir string) {
CopyExample(sourcePath, contextDir)
CopyExampleDevFile(devfilePath, filepath.Join(contextDir, "devfile.yaml"))
Cmd("odo", "create", "--context", contextDir).ShouldPass()
}

View File

@@ -114,7 +114,7 @@ var _ = Describe("odo preference and config command tests", func() {
})
It("should not prompt when user calls for help", func() {
output := helper.Cmd("odo", "create", "--help").ShouldPass().Out()
output := helper.Cmd("odo", "init", "--help").ShouldPass().Out()
Expect(output).ToNot(ContainSubstring(promptMessageSubString))
})
@@ -132,9 +132,17 @@ var _ = Describe("odo preference and config command tests", func() {
Context("When ConsentTelemetry preference value is set", func() {
// !! Do not test with true because it sends out the telemetry data and messes up the statistics !!
var workingDir string
BeforeEach(func() {
workingDir = helper.Getwd()
helper.Chdir(commonVar.Context)
})
AfterEach(func() {
helper.Chdir(workingDir)
})
It("should not prompt the user", func() {
helper.Cmd("odo", "preference", "set", "ConsentTelemetry", "false", "-f").ShouldPass()
output := helper.Cmd("odo", "create", "--context", commonVar.Context, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-registry.yaml")).ShouldPass().Out()
output := helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-registry.yaml")).ShouldPass().Out()
Expect(output).ToNot(ContainSubstring(promptMessageSubString))
})
})

View File

@@ -36,7 +36,6 @@ var _ = Describe("odo delete command tests", func() {
deploymentName = "my-component"
serviceName = "my-cs"
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
helper.Cmd("odo", "project", "set", commonVar.Project).ShouldPass()
helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-deploy-with-multiple-resources.yaml")).ShouldPass()
// Note: component will be automatically bootstrapped when `odo dev` or `odo deploy` is run
})
@@ -236,7 +235,6 @@ ComponentSettings:
// Hardcoded names from devfile-with-valid-events.yaml
cmpName = "nodejs"
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
helper.Cmd("odo", "project", "set", commonVar.Project).ShouldPass()
helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-valid-events.yaml")).ShouldPass()
session := helper.CmdRunner("odo", "dev")
defer session.Kill()

View File

@@ -47,7 +47,6 @@ var _ = Describe("odo dev command tests", func() {
When("a component is bootstrapped and pushed", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
helper.Cmd("odo", "project", "set", commonVar.Project).ShouldPass()
helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
Expect(helper.VerifyFileExists(".odo/env/env.yaml")).To(BeFalse())
})

View File

@@ -1,11 +1,11 @@
package devfile
import (
"path"
"path/filepath"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/redhat-developer/odo/tests/helper"
)
@@ -27,9 +27,8 @@ var _ = Describe("odo devfile build-images command tests", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-outerloop.yaml"), path.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "create").ShouldPass()
helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-outerloop.yaml")).ShouldPass()
helper.CreateLocalEnv(commonVar.Context, "aname", commonVar.Project)
})
It("should run odo build-images without push", func() {
stdout := helper.Cmd("odo", "build-images").AddEnv("PODMAN_CMD=echo").ShouldPass().Out()
@@ -46,8 +45,8 @@ var _ = Describe("odo devfile build-images command tests", func() {
When("using a devfile.yaml containing an Image component with Dockerfile args", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-outerloop-args.yaml"), path.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "create").ShouldPass()
helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-outerloop-args.yaml")).ShouldPass()
helper.CreateLocalEnv(commonVar.Context, "aname", commonVar.Project)
})
It("should use args to build image when running odo build-images", func() {

View File

@@ -1,425 +0,0 @@
package devfile
import (
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"github.com/redhat-developer/odo/tests/helper"
"github.com/tidwall/gjson"
"gopkg.in/yaml.v2"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/redhat-developer/odo/pkg/util"
)
var _ = Describe("odo devfile create command tests", func() {
const devfile = "devfile.yaml"
const envFile = ".odo/env/env.yaml"
var contextDevfile, cmpName, devfilePath string
var commonVar helper.CommonVar
var _ = BeforeEach(func() {
cmpName = helper.RandString(6)
commonVar = helper.CommonBeforeEach()
helper.Chdir(commonVar.Context)
})
var _ = AfterEach(func() {
helper.CommonAfterEach(commonVar)
})
// checkNodeJSDirContent checks if the required nodejs files are present in the context directory after odo create
var checkNodeJSDirContent = func(contextDir string) {
expectedFiles := []string{"package.json", "package-lock.json", "README.md", devfile}
Expect(helper.VerifyFilesExist(contextDir, expectedFiles)).To(Equal(true))
}
It("should check that .odo/env exists in gitignore", func() {
helper.Cmd("odo", "create", "nodejs", "--project", commonVar.Project, cmpName).ShouldPass()
ignoreFilePath := filepath.Join(commonVar.Context, ".gitignore")
helper.FileShouldContainSubstring(ignoreFilePath, filepath.Join(".odo", "env"))
})
It("should successfully create the devfile component with valid component name", func() {
helper.Cmd("odo", "create", "java-openliberty", cmpName).ShouldPass()
By("checking that component name and language is set correctly in the devfile", func() {
metadata := helper.GetMetadataFromDevfile(filepath.Join(commonVar.Context, "devfile.yaml"))
Expect(metadata.Name).To(BeEquivalentTo(cmpName))
Expect(metadata.Language).To(ContainSubstring("java"))
})
})
It("should fail to create the devfile component with invalid component type", func() {
fakeComponentName := "fake-component"
output := helper.Cmd("odo", "create", fakeComponentName).ShouldFail().Err()
expectedString := "component type \"" + fakeComponentName + "\" is not supported"
Expect(output).To(ContainSubstring(expectedString))
})
It("should successfully create the devfile component with --project flag", func() {
componentNamespace := helper.RandString(6)
helper.Cmd("odo", "create", "java-openliberty", "--project", componentNamespace).ShouldPass()
fileContents, err := helper.ReadFile(filepath.Join(commonVar.Context, ".odo/env/env.yaml"))
Expect(err).To(BeNil())
Expect(fileContents).To(ContainSubstring(componentNamespace))
})
When("odo create is executed with the --registry flag", func() {
It("should successfully create the devfile component if specified registry is valid", func() {
componentRegistry := "DefaultDevfileRegistry"
helper.Cmd("odo", "create", "java-openliberty", "--registry", componentRegistry).ShouldPass()
})
It("should fail to create the devfile component if specified registry is invalid", func() {
componentRegistry := "fake"
output := helper.Cmd("odo", "create", "java-openliberty", "--registry", componentRegistry).ShouldFail().Err()
helper.MatchAllInOutput(output, []string{"registry fake doesn't exist, please specify a valid registry via --registry"})
})
})
When("odo create is executed with the --context flag", func() {
var newContext, envFilePath string
BeforeEach(func() {
newContext = filepath.Join(commonVar.Context, "newContext")
helper.MakeDir(newContext)
devfilePath = filepath.Join(newContext, devfile)
envFilePath = filepath.Join(newContext, envFile)
helper.CopyExample(filepath.Join("source", "nodejs"), newContext)
})
AfterEach(func() {
helper.DeleteDir(newContext)
})
It("should successfully create the devfile component in the context", func() {
helper.Cmd("odo", "create", "nodejs", "--context", newContext).ShouldPass()
By("checking the devfile and env file exists", func() {
Expect(util.CheckPathExists(devfilePath)).Should(BeTrue())
Expect(util.CheckPathExists(envFilePath)).Should(BeTrue())
})
})
It("should successfully create the devfile component and download the source in the context when used with --starter flag", func() {
helper.Cmd("odo", "create", "nodejs", "--starter", "nodejs-starter", "--context", newContext).ShouldPass()
checkNodeJSDirContent(newContext)
})
It("should successfully create the devfile component and show json output", func() {
output := helper.Cmd("odo", "create", "nodejs", "--context", newContext, "-o", "json").ShouldPass().Out()
values := gjson.GetMany(output, "kind", "metadata.name", "status.state")
Expect(helper.GjsonMatcher(values, []string{"Component", "nodejs", "Not Pushed"})).To(Equal(true))
})
It("should successfully create and push the devfile component with --now and show json output", func() {
output := helper.Cmd("odo", "create", "nodejs", "--starter", "nodejs-starter", "--context", newContext, "-o", "json", "--now").ShouldPass().Out()
checkNodeJSDirContent(newContext)
helper.MatchAllInOutput(output, []string{"Pushed", "nodejs", "Component"})
})
It("should successfully create the devfile component and show json output for non connected cluster", func() {
output := helper.Cmd("odo", "create", "nodejs", "--context", newContext, "-o", "json").WithEnv("KUBECONFIG=/no/such/path", "GLOBALODOCONFIG="+os.Getenv("GLOBALODOCONFIG")).ShouldPass().Out()
values := gjson.GetMany(output, "kind", "metadata.name", "status.state")
Expect(helper.GjsonMatcher(values, []string{"Component", "nodejs", "Unknown"})).To(Equal(true))
})
When("the cluster is unreachable", func() {
var newKubeConfigPath string
BeforeEach(func() {
path := os.Getenv("KUBECONFIG")
// read the contents from the kubeconfig and replace the server entries
reg := regexp.MustCompile(`server: .*`)
kubeConfigContents, err := helper.ReadFile(path)
Expect(err).To(BeNil())
kubeConfigContents = reg.ReplaceAllString(kubeConfigContents, "server: https://not-reachable.com:443")
// write to a new file which will be used as the new kubeconfig
newKubeConfigPath = filepath.Join(commonVar.Context, "newKUBECONFIG")
newKubeConfig, err := os.Create(newKubeConfigPath)
Expect(err).To(BeNil())
defer newKubeConfig.Close()
_, err = newKubeConfig.WriteString(kubeConfigContents)
Expect(err).To(BeNil())
})
AfterEach(func() {
os.Remove(newKubeConfigPath)
})
It("should successfully create the devfile component and show json output", func() {
output := helper.Cmd("odo", "create", "nodejs", "--context", newContext, "-o", "json").WithEnv("KUBECONFIG="+newKubeConfigPath, "GLOBALODOCONFIG="+os.Getenv("GLOBALODOCONFIG")).ShouldPass().Out()
values := gjson.GetMany(output, "kind", "metadata.name", "status.state")
Expect(helper.GjsonMatcher(values, []string{"Component", "nodejs", "Unknown"})).To(Equal(true))
})
})
})
When("odo create is executed with the --now flag", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context)
})
It("checks that odo push works with a devfile with now flag", func() {
output := helper.Cmd("odo", "create", "nodejs", "--now").ShouldPass().Out()
Expect(output).To(ContainSubstring("Changes successfully pushed to component"))
})
})
When("odo create is executed with the --starter flag", func() {
BeforeEach(func() {
contextDevfile = helper.CreateNewContext()
helper.Chdir(contextDevfile)
devfilePath = filepath.Join(contextDevfile, devfile)
})
AfterEach(func() {
helper.Chdir(commonVar.Context)
helper.DeleteDir(contextDevfile)
})
It("should successfully create the component and download the source", func() {
helper.Cmd("odo", "create", "nodejs", "--starter", "nodejs-starter").ShouldPass()
checkNodeJSDirContent(contextDevfile)
})
It("should fail to create the component when an invalid starter project is specified", func() {
invalidProjectName := "invalid-project-name"
output := helper.Cmd("odo", "create", "nodejs", "--starter=invalid-project-name").ShouldFail().Err()
expectedString := "the project: " + invalidProjectName + " specified in --starter does not exist"
helper.MatchAllInOutput(output, []string{expectedString, "available projects", "nodejs-starter"})
})
When("the starter project has git tag or git branch specified", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-branch.yaml"), devfilePath)
})
It("should successfully create the component and download the source from the specified branch", func() {
helper.Cmd("odo", "create", "nodejs", "--starter", "nodejs-starter").ShouldPass()
checkNodeJSDirContent(contextDevfile)
})
It("should successfully create the component and download the source from the specified tag", func() {
helper.ReplaceString(devfilePath, "revision: test-branch", "revision: 0.0.1")
helper.Cmd("odo", "create", "nodejs", "--starter", "nodejs-starter").ShouldPass()
checkNodeJSDirContent(contextDevfile)
})
})
When("the starter project has subDir", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "springboot", "devfile-with-subDir.yaml"), devfilePath)
helper.CopyExample(filepath.Join("source", "devfiles", "springboot", "project"), commonVar.Context)
})
It("should successfully create the component and extract the project in the specified subDir path", func() {
var found, notToBeFound int
helper.Cmd("odo", "create", "java-springboot", "--project", commonVar.Project, "--starter", "springbootproject").ShouldPass()
pathsToValidate := map[string]bool{
filepath.Join(contextDevfile, "java", "com"): true,
filepath.Join(contextDevfile, "java", "com", "example"): true,
filepath.Join(contextDevfile, "java", "com", "example", "demo"): true,
filepath.Join(contextDevfile, "java", "com", "example", "demo", "DemoApplication.java"): true,
filepath.Join(contextDevfile, "resources", "application.properties"): true,
}
pathsNotToBePresent := map[string]bool{
filepath.Join(contextDevfile, "src"): true,
filepath.Join(contextDevfile, "main"): true,
}
err := filepath.Walk(contextDevfile, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if ok := pathsToValidate[path]; ok {
found++
}
if ok := pathsNotToBePresent[path]; ok {
notToBeFound++
}
return nil
})
Expect(err).To(BeNil())
Expect(found).To(Equal(len(pathsToValidate)))
Expect(notToBeFound).To(Equal(0))
})
})
})
When("devfile exists in the working directory", func() {
BeforeEach(func() {
devfilePath = filepath.Join(commonVar.Context, devfile)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", devfile), devfilePath)
})
It("should successfully create the devfile component", func() {
helper.Cmd("odo", "create", "nodejs").ShouldPass()
})
It("should successfully create the devfile component with --devfile points to the same devfile", func() {
helper.Cmd("odo", "create", "nodejs", "--devfile", "./devfile.yaml").ShouldPass()
fileIsEmpty, err := helper.FileIsEmpty("./devfile.yaml")
Expect(err).Should(BeNil())
Expect(fileIsEmpty).Should(BeFalse())
})
It("should fail to create the devfile component", func() {
By("passing more than 1 arguments", func() {
helper.Cmd("odo", "create", "nodejs", "mynode").ShouldFail()
})
By("invalid value to the --devfile flag", func() {
helper.Cmd("odo", "create", "nodejs", "--devfile", "/path/to/file").ShouldFail()
})
By("creating the devfile component multiple times", func() {
helper.Cmd("odo", "create", "nodejs").ShouldPass()
output := helper.Cmd("odo", "create", "nodejs").ShouldFail().Err()
Expect(output).To(ContainSubstring("this directory already contains a component"))
})
})
When("devfile contains parent URI", func() {
var originalKeyList []string
var content map[string]interface{}
BeforeEach(func() {
var err error
devfilePath = filepath.Join(commonVar.Context, devfile)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-parent.yaml"), devfilePath)
originalDevfileContent, err := ioutil.ReadFile(devfilePath)
Expect(err).To(BeNil())
Expect(yaml.Unmarshal(originalDevfileContent, &content)).To(BeNil())
for k := range content {
originalKeyList = append(originalKeyList, k)
}
})
It("should not replace the original devfile", func() {
helper.Cmd("odo", "create").ShouldPass()
devfileContent, err := ioutil.ReadFile(devfilePath)
Expect(err).To(BeNil())
Expect(yaml.Unmarshal(devfileContent, &content)).To(BeNil())
for k := range content {
Expect(k).To(BeElementOf(originalKeyList))
}
})
})
})
When("devfile does not exist in the working directory and user specifies the devfile path via --devfile", func() {
BeforeEach(func() {
newContext := path.Join(commonVar.Context, "newContext")
devfilePath = filepath.Join(newContext, devfile)
helper.MakeDir(newContext)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", devfile), devfilePath)
})
It("should successfully create the devfile component with valid file system path", func() {
helper.Cmd("odo", "create", "nodejs", "--devfile", devfilePath).ShouldPass()
})
It("should successfully create the devfile component with valid specifies URL path", func() {
helper.Cmd("odo", "create", "nodejs", "--devfile", "https://raw.githubusercontent.com/odo-devfiles/registry/master/devfiles/nodejs/devfile.yaml").ShouldPass()
})
It("should fail to create the devfile component", func() {
By("using an invalid file system path", func() {
errOut := helper.Cmd("odo", "create", "nodejs", "--devfile", "@123!").ShouldFail().Err()
Expect(errOut).To(ContainSubstring("the devfile path you specify is invalid"))
})
By("using an invalid URL path", func() {
errOut := helper.Cmd("odo", "create", "nodejs", "--devfile", "://www.example.com/").ShouldFail().Err()
Expect(errOut).To(ContainSubstring("the devfile path you specify is invalid"))
})
By("passing more than 1 arguments", func() {
errOut := helper.Cmd("odo", "create", "nodejs", "nodejs", "--devfile", devfilePath).ShouldFail().Err()
Expect(errOut).To(ContainSubstring("accepts between 0 and 1 arg when using existing devfile, received 2"))
})
By("using --registry flag", func() {
errOut := helper.Cmd("odo", "create", "nodejs", "--devfile", devfilePath, "--registry", "DefaultDevfileRegistry").ShouldFail().Err()
Expect(errOut).To(ContainSubstring("you can't specify registry via --registry if you want to use the devfile that is specified via --devfile"))
})
})
When("devfile contains parent URI", func() {
var originalKeyList []string
var content map[string]interface{}
BeforeEach(func() {
var err error
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-parent.yaml"), devfilePath)
originalDevfileContent, err := ioutil.ReadFile(devfilePath)
Expect(err).To(BeNil())
err = yaml.Unmarshal(originalDevfileContent, &content)
Expect(err).To(BeNil())
for k := range content {
originalKeyList = append(originalKeyList, k)
}
})
It("should not replace the original devfile", func() {
helper.Cmd("odo", "create", "mycomp", "--devfile", devfilePath).ShouldPass()
devfileContent, err := ioutil.ReadFile(filepath.Join(commonVar.Context, devfile))
Expect(err).To(BeNil())
var content map[string]interface{}
err = yaml.Unmarshal(devfileContent, &content)
Expect(err).To(BeNil())
for k := range content {
Expect(k).To(BeElementOf(originalKeyList))
}
})
})
})
When("a dangling env file exists in the working directory", func() {
BeforeEach(func() {
helper.Cmd("odo", "create", "java-quarkus").ShouldPass()
helper.DeleteFile("devfile.yaml")
})
It("should successfully create a devfile component and remove the dangling env file", func() {
out, outerr := helper.Cmd("odo", "create", "nodejs").ShouldPass().OutAndErr()
helper.MatchAllInOutput(out, []string{
"Please use `odo push` command to create the component with source deployed"})
helper.MatchAllInOutput(outerr, []string{
"Found a dangling env file without a devfile, overwriting it",
})
})
})
When("creating a component using .devfile.yaml", func() {
var stdout string
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, ".devfile.yaml"))
stdout = helper.Cmd("odo", "create", cmpName, "--project", commonVar.Project).ShouldPass().Out()
})
It("should successfully create a devfile component", func() {
Expect(stdout).To(ContainSubstring("Please use `odo push` command to create the component with source deployed"))
})
})
When("there is already a devfile in the directory", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
})
It("should fail with appropriate error when --starter is given to odo create", func() {
stderr := helper.Cmd("odo", "create", "--starter", "nodejs-starter", "--context", commonVar.Context, "--project", commonVar.Project).ShouldFail().Err()
Expect(stderr).To(ContainSubstring("already has a devfile so you cannot provide a starter"))
})
})
When("a devfile is provided which has a starter that has its own devfile", func() {
BeforeEach(func() {
examplesPath := helper.GetExamplePath()
helper.Cmd("odo", "create", "nodejs", "--project", commonVar.Project, "--context", commonVar.Context, "--starter", "nodejs-starter", "--devfile", filepath.Join(examplesPath, "source", "devfiles", "nodejs", "devfile-with-starter-with-devfile.yaml")).ShouldPass()
})
It("should pass and keep the devfile in starter", func() {
devfileContent, err := helper.ReadFile(filepath.Join(commonVar.Context, "devfile.yaml"))
Expect(err).To(Not(HaveOccurred()))
helper.MatchAllInOutput(devfileContent, []string{"2.2.0", "outerloop-deploy", "deployk8s", "outerloop-build"})
})
})
})

View File

@@ -1,14 +1,16 @@
package devfile
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/redhat-developer/odo/tests/helper"
"gopkg.in/yaml.v2"
"github.com/redhat-developer/odo/tests/helper"
)
var _ = Describe("odo devfile init command tests", func() {
@@ -35,6 +37,9 @@ var _ = Describe("odo devfile init command tests", func() {
files := helper.ListFilesInDir(commonVar.Context)
Expect(len(files)).To(Equal(0))
})
By("using an invalid devfile name", func() {
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "invalid").ShouldFail()
})
By("running odo init in a directory containing a devfile.yaml", func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-registry.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
defer os.Remove(filepath.Join(commonVar.Context, "devfile.yaml"))
@@ -44,7 +49,7 @@ var _ = Describe("odo devfile init command tests", func() {
By("running odo init in a directory containing a .devfile.yaml", func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-registry.yaml"), filepath.Join(commonVar.Context, ".devfile.yaml"))
defer os.Remove(filepath.Join(commonVar.Context, ".devfile.yaml"))
defer helper.DeleteFile(filepath.Join(commonVar.Context, ".devfile.yaml"))
err := helper.Cmd("odo", "init").ShouldFail().Err()
Expect(err).To(ContainSubstring("a devfile already exists in the current directory"))
})
@@ -57,17 +62,36 @@ var _ = Describe("odo devfile init command tests", func() {
err := helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", "https://github.com/path/to/devfile.yaml").ShouldFail().Err()
Expect(err).To(ContainSubstring("unable to download devfile"))
})
By("running odo init multiple times", func() {
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs").ShouldPass()
defer helper.DeleteFile(filepath.Join(commonVar.Context, "devfile.yaml"))
output := helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs").ShouldFail().Err()
Expect(output).To(ContainSubstring("a devfile already exists in the current directory"))
})
By("running odo init with --devfile-path and --devfile-registry", func() {
errOut := helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", "https://github.com/path/to/devfile.yaml", "--devfile-registry", "DefaultDevfileRegistry").ShouldFail().Err()
Expect(errOut).To(ContainSubstring("--devfile-registry parameter cannot be used with --devfile-path"))
})
By("running odo init with invalid --devfile-registry value", func() {
fakeRegistry := "fake"
errOut := helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", "https://github.com/path/to/devfile.yaml", "--devfile-registry", fakeRegistry).ShouldFail().Err()
Expect(errOut).To(ContainSubstring(fmt.Sprintf("%q not found", fakeRegistry)))
})
})
Context("running odo init with valid flags", func() {
When("using --devfile flag", func() {
compName := "aname"
BeforeEach(func() {
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "go").ShouldPass().Out()
helper.Cmd("odo", "init", "--name", compName, "--devfile", "go").ShouldPass().Out()
})
It("should download a devfile.yaml file", func() {
It("should download a devfile.yaml file and correctly set the component name in it", func() {
files := helper.ListFilesInDir(commonVar.Context)
Expect(files).To(Equal([]string{"devfile.yaml"}))
metadata := helper.GetMetadataFromDevfile(filepath.Join(commonVar.Context, "devfile.yaml"))
Expect(metadata.Name).To(BeEquivalentTo(compName))
})
})
When("using --devfile-path flag with a local devfile", func() {
@@ -95,9 +119,81 @@ var _ = Describe("odo devfile init command tests", func() {
Expect(files).To(Equal([]string{"devfile.yaml"}))
})
})
When("using --devfile-registry flag", func() {
It("should successfully run odo init if specified registry is valid", func() {
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "go", "--devfile-registry", "DefaultDevfileRegistry").ShouldPass()
})
})
})
When("a dangling env file exists in the working directory", func() {
BeforeEach(func() {
helper.CreateLocalEnv(commonVar.Context, "aname", commonVar.Project)
})
It("should successfully create a devfile component and remove the dangling env file", func() {
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "go").ShouldPass()
})
})
When("a devfile is provided which has a starter that has its own devfile", func() {
BeforeEach(func() {
helper.Cmd("odo", "init", "--name", "aname", "--starter", "nodejs-starter", "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-starter-with-devfile.yaml")).ShouldPass()
})
It("should pass and keep the devfile in starter", func() {
devfileContent, err := helper.ReadFile(filepath.Join(commonVar.Context, "devfile.yaml"))
Expect(err).To(Not(HaveOccurred()))
helper.MatchAllInOutput(devfileContent, []string{"2.2.0", "outerloop-deploy", "deployk8s", "outerloop-build"})
})
})
When("running odo init with a devfile that has a subDir starter project", func() {
BeforeEach(func() {
helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "springboot", "devfile-with-subDir.yaml"), "--starter", "springbootproject").ShouldPass()
})
It("should successfully extract the project in the specified subDir path", func() {
var found, notToBeFound int
pathsToValidate := map[string]bool{
filepath.Join(commonVar.Context, "java", "com"): true,
filepath.Join(commonVar.Context, "java", "com", "example"): true,
filepath.Join(commonVar.Context, "java", "com", "example", "demo"): true,
filepath.Join(commonVar.Context, "java", "com", "example", "demo", "DemoApplication.java"): true,
filepath.Join(commonVar.Context, "resources", "application.properties"): true,
}
pathsNotToBePresent := map[string]bool{
filepath.Join(commonVar.Context, "src"): true,
filepath.Join(commonVar.Context, "main"): true,
}
err := filepath.Walk(commonVar.Context, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if ok := pathsToValidate[path]; ok {
found++
}
if ok := pathsNotToBePresent[path]; ok {
notToBeFound++
}
return nil
})
Expect(err).To(BeNil())
Expect(found).To(Equal(len(pathsToValidate)))
Expect(notToBeFound).To(Equal(0))
})
})
It("should successfully run odo init for devfile with starter project from the specified branch", func() {
helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-branch.yaml"), "--starter", "nodejs-starter").ShouldPass()
expectedFiles := []string{"package.json", "package-lock.json", "README.md", "devfile.yaml", "test"}
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements(expectedFiles))
})
It("should successfully run odo init for devfile with starter project from the specified tag", func() {
helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-tag.yaml"), "--starter", "nodejs-starter").ShouldPass()
expectedFiles := []string{"package.json", "package-lock.json", "README.md", "devfile.yaml", "app"}
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements(expectedFiles))
})
When("running odo init from a directory with sources", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context)

View File

@@ -5,9 +5,10 @@ import (
"path/filepath"
devfilepkg "github.com/devfile/api/v2/pkg/devfile"
"github.com/tidwall/gjson"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/tests/helper"
"github.com/tidwall/gjson"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -32,9 +33,9 @@ var _ = Describe("odo list with devfile", func() {
When("a component created in 'app' application", func() {
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, cmpName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
helper.CreateLocalEnv(commonVar.Context, cmpName, commonVar.Project)
})
It("should show the component as 'Not Pushed'", func() {
@@ -52,7 +53,9 @@ var _ = Describe("odo list with devfile", func() {
Context("devfile has missing metadata", func() {
// Note: We will be using SpringBoot example here because it helps to distinguish between language and projectType.
// In terms of SpringBoot, spring is the projectType and java is the language; see https://github.com/redhat-developer/odo/issues/4815
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "devfiles", "springboot", "project"), commonVar.Context)
})
var metadata devfilepkg.DevfileMetadata
// checkList checks the list output (both normal and json) to see if it contains the expected componentType
@@ -69,7 +72,8 @@ var _ = Describe("odo list with devfile", func() {
When("projectType is missing", func() {
BeforeEach(func() {
helper.CopyAndCreate(filepath.Join("source", "devfiles", "springboot", "project"), filepath.Join("source", "devfiles", "springboot", "devfile-with-missing-projectType-metadata.yaml"), commonVar.Context)
helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "springboot", "devfile-with-missing-projectType-metadata.yaml")).ShouldPass()
helper.CreateLocalEnv(commonVar.Context, "aname", commonVar.Project)
metadata = helper.GetMetadataFromDevfile(filepath.Join(commonVar.Context, "devfile.yaml"))
})
@@ -93,7 +97,8 @@ var _ = Describe("odo list with devfile", func() {
When("projectType and language is missing", func() {
BeforeEach(func() {
helper.CopyAndCreate(filepath.Join("source", "devfiles", "springboot", "project"), filepath.Join("source", "devfiles", "springboot", "devfile-with-missing-projectType-and-language-metadata.yaml"), commonVar.Context)
helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "springboot", "devfile-with-missing-projectType-and-language-metadata.yaml")).ShouldPass()
helper.CreateLocalEnv(commonVar.Context, "aname", commonVar.Project)
metadata = helper.GetMetadataFromDevfile(filepath.Join(commonVar.Context, "devfile.yaml"))
})
It("should show 'Not available' for 'Type' in odo list", func() {

View File

@@ -1,931 +0,0 @@
package devfile
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/redhat-developer/odo/pkg/util"
"github.com/redhat-developer/odo/tests/helper"
"github.com/redhat-developer/odo/tests/integration/devfile/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("odo devfile push command tests", func() {
var cmpName string
var sourcePath = "/projects"
var commonVar helper.CommonVar
// This is run before every Spec (It)
var _ = BeforeEach(func() {
commonVar = helper.CommonBeforeEach()
cmpName = helper.RandString(6)
helper.Chdir(commonVar.Context)
})
// This is run after every Spec (It)
var _ = AfterEach(func() {
helper.CommonAfterEach(commonVar)
})
When("creating a nodejs component", func() {
output := ""
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, cmpName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-registry.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
})
When("setting git config and running odo push", func() {
remoteURL := "https://github.com/odo-devfiles/nodejs-ex"
BeforeEach(func() {
helper.Cmd("git", "init").ShouldPass()
remote := "origin"
helper.Cmd("git", "remote", "add", remote, remoteURL).ShouldPass()
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
It("checks that odo push works with a devfile", func() {
Expect(output).To(ContainSubstring("Changes successfully pushed to component"))
})
It("check annotations from the deployment after odo push", func() {
annotations := commonVar.CliRunner.GetAnnotationsDeployment(cmpName, "app", commonVar.Project)
var valueFound bool
for key, value := range annotations {
if key == "app.openshift.io/vcs-uri" && value == remoteURL {
valueFound = true
}
}
Expect(valueFound).To(BeTrue())
})
When("updating a variable into devfile", func() {
BeforeEach(func() {
helper.ReplaceString("devfile.yaml", "name: FOO", "name: BAR")
})
It("should run odo push successfully", func() {
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
})
})
When("odo push is executed with json output", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push", "-o", "json", "--project", commonVar.Project).ShouldPass().Out()
})
It("should display push output in JSON format", func() {
utils.AnalyzePushConsoleOutput(output)
})
When("update devfile and push again", func() {
BeforeEach(func() {
helper.ReplaceString("devfile.yaml", "name: FOO", "name: BAR")
output = helper.Cmd("odo", "push", "-o", "json", "--project", commonVar.Project).ShouldPass().Out()
})
It("should display push updated output in JSON format", func() {
utils.AnalyzePushConsoleOutput(output)
})
})
})
When("running odo push outside the context directory", func() {
newContext := ""
BeforeEach(func() {
newContext = helper.CreateNewContext()
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.Chdir(newContext)
output = helper.Cmd("odo", "push", "--context", commonVar.Context).ShouldPass().Out()
})
AfterEach(func() {
helper.Chdir(commonVar.Context)
helper.DeleteDir(newContext)
})
It("should push correct component based on --context flag", func() {
Expect(output).To(ContainSubstring("Changes successfully pushed to component"))
})
})
When("Devfile 2.1.0 is used", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-variables.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
})
When("doing odo push", func() {
BeforeEach(func() {
output = helper.Cmd("odo", "push").ShouldPass().Out()
})
It("should pass", func() {
Expect(output).To(ContainSubstring("Changes successfully pushed to component"))
})
It("should check if the env variable has a correct value", func() {
envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(cmpName, "app", commonVar.Project)
// check if the env variable has a correct value. This value was substituted from in devfile from variable
Expect(envVars["FOO"]).To(Equal("bar"))
})
})
})
When("doing odo push", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
When("doing odo push with -f flag", func() {
BeforeEach(func() {
output = helper.Cmd("odo", "push", "-f", "--project", commonVar.Project).ShouldPass().Out()
})
It("should build even when no changes are detected", func() {
Expect(output).To(Not(ContainSubstring("No file changes detected, skipping build")))
})
})
When("the pod is deleted and replaced", func() {
BeforeEach(func() {
oldPod := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
commonVar.CliRunner.DeletePod(oldPod, commonVar.Project)
Eventually(func() bool {
newPod := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
return newPod != "" && newPod != oldPod
}, 180, 10).Should(Equal(true))
newPod := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
commonVar.CliRunner.PodsShouldBeRunning(commonVar.Project, newPod)
})
It("should execute run command on odo push", func() {
output = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
})
When("the Deployment's Replica count (pods) is scaled to 0", func() {
BeforeEach(func() {
commonVar.CliRunner.ScalePodToZero(cmpName, "app", commonVar.Project)
})
It("odo push should successfully recreate the pod", func() {
helper.Cmd("odo", "push").ShouldPass()
})
})
})
When("creating local files and dir and doing odo push", func() {
var newDirPath, newFilePath, stdOut, podName string
BeforeEach(func() {
newFilePath = filepath.Join(commonVar.Context, "foobar.txt")
newDirPath = filepath.Join(commonVar.Context, "testdir")
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
// Create a new file that we plan on deleting later...
if err := helper.CreateFileWithContent(newFilePath, "hello world"); err != nil {
fmt.Printf("the foobar.txt file was not created, reason %v", err.Error())
}
// Create a new directory
helper.MakeDir(newDirPath)
helper.Cmd("odo", "push", "--project", commonVar.Project, "-v4").ShouldPass()
})
It("should correctly propagate changes to the container", func() {
// Check to see if it's been pushed (foobar.txt abd directory testdir)
podName = commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, sourcePath)
helper.MatchAllInOutput(stdOut, []string{"foobar.txt", "testdir"})
})
When("deleting local files and dir and doing odo push again", func() {
BeforeEach(func() {
// Now we delete the file and dir and push
helper.DeleteDir(newFilePath)
helper.DeleteDir(newDirPath)
helper.Cmd("odo", "push", "--project", commonVar.Project, "-v4").ShouldPass()
})
It("should not list deleted dir and file in container", func() {
podName = commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
// Then check to see if it's truly been deleted
stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, sourcePath)
helper.DontMatchAllInOutput(stdOut, []string{"foobar.txt", "testdir"})
})
})
})
When("devfile has sourcemappings and doing odo push", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileSourceMapping.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
It("should sync files to the correct location", func() {
// Verify source code was synced to /test instead of /projects
var statErr error
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod(
podName,
"runtime",
commonVar.Project,
[]string{"stat", "/test/server.js"},
func(cmdOp string, err error) bool {
statErr = err
return err == nil
},
)
Expect(statErr).ToNot(HaveOccurred())
})
})
When("project and clonePath is present in devfile and doing odo push", func() {
BeforeEach(func() {
// devfile with clonePath set in project field
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "push", "--v", "5").ShouldPass()
})
It("should sync to the correct dir in container", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
// source code is synced to $PROJECTS_ROOT/clonePath
// $PROJECTS_ROOT is /projects by default, if sourceMapping is set it is same as sourceMapping
// for devfile-with-projects.yaml, sourceMapping is apps and clonePath is webapp
// so source code would be synced to /apps/webapp
output = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/apps/webapp")
helper.MatchAllInOutput(output, []string{"package.json"})
// Verify the sync env variables are correct
utils.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/apps/webapp", "/apps", commonVar.CliRunner)
})
})
When("devfile project field is present and doing odo push", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
// reset clonePath and change the workdir accordingly, it should sync to project name
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), "clonePath: webapp/", "# clonePath: webapp/")
helper.Cmd("odo", "push").ShouldPass()
})
It("should sync to the correct dir in container", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
output = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/apps/nodeshift")
helper.MatchAllInOutput(output, []string{"package.json"})
// Verify the sync env variables are correct
utils.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/apps/nodeshift", "/apps", commonVar.CliRunner)
})
})
When("multiple project is present", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-multiple-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "push").ShouldPass()
})
It("should sync to the correct dir in container", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
// for devfile-with-multiple-projects.yaml source mapping is not set so $PROJECTS_ROOT is /projects
// multiple projects, so source code would sync to the first project /projects/webapp
output = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects/webapp")
helper.MatchAllInOutput(output, []string{"package.json"})
// Verify the sync env variables are correct
utils.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/projects/webapp", "/projects", commonVar.CliRunner)
})
})
When("no project is present", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "push").ShouldPass()
})
It("should sync to the correct dir in container", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
output = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects")
helper.MatchAllInOutput(output, []string{"package.json"})
// Verify the sync env variables are correct
utils.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/projects", "/projects", commonVar.CliRunner)
})
})
When("doing odo push with devfile contain volume", func() {
var statErr error
var cmdOutput string
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-volumes.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
It("should create pvc and reuse if it shares the same devfile volume name", func() {
// Check to see if it's been pushed (foobar.txt abd directory testdir)
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod(
podName,
"runtime2",
commonVar.Project,
[]string{"cat", "/myvol/myfile.log"},
func(cmdOp string, err error) bool {
cmdOutput = cmdOp
statErr = err
return err == nil
},
)
Expect(statErr).ToNot(HaveOccurred())
Expect(cmdOutput).To(ContainSubstring("hello"))
commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod(
podName,
"runtime2",
commonVar.Project,
[]string{"stat", "/data2"},
func(cmdOp string, err error) bool {
statErr = err
return err == nil
},
)
Expect(statErr).ToNot(HaveOccurred())
})
It("check the volume name and mount paths for the containers", func() {
deploymentName, err := util.NamespaceKubernetesObject(cmpName, "app")
Expect(err).To(BeNil())
volumesMatched := false
// check the volume name and mount paths for the containers
volNamesAndPaths := commonVar.CliRunner.GetVolumeMountNamesandPathsFromContainer(deploymentName, "runtime", commonVar.Project)
volNamesAndPathsArr := strings.Fields(volNamesAndPaths)
for _, volNamesAndPath := range volNamesAndPathsArr {
volNamesAndPathArr := strings.Split(volNamesAndPath, ":")
if strings.Contains(volNamesAndPathArr[0], "myvol") && volNamesAndPathArr[1] == "/data" {
volumesMatched = true
}
}
Expect(volumesMatched).To(Equal(true))
})
})
When("doing odo push with devfile containing volume-component", func() {
BeforeEach(func() {
helper.RenameFile("devfile.yaml", "devfile-old.yaml")
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-volume-components.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
It("should successfully use the volume components in container components", func() {
// Verify the pvc size for firstvol
storageSize := commonVar.CliRunner.GetPVCSize(cmpName, "firstvol", commonVar.Project)
// should be the default size
Expect(storageSize).To(ContainSubstring("1Gi"))
// Verify the pvc size for secondvol
storageSize = commonVar.CliRunner.GetPVCSize(cmpName, "secondvol", commonVar.Project)
// should be the specified size in the devfile volume component
Expect(storageSize).To(ContainSubstring("3Gi"))
})
})
When("doing odo push and run command is not marked as hotReloadCapable", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push").ShouldPass().Out()
})
It("should restart the application", func() {
// TODO: this is almost the same test as one below
helper.Cmd("odo", "push", "-f").ShouldPass()
})
})
When("doing odo push and run command is marked as hotReloadCapable:true", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-hotReload.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push").ShouldPass().Out()
})
It("should not restart the application", func() {
helper.Cmd("odo", "push", "-f").ShouldPass()
})
When("doing odo push --debug ", func() {
stdOut := ""
BeforeEach(func() {
stdOut = helper.Cmd("odo", "push", "--debug", "--project", commonVar.Project).ShouldPass().Out()
})
It("should restart the application regardless of hotReloadCapable value", func() {
Expect(stdOut).To(Not(ContainSubstring("No file changes detected, skipping build")))
})
})
})
When("doing odo push and devfile with composite command", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeCommands.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push").ShouldPass().Out()
})
It("should execute all commands in composite commmand", func() {
// Verify the command executed successfully
var statErr error
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod(
podName,
"runtime",
commonVar.Project,
[]string{"stat", "/projects/testfolder"},
func(cmdOp string, err error) bool {
statErr = err
return err == nil
},
)
Expect(statErr).ToNot(HaveOccurred())
})
})
When("doing odo push and composite command is marked as paralell:true ", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeCommandsParallel.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push", "--build-command", "buildandmkdir").ShouldPass().Out()
})
It("should execute all commands in composite commmand", func() {
// Verify the command executed successfully
var statErr error
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod(
podName,
"runtime",
commonVar.Project,
[]string{"stat", "/projects/testfolder"},
func(cmdOp string, err error) bool {
statErr = err
return err == nil
},
)
Expect(statErr).ToNot(HaveOccurred())
})
})
When("doing odo push and composite command are nested", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileNestedCompCommands.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "push").ShouldPass()
})
It("should execute all commands in composite commmand", func() {
// Verify the command executed successfully
var statErr error
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod(
podName,
"runtime",
commonVar.Project,
[]string{"stat", "/projects/testfolder"},
func(cmdOp string, err error) bool {
statErr = err
return err == nil
},
)
Expect(statErr).ToNot(HaveOccurred())
})
})
When("doing odo push and composite command is used as a run command", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeRun.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push").ShouldFail().Err()
})
It("should throw a validation error for composite run commands", func() {
Expect(output).To(ContainSubstring("not supported currently"))
})
})
When("events are defined", func() {
It("should correctly execute PreStart commands", func() {
// expectedInitContainers := []string{"tools-myprestart-1", "tools-myprestart-2", "runtime-secondprestart-3"}
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-preStart.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldFail().Err()
// This is expected to fail for now.
// see https://github.com/redhat-developer/odo/issues/4187 for more info
helper.MatchAllInOutput(output, []string{"myprestart should either map to an apply command or a composite command with apply commands\n"})
/*
helper.MatchAllInOutput(output, []string{"PreStart commands have been added to the component"})
firstPushPodName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
firstPushInitContainers := commonVar.CliRunner.GetPodInitContainers(cmpName, commonVar.Project)
// 3 preStart events + 1 supervisord init containers
Expect(len(firstPushInitContainers)).To(Equal(4))
helper.MatchAllInOutput(strings.Join(firstPushInitContainers, ","), expectedInitContainers)
// Need to force so build and run get triggered again with the component already created.
output = helper.Cmd("odo", "push", "--project", commonVar.Project, "-f").ShouldPass().Out()
helper.MatchAllInOutput(output, []string{"PreStart commands have been added to the component"})
secondPushPodName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
secondPushInitContainers := commonVar.CliRunner.GetPodInitContainers(cmpName, commonVar.Project)
Expect(len(secondPushInitContainers)).To(Equal(4))
helper.MatchAllInOutput(strings.Join(secondPushInitContainers, ","), expectedInitContainers)
Expect(firstPushPodName).To(Equal(secondPushPodName))
Expect(firstPushInitContainers).To(Equal(secondPushInitContainers))
var statErr error
commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod(
firstPushPodName,
"runtime",
commonVar.Project,
[]string{"cat", "/projects/test.txt"},
func(cmdOp string, err error) bool {
if err != nil {
statErr = err
} else if cmdOp == "" {
statErr = fmt.Errorf("prestart event action error, expected: hello test2\nhello test2\nhello test\n, got empty string")
} else {
fileContents := strings.Split(cmdOp, "\n")
if len(fileContents)-1 != 3 {
statErr = fmt.Errorf("prestart event action count error, expected: 3 strings, got %d strings: %s", len(fileContents), strings.Join(fileContents, ","))
} else if cmdOp != "hello test2\nhello test2\nhello test\n" {
statErr = fmt.Errorf("prestart event action error, expected: hello test2\nhello test2\nhello test\n, got: %s", cmdOp)
}
}
return true
},
)
Expect(statErr).ToNot(HaveOccurred())
*/
})
It("should correctly execute PostStart commands", func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-valid-events.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
// Need to force so build and run get triggered again with the component already created.
helper.Cmd("odo", "push", "--project", commonVar.Project, "-f").ShouldPass()
})
})
When("doing odo push and using wrong custom commands (specified by flags)", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push", "--build-command", "buildgarbage").ShouldFail().Err()
})
It("should error out", func() {
Expect(output).NotTo(ContainSubstring("Executing buildgarbage command"))
Expect(output).To(ContainSubstring("the command \"%v\" is not found in the devfile", "buildgarbage"))
})
})
When("command has no group kind and doing odo push", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-no-group-kind.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
output = helper.Cmd("odo", "push", "--build-command", "devbuild", "--run-command", "devrun").ShouldPass().Out()
})
It("should execute commands with flags", func() {
helper.MatchAllInOutput(output, []string{
"Building your application in container on cluster",
})
})
})
When("doing odo push and run command throws an error", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), "npm start", "npm starts")
_, output = helper.Cmd("odo", "push").ShouldPass().OutAndErr()
})
It("should wait and error out with some log", func() {
helper.MatchAllInOutput(output, []string{
"exited with error status within 1 sec",
"Did you mean one of these?",
})
_, output = helper.Cmd("odo", "push", "-f", "--run-command", "run").ShouldPass().OutAndErr()
helper.MatchAllInOutput(output, []string{
"exited with error status within 1 sec",
"Did you mean one of these?",
})
})
})
When("commands specify have env variables", func() {
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-command-envs.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
})
When("doing odo push and sigle env var is set", func() {
BeforeEach(func() {
helper.Cmd("odo", "push", "--build-command", "buildwithenv", "--run-command", "singleenv").ShouldPass()
})
It("should be able to exec command", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
output = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, sourcePath)
helper.MatchAllInOutput(output, []string{"test_env_variable", "test_build_env_variable"})
})
})
When("doing odo push and multiple env variables are set", func() {
BeforeEach(func() {
output = helper.Cmd("odo", "push", "--build-command", "buildwithmultipleenv", "--run-command", "multipleenv").ShouldPass().Out()
})
It("should be able to exec command", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
output = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, sourcePath)
helper.MatchAllInOutput(output, []string{"test_build_env_variable1", "test_build_env_variable2", "test_env_variable1", "test_env_variable2"})
})
})
When("doing odo push and there is a env variable with spaces", func() {
BeforeEach(func() {
helper.Cmd("odo", "push", "--build-command", "buildenvwithspace", "--run-command", "envwithspace").ShouldPass()
})
It("should be able to exec command", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
output = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, sourcePath)
helper.MatchAllInOutput(output, []string{"build env variable with space", "env with space"})
})
})
})
})
Context("pushing devfile without an .odo folder", func() {
It("should error out on odo push and passing invalid devfile", func() {
helper.Cmd("odo", "push", "--project", commonVar.Project, "--devfile", "invalid.yaml").ShouldFail()
})
})
When("Create and push java-springboot component", func() {
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, cmpName, "--devfile", helper.GetExamplePath("source", "devfiles", "springboot", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "springboot", "project"), commonVar.Context)
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
It("should execute default build and run commands correctly", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
var statErr error
var cmdOutput string
commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod(
podName,
"runtime",
commonVar.Project,
// [s] to not match the current command: https://unix.stackexchange.com/questions/74185/how-can-i-prevent-grep-from-showing-up-in-ps-results
[]string{"bash", "-c", "grep [s]pring-boot:run /proc/*/cmdline"},
func(cmdOp string, err error) bool {
cmdOutput = cmdOp
statErr = err
return err == nil
},
)
Expect(statErr).ToNot(HaveOccurred())
Expect(cmdOutput).To(MatchRegexp("Binary file .* matches"))
})
})
Context("devfile is modified", func() {
// Tests https://github.com/redhat-developer/odo/issues/3838
ensureFilesSyncedTest := func(namespace string, shouldForcePush bool) {
output := ""
When("java-springboot application is created and pushed", func() {
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, cmpName, "--devfile", helper.GetExamplePath("source", "devfiles", "springboot", "devfile-registry.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "springboot", "project"), commonVar.Context)
fmt.Fprintf(GinkgoWriter, "Testing with force push %v", shouldForcePush)
output = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
It("should push the component successfully", func() {
Expect(output).To(ContainSubstring("Changes successfully pushed to component"))
})
When("Update the devfile.yaml, do odo push", func() {
BeforeEach(func() {
helper.ReplaceString("devfile.yaml", "memoryLimit: 768Mi", "memoryLimit: 769Mi")
commands := []string{"push", "-v", "4", "--project", commonVar.Project}
if shouldForcePush {
commands = append(commands, "-f")
}
output = helper.Cmd("odo", commands...).ShouldPass().Out()
})
It("Ensure the build passes", func() {
Expect(output).To(ContainSubstring("BUILD SUCCESS"))
})
When("compare the local and remote files", func() {
remoteFiles := []string{}
localFiles := []string{}
BeforeEach(func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, namespace)
output = commonVar.CliRunner.Exec(podName, namespace, "find", sourcePath)
outputArr := strings.Split(output, "\n")
for _, line := range outputArr {
if !strings.HasPrefix(line, sourcePath+"/") || strings.Contains(line, "lost+found") {
continue
}
newLine, err := filepath.Rel(sourcePath, line)
Expect(err).ToNot(HaveOccurred())
newLine = filepath.ToSlash(newLine)
if strings.HasPrefix(newLine, "target/") || newLine == "target" || strings.HasPrefix(newLine, ".") {
continue
}
remoteFiles = append(remoteFiles, newLine)
}
// 5) Acquire file from local context, filtering out .*
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
newPath := filepath.ToSlash(path)
if strings.HasPrefix(newPath, ".") {
return nil
}
localFiles = append(localFiles, newPath)
return nil
})
Expect(err).ToNot(HaveOccurred())
})
It("localFiles and remoteFiles should match", func() {
sort.Strings(localFiles)
sort.Strings(remoteFiles)
Expect(localFiles).To(Equal(remoteFiles))
})
})
})
})
}
Context("odo push -f is executed", func() {
ensureFilesSyncedTest(commonVar.Project, true)
})
Context("odo push (without -f) is executed", func() {
ensureFilesSyncedTest(commonVar.Project, false)
})
When("node-js application is created and pushed with devfile schema 2.2.0", func() {
var output string
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, cmpName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-MR-CL-CR.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
output = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
ensureResource := func(output, cpulimit, cpurequest, memoryrequest string) {
By("check for cpuLimit", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
bufferOutput := commonVar.CliRunner.Run("get", "pods", podName, "-o", "jsonpath='{.spec.containers[0].resources.limits.cpu}'").Out.Contents()
output = string(bufferOutput)
Expect(output).To(ContainSubstring(cpulimit))
})
By("check for cpuRequests", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
bufferOutput := commonVar.CliRunner.Run("get", "pods", podName, "-o", "jsonpath='{.spec.containers[0].resources.requests.cpu}'").Out.Contents()
output = string(bufferOutput)
Expect(output).To(ContainSubstring(cpurequest))
})
By("check for memoryRequests", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
bufferOutput := commonVar.CliRunner.Run("get", "pods", podName, "-o", "jsonpath='{.spec.containers[0].resources.requests.memory}'").Out.Contents()
output = string(bufferOutput)
Expect(output).To(ContainSubstring(memoryrequest))
})
}
It("should check cpuLimit,cpuRequests,memoryRequests", func() {
ensureResource(output, "1", "200m", "512Mi")
})
When("Update the devfile.yaml, do odo push", func() {
BeforeEach(func() {
helper.ReplaceString("devfile.yaml", "cpuLimit: \"1\"", "cpuLimit: 700m")
helper.ReplaceString("devfile.yaml", "cpuRequest: 200m", "cpuRequest: 250m")
helper.ReplaceString("devfile.yaml", "memoryRequest: 512Mi", "memoryRequest: 550Mi")
output = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
It("should check cpuLimit,cpuRequests,memoryRequests", func() {
ensureResource(output, "700m", "250m", "550Mi")
})
})
})
})
When("creating nodejs component, doing odo push and run command has dev.odo.push.path attribute", func() {
BeforeEach(func() {
helper.Cmd("odo", "create", cmpName, "--context", commonVar.Context, "--project", commonVar.Project, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-remote-attributes.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
// create a folder and file which shouldn't be pushed
helper.MakeDir(filepath.Join(commonVar.Context, "views"))
_, _ = helper.CreateSimpleFile(filepath.Join(commonVar.Context, "views"), "view", ".html")
helper.ReplaceString("package.json", "node server.js", "node server/server.js")
helper.Cmd("odo", "push", "--context", commonVar.Context).ShouldPass()
})
It("should push only the mentioned files at the appropriate remote destination", func() {
podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project)
stdOut := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, sourcePath)
helper.MatchAllInOutput(stdOut, []string{"package.json", "server"})
helper.DontMatchAllInOutput(stdOut, []string{"test", "views", "devfile.yaml"})
stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, sourcePath+"/server")
helper.MatchAllInOutput(stdOut, []string{"server.js", "test"})
helper.Cmd("odo", "push", "--context", commonVar.Context).ShouldPass()
})
})
Context("using OpenShift cluster", func() {
BeforeEach(func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
})
When("project with with 'default' name is used", func() {
It("should throw an error", func() {
componentName := helper.RandString(6)
helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context)
helper.Cmd("odo", "create", "--project", "default", componentName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
stdout := helper.Cmd("odo", "push").ShouldFail().Err()
helper.MatchAllInOutput(stdout, []string{"odo may not work as expected in the default project, please run the odo component in a non-default project"})
})
})
})
Context("using Kubernetes cluster", func() {
BeforeEach(func() {
if os.Getenv("KUBERNETES") != "true" {
Skip("This is a Kubernetes specific scenario, skipping")
}
})
When("project with with 'default' name is used", func() {
It("should push successfully", func() {
componentName := helper.RandString(6)
helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context)
helper.Cmd("odo", "create", "--project", "default", componentName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
stdout := helper.Cmd("odo", "push").ShouldPass().Out()
helper.DontMatchAllInOutput(stdout, []string{"odo may not work as expected in the default project"})
helper.Cmd("odo", "delete", "component", "-f", "--name", componentName, "--namespace", commonVar.Project).ShouldPass()
})
})
})
})

View File

@@ -61,7 +61,7 @@ var _ = Describe("odo devfile registry command tests", func() {
})
It("should pass, when doing odo create with --registry flag", func() {
helper.Cmd("odo", "create", "nodejs", "--registry", registryName).ShouldPass()
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs", "--devfile-registry", registryName).ShouldPass()
})
It("should fail, when adding same registry", func() {
@@ -80,7 +80,7 @@ var _ = Describe("odo devfile registry command tests", func() {
It("deleting registry and creating component with registry flag ", func() {
helper.Cmd("odo", "preference", "registry", "delete", registryName, "-f").ShouldPass()
helper.Cmd("odo", "create", "java-maven", "--registry", registryName).ShouldFail()
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "java-maven", "--devfile-registry", registryName).ShouldFail()
})
})
@@ -98,26 +98,27 @@ var _ = Describe("odo devfile registry command tests", func() {
When("adding git based registries", func() {
BeforeEach(func() {
out, err = helper.Cmd("odo", "preference", "registry", "add", "RegistryFromGitHub", "https://github.com/odo-devfiles/registry").ShouldPass().OutAndErr()
out, err = helper.Cmd("odo", "preference", "registry", "add", "RegistryFromGitHub", "https://github.com/devfile/registry").ShouldPass().OutAndErr()
})
It("should show deprication warning", func() {
It("should show deprecation warning", func() {
co = fmt.Sprintln(out, err)
helper.MatchAllInOutput(co, []string{deprecated, docLink})
By("odo resgistry list is executed, should show the warning", func() {
By("odo registry list is executed, should show the warning", func() {
out, err = helper.Cmd("odo", "preference", "registry", "list").ShouldPass().OutAndErr()
co = fmt.Sprintln(out, err)
helper.MatchAllInOutput(co, []string{deprecated, docLink})
})
By("should successfully delete registry", func() {
out, err = helper.Cmd("odo", "create", "nodejs", "--registry", "RegistryFromGitHub").ShouldPass().OutAndErr()
co = fmt.Sprintln(out, err)
helper.MatchAllInOutput(co, []string{deprecated, docLink})
})
// TODO: Should `odo init` support GitHub based registries
// By("should successfully delete registry", func() {
// out, err = helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs", "--devfile-registry", "RegistryFromGitHub").ShouldPass().OutAndErr()
// co = fmt.Sprintln(out, err)
// helper.MatchAllInOutput(co, []string{deprecated, docLink})
// })
})
It("should not show deprication warning if git registry is not used for component creation", func() {
out, err = helper.Cmd("odo", "create", "nodejs", "--registry", "DefaultDevfileRegistry").ShouldPass().OutAndErr()
It("should not show deprecation warning if git registry is not used for component creation", func() {
out, err = helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs", "--devfile-registry", "DefaultDevfileRegistry").ShouldPass().OutAndErr()
helper.DontMatchAllInOutput(fmt.Sprintln(out, err), []string{deprecated, docLink})
})
})

View File

@@ -1,452 +0,0 @@
package devfile
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/redhat-developer/odo/tests/helper"
"github.com/tidwall/gjson"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("odo devfile url command tests", func() {
var componentName string
var commonVar helper.CommonVar
url1 := helper.RandString(5)
url1Port := "5000"
url2 := helper.RandString(5)
url2Port := "9000"
// Required to ensure duplicate ports are not used to create a URL
nodejsDevfilePort := "3000"
nodejsDevfileURLName := "3000-tcp"
springbootDevfilePort := "8080"
portNumber := "3030"
host := helper.RandString(5) + ".com"
// This is run before every Spec (It)
var _ = BeforeEach(func() {
commonVar = helper.CommonBeforeEach()
componentName = helper.RandString(6)
helper.Chdir(commonVar.Context)
})
// This is run after every Spec (It)
var _ = AfterEach(func() {
helper.CommonAfterEach(commonVar)
})
It("should error out on devfile flag", func() {
helper.Cmd("odo", "url", "create", "mynodejs", "--devfile", "invalid.yaml").ShouldFail()
helper.Cmd("odo", "url", "delete", "mynodejs", "--devfile", "invalid.yaml").ShouldFail()
})
When("creating a Nodejs component", func() {
var stdout string
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, componentName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
})
It("should not allow creating an invalid host", func() {
stdout = helper.Cmd("odo", "url", "create", "--host", "https://127.0.0.1:60104", "--port", portNumber, "--ingress").ShouldFail().Err()
Expect(stdout).To(ContainSubstring("is not a valid host name"))
})
It("should not allow using tls secret if url is not secure", func() {
stdout = helper.Cmd("odo", "url", "create", "--tls-secret", "foo", "--port", portNumber, "--ingress").ShouldFail().Err()
Expect(stdout).To(ContainSubstring("TLS secret is only available for secure URLs of Ingress kind"))
})
It("should report multiple issues when it's the case", func() {
stdout = helper.Cmd("odo", "url", "create", "--host", "https://127.0.0.1:60104", "--tls-secret", "foo", "--port", portNumber, "--ingress").ShouldFail().Err()
Expect(stdout).To(And(ContainSubstring("is not a valid host name"), ContainSubstring("TLS secret is only available for secure URLs of Ingress kind")))
})
It("should not allow to create URL with duplicate port", func() {
stdout = helper.Cmd("odo", "url", "create", url1, "--port", nodejsDevfilePort).ShouldFail().Err()
Expect(stdout).To(ContainSubstring("port 3000 already exists in devfile endpoint entry"))
})
It("should not allow creating under an invalid container", func() {
containerName := helper.RandString(5)
stdout = helper.Cmd("odo", "url", "create", "--host", "com", "--port", portNumber, "--container", containerName, "--ingress").ShouldFail().Err()
helper.MatchAllInOutput(stdout, []string{"container", containerName, "not exist"})
})
It("should not allow creating an endpoint with same name", func() {
stdout = helper.Cmd("odo", "url", "create", nodejsDevfileURLName, "--host", "com", "--port", nodejsDevfilePort, "--ingress").ShouldFail().Err()
Expect(stdout).To(ContainSubstring(fmt.Sprintf("url %s already exists in devfile endpoint entry", nodejsDevfileURLName)))
})
When("creating ingress url1 with port flag and doing odo push", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--host", host, "--secure", "--ingress").ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
})
It("should check state of url1 list", func() {
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
helper.MatchAllInOutput(stdout, []string{url1, "Pushed", "true", "ingress"})
})
It("should take user provided url name", func() {
stdout = string(commonVar.CliRunner.Run("get", "svc", "--namespace", commonVar.Project, fmt.Sprintf("%v-%v", componentName, "app"), "-ojsonpath='{.spec.ports[*].name}'").Out.Contents())
Expect(stdout).To(ContainSubstring(url1))
})
When("creating ingress url2 with port flag and doing odo push", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url2, "--port", url2Port, "--host", host, "--ingress").ShouldPass()
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
})
It("should check state of url2", func() {
helper.MatchAllInOutput(stdout, []string{url2, "Not Pushed", "false", "ingress"})
})
When("deleting url1", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "delete", url1, "-f").ShouldPass()
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
})
It("should check status of url1 and url2", func() {
helper.MatchAllInOutput(stdout, []string{url1, "Locally Deleted", "true", "ingress"})
helper.MatchAllInOutput(stdout, []string{url2, "Not Pushed", "false", "ingress"})
})
})
})
})
When("creating a url with -o json", func() {
var createExpected []string
var createValues []gjson.Result
var createJSON string
BeforeEach(func() {
helper.Cmd("odo", "url", "delete", nodejsDevfileURLName, "-f").ShouldPass()
createJSON = helper.Cmd("odo", "url", "create", url1, "--port", nodejsDevfilePort, "--host", host, "--ingress", "-o", "json").ShouldPass().Out()
})
It("should validate machine readable output for url create", func() {
createValues = gjson.GetMany(createJSON, "kind", "metadata.name", "spec.port", "status.state")
createExpected = []string{"URL", url1, nodejsDevfilePort, "Not Pushed"}
Expect(helper.GjsonMatcher(createValues, createExpected)).To(Equal(true))
})
When("doing odo push", func() {
BeforeEach(func() {
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
It("validate machine readable output for url list", func() {
helper.WaitForCmdOut("odo", []string{"url", "list", "-o", "json"}, 1, true, func(output string) bool {
if strings.Contains(output, url1) {
values := gjson.GetMany(output, "kind", "items.0.kind", "items.0.metadata.name", "items.0.spec.host", "items.0.status.state")
expected := []string{"List", "URL", url1, url1, "Pushed"}
Expect(helper.GjsonMatcher(values, expected)).To(Equal(true))
return true
}
return false
})
})
})
})
XWhen("creating a URL without port flag", func() {
// marking it as pending, since we want to start using port number as a key to URLs in devfile,
// FIXME: and odo should not allow using url create without a port number, this should be taken care of in v3-alpha2
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--host", host, "--ingress").ShouldPass()
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
})
It("should create a URL without port flag if only one port exposed in devfile", func() {
helper.MatchAllInOutput(stdout, []string{url1, "3000", "Not Pushed"})
})
})
When("creating a secure URL and doing odo push", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--host", host, "--secure", "--ingress").ShouldPass()
stdout = helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass().Out()
})
It("should list secure URL", func() {
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
helper.MatchAllInOutput(stdout, []string{"https:", url1 + "." + host, "true"})
})
})
When("create with now flag", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--host", host, "--now", "--ingress").ShouldPass()
})
It("should check if url created for component", func() {
// check the env for the runMode
envOutput, err := helper.ReadFile(filepath.Join(commonVar.Context, ".odo/env/env.yaml"))
Expect(err).To(BeNil())
Expect(envOutput).To(ContainSubstring(" RunMode: run"))
})
})
When("creating a url and doing odo push twice", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--host", host, "--ingress").ShouldPass()
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
It("should be able to push again twice", func() {
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
When("deleting url and doing odo push twice", func() {
BeforeEach(func() {
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
helper.Cmd("odo", "url", "delete", url1, "-f").ShouldPass()
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
It("should be able to push again twice", func() {
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
})
})
When("executing odo outside of context", func() {
subFolderContext := ""
BeforeEach(func() {
subFolderContext = filepath.Join(commonVar.Context, helper.RandString(6))
helper.MakeDir(subFolderContext)
helper.Chdir(subFolderContext)
})
It("should fail if url is created from non context dir", func() {
stdout = helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--host", host, "--ingress").ShouldFail().Err()
Expect(stdout).To(ContainSubstring("The current directory does not represent an odo component"))
})
It("should fail if host flag not provided with ingress flag", func() {
stdout = helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--ingress", "--context", commonVar.Context).ShouldFail().Err()
Expect(stdout).To(ContainSubstring("host must be provided"))
})
When("creating url1,url2 and doing odo push with context flag", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url2, "--port", url2Port, "--host", host, "--ingress", "--context", commonVar.Context).ShouldPass()
helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--host", host, "--ingress", "--context", commonVar.Context).ShouldPass()
stdout = helper.Cmd("odo", "push", "--context", commonVar.Context).ShouldPass().Out()
})
When("doing url list", func() {
BeforeEach(func() {
stdout = helper.Cmd("odo", "url", "list", "--context", commonVar.Context).ShouldPass().Out()
})
It("should successfully push url1 and url2", func() {
helper.MatchAllInOutput(stdout, []string{url1, url2, "Pushed", "false", "ingress"})
})
})
})
})
})
When("creating a java-springboot component", func() {
var stdout string
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, componentName, "--devfile", helper.GetExamplePath("source", "devfiles", "springboot", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "springboot", "project"), commonVar.Context)
})
XWhen("create URLs under different container names with same port number", func() {
// marking it as pending, since the current behavior of odo url create is buggy, this should be fixed in odo v3-alpha2
BeforeEach(func() {
stdout = helper.Cmd("odo", "url", "create", url1, "--port", "8080", "--host", host, "--container", "tools", "--ingress").ShouldFail().Err()
})
It("should not create URLs under different container names with same port number", func() {
helper.MatchAllInOutput(stdout, []string{fmt.Sprintf("cannot set URL %s under container tools", url1), fmt.Sprintf("TargetPort %s is being used under container runtime", springbootDevfilePort)})
})
})
})
When("Creating nodejs component and url with .devfile.yaml", func() {
var stdout string
BeforeEach(func() {
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, ".devfile.yaml"))
helper.Cmd("odo", "create", "--project", commonVar.Project, componentName).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "springboot", "project"), commonVar.Context)
stdout = helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--host", host, "--container", "runtime", "--ingress").ShouldPass().Out()
})
It("should create url", func() {
helper.MatchAllInOutput(stdout, []string{url1, "created"})
})
When("listing url", func() {
BeforeEach(func() {
stdout = helper.Cmd("odo", "url", "list", "--context", commonVar.Context).ShouldPass().Out()
})
It("should list urls", func() {
helper.MatchAllInOutput(stdout, []string{url1, "Pushed", "false", "ingress"})
})
})
})
Context("Testing URLs for OpenShift specific scenarios", func() {
stdout := ""
BeforeEach(func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
})
When("creating a nodejs component", func() {
ingressurl := helper.RandString(5)
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, componentName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
})
It("should error out when a host is provided with a route on a openShift cluster", func() {
output := helper.Cmd("odo", "url", "create", url1, "--host", "com", "--port", portNumber).ShouldFail().Err()
Expect(output).To(ContainSubstring("host is not supported"))
})
When("creating multiple url with different state", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--port", url1Port, "--secure").ShouldPass()
helper.Cmd("odo", "url", "create", ingressurl, "--port", portNumber, "--host", host, "--ingress").ShouldPass()
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
helper.Cmd("odo", "url", "create", url2, "--port", url2Port).ShouldPass()
})
It("should list route and ingress urls with appropriate state", func() {
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
helper.MatchAllInOutput(stdout, []string{url1, "Pushed", "true", "route"})
helper.MatchAllInOutput(stdout, []string{url2, "Not Pushed", "false", "route"})
helper.MatchAllInOutput(stdout, []string{ingressurl, "Pushed", "false", "ingress"})
})
When("url1 is deleted locally", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "delete", url1, "-f").ShouldPass()
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
})
It("should list route and ingress urls with appropriate state", func() {
helper.MatchAllInOutput(stdout, []string{url1, "Locally Deleted", "true", "route"})
helper.MatchAllInOutput(stdout, []string{url2, "Not Pushed", "false", "route"})
helper.MatchAllInOutput(stdout, []string{ingressurl, "Pushed", "false", "ingress"})
})
})
})
XIt("should automatically create route on a openShift cluster", func() {
// marking it as pending, since current odo url create behavior is buggy, and this should be taken care of in v3-alpha2
helper.Cmd("odo", "url", "create", url1, "--port", nodejsDevfilePort).ShouldPass()
fileOutput, err := helper.ReadFile(filepath.Join(commonVar.Context, "devfile.yaml"))
Expect(err).To(BeNil())
helper.MatchAllInOutput(fileOutput, []string{nodejsDevfileURLName, nodejsDevfilePort})
})
When("doing odo push twice and doing url list", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--port", url1Port).ShouldPass()
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
})
It("should push successfully", func() {
Expect(stdout).Should(ContainSubstring(url1))
})
When("deleting the url1 and doing odo push twice and doing url list", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "delete", url1, "-f").ShouldPass()
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
})
It("should push successfully", func() {
Expect(stdout).ShouldNot(ContainSubstring(url1))
})
})
})
When("doing odo push", func() {
BeforeEach(func() {
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
})
It("should create a route on a openShift cluster without calling url create", func() {
Expect(stdout).Should(ContainSubstring("3000-tcp"))
})
})
})
})
When("creating a python component", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "python"), commonVar.Context)
helper.Chdir(commonVar.Context)
helper.Cmd("odo", "create", "--project", commonVar.Project, componentName, "--devfile", helper.GetExamplePath("source", "devfiles", "python", "devfile-registry.yaml")).ShouldPass()
})
When("creating a url and doing odo push", func() {
var output string
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--port", "5000", "--host", "com", "--ingress").ShouldPass()
helper.Cmd("odo", "push", "--project", commonVar.Project).ShouldPass()
})
It("should create a url for a unsupported devfile component", func() {
output = helper.Cmd("odo", "url", "list").ShouldPass().Out()
Expect(output).Should(ContainSubstring(url1))
})
})
})
XContext("Testing URLs for Kubernetes specific scenarios", func() {
// TODO: marking it as pending, since current odo url create behavior is buggy, and this should be taken care of in v3-alpha2
BeforeEach(func() {
if os.Getenv("KUBERNETES") != "true" {
Skip("This is a Kubernetes specific scenario, skipping")
}
})
When("creating a nodejs component", func() {
BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, componentName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
})
When("creating url", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", "--host", "com", "--port", nodejsDevfilePort).ShouldPass()
})
When("creating a second url", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "create", url1, "--host", "com", "--port", url1Port).ShouldPass()
})
It("should use an existing URL when there are URLs with no host defined in the env file with same port", func() {
fileOutput, err := helper.ReadFile(filepath.Join(commonVar.Context, "devfile.yaml"))
Expect(err).To(BeNil())
helper.MatchAllInOutput(fileOutput, []string{url1, url1Port})
count := strings.Count(fileOutput, "targetPort")
Expect(count).To(Equal(2))
})
})
It("should verify", func() {
fileOutput, err := helper.ReadFile(filepath.Join(commonVar.Context, "devfile.yaml"))
Expect(err).To(BeNil())
helper.MatchAllInOutput(fileOutput, []string{nodejsDevfileURLName, nodejsDevfilePort})
count := strings.Count(fileOutput, "targetPort")
Expect(count).To(Equal(1))
})
})
})
})
})

View File

@@ -1,60 +0,0 @@
package devfile
import (
"path/filepath"
. "github.com/onsi/ginkgo"
//We continued iterating on bracket pair guides. Horizontal lines now outline the scope of a bracket pair. Also, vertical lines now depend on the indentation of the code that is surrounded by the bracket pair.. "github.com/onsi/gomega"
"github.com/redhat-developer/odo/tests/helper"
)
var _ = Describe("Test suits to check .devfile.yaml compatibility", func() {
var cmpName string
var commonVar helper.CommonVar
BeforeEach(func() {
commonVar = helper.CommonBeforeEach()
cmpName = helper.RandString(6)
helper.Chdir(commonVar.Context)
})
AfterEach(func() {
helper.CommonAfterEach(commonVar)
})
When("Creating a nodejs component and replace devfile.yaml to .devfile.yaml", func() {
var _ = BeforeEach(func() {
helper.Cmd("odo", "create", "--project", commonVar.Project, cmpName, "--devfile", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
helper.Cmd("mv", "devfile.yaml", ".devfile.yaml").ShouldPass()
})
When("Creating url and doing odo push", func() {
var stdout, url1, host string
BeforeEach(func() {
url1 = helper.RandString(6)
host = helper.RandString(6)
helper.Cmd("odo", "url", "create", url1, "--port", "9090", "--host", host, "--secure", "--ingress").ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
})
It("should verify if url is created and pushed", func() {
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
helper.MatchAllInOutput(stdout, []string{url1, "Pushed", "true", "ingress"})
})
When("Deleting url doing odo push", func() {
BeforeEach(func() {
helper.Cmd("odo", "url", "delete", url1, "-f").ShouldPass()
})
It("should verify if url is created and pushed", func() {
stdout = helper.Cmd("odo", "url", "list").ShouldPass().Out()
helper.MatchAllInOutput(stdout, []string{url1, "Locally Deleted", "true", "ingress"})
})
})
})
})
})