mirror of
				https://github.com/redhat-developer/odo.git
				synced 2025-10-19 03:06:19 +03:00 
			
		
		
		
	Remove references to v2 commands, remove unused functions, and constants (#5638)
* Remove references to v2 commands, remove unused functions, and constants Signed-off-by: Parthvi Vala <pvala@redhat.com> * Dharmit's review Signed-off-by: Parthvi Vala <pvala@redhat.com>
This commit is contained in:
		| @@ -34,28 +34,6 @@ func GetComponentTypeFromDevfileMetadata(metadata devfile.DevfileMetadata) strin | ||||
| 	return componentType | ||||
| } | ||||
|  | ||||
| // GetProjectTypeFromDevfileMetadata returns component type from the devfile metadata | ||||
| func GetProjectTypeFromDevfileMetadata(metadata devfile.DevfileMetadata) string { | ||||
| 	var projectType string | ||||
| 	if metadata.ProjectType != "" { | ||||
| 		projectType = metadata.ProjectType | ||||
| 	} else { | ||||
| 		projectType = NotAvailable | ||||
| 	} | ||||
| 	return projectType | ||||
| } | ||||
|  | ||||
| // GetLanguageFromDevfileMetadata returns component type from the devfile metadata | ||||
| func GetLanguageFromDevfileMetadata(metadata devfile.DevfileMetadata) string { | ||||
| 	var language string | ||||
| 	if metadata.Language != "" { | ||||
| 		language = metadata.Language | ||||
| 	} else { | ||||
| 		language = NotAvailable | ||||
| 	} | ||||
| 	return language | ||||
| } | ||||
|  | ||||
| // GatherName parses the Devfile and retrieves an appropriate name in two ways. | ||||
| // 1. If metadata.name exists, we use it | ||||
| // 2. If metadata.name does NOT exist, we use the folder name where the devfile.yaml is located | ||||
|   | ||||
| @@ -177,7 +177,7 @@ func (do *DeleteComponentClient) ExecutePreStopEvents(devfileObj parser.DevfileO | ||||
|  | ||||
| 	klog.V(4).Infof("Executing %q event commands for component %q", libdevfile.PreStop, componentName) | ||||
| 	// ignore the failures if any; delete should not fail because preStop events failed to execute | ||||
| 	err = libdevfile.ExecPreStopEvents(devfileObj, componentName, component.NewExecHandler(do.kubeClient, pod.Name, false)) | ||||
| 	err = libdevfile.ExecPreStopEvents(devfileObj, component.NewExecHandler(do.kubeClient, pod.Name, false)) | ||||
| 	if err != nil { | ||||
| 		klog.V(4).Infof("Failed to execute %q event commands for component %q, cause: %v", libdevfile.PreStop, componentName, err.Error()) | ||||
| 	} | ||||
|   | ||||
| @@ -8,9 +8,6 @@ import ( | ||||
| // KubernetesInstanceLabel is a label key used to identify the component name | ||||
| const KubernetesInstanceLabel = "app.kubernetes.io/instance" | ||||
|  | ||||
| // KubernetesNameLabel is Kubernetes label that identifies the type of a component being used | ||||
| const KubernetesNameLabel = "app.kubernetes.io/name" | ||||
|  | ||||
| // KubernetesManagedByLabel ... | ||||
| const KubernetesManagedByLabel = "app.kubernetes.io/managed-by" | ||||
|  | ||||
| @@ -20,9 +17,6 @@ const ComponentDevName = "Dev" | ||||
| // ComponentDeployName ... | ||||
| const ComponentDeployName = "Deploy" | ||||
|  | ||||
| // ComponentNoneName ... | ||||
| const ComponentNoneName = "None" | ||||
|  | ||||
| // OdoModeLabel ... | ||||
| const OdoModeLabel = "odo.dev/mode" | ||||
|  | ||||
|   | ||||
| @@ -1,192 +0,0 @@ | ||||
| package component | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
|  | ||||
| 	componentlabels "github.com/redhat-developer/odo/pkg/component/labels" | ||||
| 	"github.com/redhat-developer/odo/pkg/kclient" | ||||
| 	"github.com/redhat-developer/odo/pkg/storage" | ||||
| 	v1 "k8s.io/api/apps/v1" | ||||
| 	v12 "k8s.io/api/core/v1" | ||||
| 	kerrors "k8s.io/apimachinery/pkg/api/errors" | ||||
| ) | ||||
|  | ||||
| type provider interface { | ||||
| 	GetLabels() map[string]string | ||||
| 	GetAnnotations() map[string]string | ||||
| 	GetName() string | ||||
| 	GetEnvVars() []v12.EnvVar | ||||
| 	GetLinkedSecrets() []SecretMount | ||||
| } | ||||
|  | ||||
| // PushedComponent is an abstraction over the cluster representation of the component | ||||
| type PushedComponent interface { | ||||
| 	provider | ||||
| 	GetApplication() string | ||||
| 	GetType() (string, error) | ||||
| 	GetStorage() ([]storage.Storage, error) | ||||
| } | ||||
|  | ||||
| type defaultPushedComponent struct { | ||||
| 	application   string | ||||
| 	storage       []storage.Storage | ||||
| 	provider      provider | ||||
| 	client        kclient.ClientInterface | ||||
| 	storageClient storage.Client | ||||
| } | ||||
|  | ||||
| func (d defaultPushedComponent) GetLabels() map[string]string { | ||||
| 	return d.provider.GetLabels() | ||||
| } | ||||
|  | ||||
| func (d defaultPushedComponent) GetAnnotations() map[string]string { | ||||
| 	return d.provider.GetAnnotations() | ||||
| } | ||||
|  | ||||
| func (d defaultPushedComponent) GetName() string { | ||||
| 	return d.provider.GetName() | ||||
| } | ||||
|  | ||||
| func (d defaultPushedComponent) GetType() (string, error) { | ||||
| 	return getType(d.provider) | ||||
| } | ||||
|  | ||||
| func (d defaultPushedComponent) GetEnvVars() []v12.EnvVar { | ||||
| 	return d.provider.GetEnvVars() | ||||
| } | ||||
|  | ||||
| func (d defaultPushedComponent) GetLinkedSecrets() []SecretMount { | ||||
| 	return d.provider.GetLinkedSecrets() | ||||
| } | ||||
|  | ||||
| // GetStorage gets the storage using the storage client of the given pushed component | ||||
| func (d defaultPushedComponent) GetStorage() ([]storage.Storage, error) { | ||||
| 	if d.storage == nil { | ||||
| 		if _, ok := d.provider.(*devfileComponent); ok { | ||||
| 			storageList, err := d.storageClient.ListFromCluster() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			d.storage = storageList.Items | ||||
| 		} | ||||
| 	} | ||||
| 	return d.storage, nil | ||||
| } | ||||
|  | ||||
| func (d defaultPushedComponent) GetApplication() string { | ||||
| 	return d.application | ||||
| } | ||||
|  | ||||
| type devfileComponent struct { | ||||
| 	d v1.Deployment | ||||
| } | ||||
|  | ||||
| func (d devfileComponent) GetLinkedSecrets() (secretMounts []SecretMount) { | ||||
| 	for _, container := range d.d.Spec.Template.Spec.Containers { | ||||
| 		for _, env := range container.EnvFrom { | ||||
| 			if env.SecretRef != nil { | ||||
| 				secretMounts = append(secretMounts, SecretMount{ | ||||
| 					SecretName:  env.SecretRef.Name, | ||||
| 					MountVolume: false, | ||||
| 				}) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for _, volume := range d.d.Spec.Template.Spec.Volumes { | ||||
| 		if volume.Secret != nil { | ||||
| 			mountPath := "" | ||||
| 			for _, container := range d.d.Spec.Template.Spec.Containers { | ||||
| 				for _, mount := range container.VolumeMounts { | ||||
| 					if mount.Name == volume.Name { | ||||
| 						mountPath = mount.MountPath | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			secretMounts = append(secretMounts, SecretMount{ | ||||
| 				SecretName:  volume.Secret.SecretName, | ||||
| 				MountVolume: true, | ||||
| 				MountPath:   mountPath, | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return secretMounts | ||||
| } | ||||
|  | ||||
| func (d devfileComponent) GetEnvVars() []v12.EnvVar { | ||||
| 	var envs []v12.EnvVar | ||||
| 	for _, container := range d.d.Spec.Template.Spec.Containers { | ||||
| 		envs = append(envs, container.Env...) | ||||
| 	} | ||||
| 	return envs | ||||
| } | ||||
|  | ||||
| func (d devfileComponent) GetLabels() map[string]string { | ||||
| 	return d.d.Labels | ||||
| } | ||||
| func (d devfileComponent) GetAnnotations() map[string]string { | ||||
| 	return d.d.Annotations | ||||
| } | ||||
|  | ||||
| func (d devfileComponent) GetName() string { | ||||
| 	return d.d.Labels[componentlabels.KubernetesInstanceLabel] | ||||
| } | ||||
|  | ||||
| func getType(component provider) (string, error) { | ||||
|  | ||||
| 	// For backwards compatibility with previously deployed components that could be non-odo, check the annotation first | ||||
| 	// then check to see if there is a label with the project type | ||||
| 	if componentType, ok := component.GetAnnotations()[componentlabels.OdoProjectTypeAnnotation]; ok { | ||||
| 		return componentType, nil | ||||
| 	} else if componentType, ok = component.GetLabels()[componentlabels.OdoProjectTypeAnnotation]; ok { | ||||
| 		return componentType, nil | ||||
| 	} | ||||
|  | ||||
| 	return "", fmt.Errorf("%s component doesn't provide a type annotation; consider pushing the component again", component.GetName()) | ||||
| } | ||||
|  | ||||
| func newPushedComponent(applicationName string, p provider, c kclient.ClientInterface, storageClient storage.Client) PushedComponent { | ||||
| 	return &defaultPushedComponent{ | ||||
| 		application:   applicationName, | ||||
| 		provider:      p, | ||||
| 		client:        c, | ||||
| 		storageClient: storageClient, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetPushedComponent returns an abstraction over the cluster representation of the component | ||||
| func GetPushedComponent(c kclient.ClientInterface, componentName, applicationName string) (PushedComponent, error) { | ||||
| 	d, err := c.GetOneDeployment(componentName, applicationName) | ||||
| 	if err != nil { | ||||
| 		if isIgnorableError(err) { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	storageClient := storage.NewClient(storage.ClientOptions{ | ||||
| 		Client:     c, | ||||
| 		Deployment: d, | ||||
| 	}) | ||||
|  | ||||
| 	return newPushedComponent(applicationName, &devfileComponent{d: *d}, c, storageClient), nil | ||||
| } | ||||
|  | ||||
| func isIgnorableError(err error) bool { | ||||
| 	for { | ||||
| 		e := errors.Unwrap(err) | ||||
| 		if e != nil { | ||||
| 			err = e | ||||
| 		} else { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := err.(*kclient.DeploymentNotFoundError); ok { | ||||
| 		return true | ||||
| 	} | ||||
| 	return kerrors.IsNotFound(err) || kerrors.IsForbidden(err) || kerrors.IsUnauthorized(err) | ||||
|  | ||||
| } | ||||
| @@ -1,10 +1,11 @@ | ||||
| package component | ||||
|  | ||||
| import ( | ||||
| 	"github.com/redhat-developer/odo/pkg/machineoutput" | ||||
| 	"github.com/redhat-developer/odo/pkg/storage" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/machineoutput" | ||||
| 	"github.com/redhat-developer/odo/pkg/storage" | ||||
| ) | ||||
|  | ||||
| const ComponentKind = "Component" | ||||
| @@ -111,24 +112,3 @@ func newComponentList(comps []Component) ComponentList { | ||||
| 		Items:    comps, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewCombinedComponentList returns list of devfile, s2i components and other components(not managed by odo) in machine readable format | ||||
| func NewCombinedComponentList(devfileComps []Component, otherComps []Component) CombinedComponentList { | ||||
|  | ||||
| 	if len(devfileComps) == 0 { | ||||
| 		devfileComps = []Component{} | ||||
| 	} | ||||
| 	if len(otherComps) == 0 { | ||||
| 		otherComps = []Component{} | ||||
| 	} | ||||
|  | ||||
| 	return CombinedComponentList{ | ||||
| 		TypeMeta: metav1.TypeMeta{ | ||||
| 			Kind:       machineoutput.ListKind, | ||||
| 			APIVersion: machineoutput.APIVersion, | ||||
| 		}, | ||||
| 		ListMeta:          metav1.ListMeta{}, | ||||
| 		DevfileComponents: devfileComps, | ||||
| 		OtherComponents:   otherComps, | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -52,7 +52,7 @@ func newDeployHandler(devfileObj parser.DevfileObj, path string, kubeClient kcli | ||||
|  | ||||
| // ApplyImage builds and pushes the OCI image to be used on Kubernetes | ||||
| func (o *deployHandler) ApplyImage(img v1alpha2.Component) error { | ||||
| 	return image.BuildPushSpecificImage(o.devfileObj, o.path, img, true) | ||||
| 	return image.BuildPushSpecificImage(o.path, img, true) | ||||
| } | ||||
|  | ||||
| // ApplyKubernetes applies inline Kubernetes YAML from the devfile.yaml file | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package common | ||||
|  | ||||
| import ( | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| @@ -9,9 +8,7 @@ import ( | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" | ||||
| 	devfileParser "github.com/devfile/library/pkg/devfile/parser" | ||||
| 	parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common" | ||||
| 	devfilefs "github.com/devfile/library/pkg/testingutil/filesystem" | ||||
| ) | ||||
|  | ||||
| // PredefinedDevfileCommands encapsulates constants for predefined devfile commands | ||||
| @@ -184,45 +181,3 @@ func GetSyncFilesFromAttributes(commandsMap PushCommandsMap) map[string]string { | ||||
| 	} | ||||
| 	return syncMap | ||||
| } | ||||
|  | ||||
| // RemoveDevfileURIContents removes contents | ||||
| // which are used via a URI in the devfile | ||||
| func RemoveDevfileURIContents(devfile devfileParser.DevfileObj, componentContext string) error { | ||||
| 	return removeDevfileURIContents(devfile, componentContext, devfilefs.DefaultFs{}) | ||||
| } | ||||
|  | ||||
| func removeDevfileURIContents(devfile devfileParser.DevfileObj, componentContext string, fs devfilefs.Filesystem) error { | ||||
| 	components, err := devfile.Data.GetComponents(parsercommon.DevfileOptions{}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, component := range components { | ||||
| 		var uri string | ||||
| 		if component.Kubernetes != nil && component.Kubernetes.Uri != "" { | ||||
| 			uri = component.Kubernetes.Uri | ||||
| 		} | ||||
|  | ||||
| 		if component.Openshift != nil && component.Openshift.Uri != "" { | ||||
| 			uri = component.Openshift.Uri | ||||
| 		} | ||||
|  | ||||
| 		if uri == "" { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		parsedURL, err := url.Parse(uri) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		if len(parsedURL.Host) != 0 && len(parsedURL.Scheme) != 0 { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		completePath := filepath.Join(componentContext, uri) | ||||
| 		err = fs.Remove(completePath) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ package common | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
|  | ||||
| @@ -12,8 +11,7 @@ import ( | ||||
| 	devfileParser "github.com/devfile/library/pkg/devfile/parser" | ||||
| 	parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common" | ||||
| 	"github.com/devfile/library/pkg/testingutil" | ||||
| 	devfileFileSystem "github.com/devfile/library/pkg/testingutil/filesystem" | ||||
| 	odotestingutil "github.com/redhat-developer/odo/pkg/testingutil" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
| ) | ||||
|  | ||||
| @@ -580,112 +578,3 @@ func TestGetCommandsFromEvent(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func Test_removeDevfileURIContents(t *testing.T) { | ||||
| 	fs := devfileFileSystem.NewFakeFs() | ||||
|  | ||||
| 	uriFolderName := "kubernetes" | ||||
|  | ||||
| 	fileName0 := "odo-service-some-service.yaml" | ||||
| 	fileName1 := "odo-url-some-url.yaml" | ||||
|  | ||||
| 	err := fs.MkdirAll(uriFolderName, os.ModePerm) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	file0, err := fs.Create(filepath.Join(uriFolderName, fileName0)) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	file1, err := fs.Create(filepath.Join(uriFolderName, fileName1)) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	addURIComponents := func(obj devfileParser.DevfileObj, name, uri string) error { | ||||
| 		err = obj.Data.AddComponents([]devfilev1.Component{{ | ||||
| 			Name: name, | ||||
| 			ComponentUnion: devfilev1.ComponentUnion{ | ||||
| 				Kubernetes: &devfilev1.KubernetesComponent{ | ||||
| 					K8sLikeComponent: devfilev1.K8sLikeComponent{ | ||||
| 						BaseComponent: devfilev1.BaseComponent{}, | ||||
| 						K8sLikeComponentLocation: devfilev1.K8sLikeComponentLocation{ | ||||
| 							Uri: uri, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}}) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	devfileObj := odotestingutil.GetTestDevfileObj(fs) | ||||
| 	err = addURIComponents(devfileObj, "some-service.yaml", file0.Name()) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error: %v", err) | ||||
| 	} | ||||
| 	err = addURIComponents(devfileObj, "some-ingress.yaml", file1.Name()) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error: %v", err) | ||||
| 	} | ||||
| 	err = addURIComponents(devfileObj, "some-route.yaml", "https://example.com") | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	devfileObjWithMissingFiles := odotestingutil.GetTestDevfileObj(fs) | ||||
| 	err = addURIComponents(devfileObjWithMissingFiles, "some-blah.yaml", file0.Name()+"blah") | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	type args struct { | ||||
| 		devfile          devfileParser.DevfileObj | ||||
| 		componentContext string | ||||
| 	} | ||||
| 	tests := []struct { | ||||
| 		name    string | ||||
| 		args    args | ||||
| 		wantErr bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "case 1: the files mentioned in the URI exists", | ||||
| 			args: args{ | ||||
| 				devfile:          devfileObj, | ||||
| 				componentContext: "", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "case 2: the files mentioned in the URI don't exists", | ||||
| 			args: args{ | ||||
| 				devfile:          devfileObjWithMissingFiles, | ||||
| 				componentContext: "", | ||||
| 			}, | ||||
| 			wantErr: true, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			if err := removeDevfileURIContents(tt.args.devfile, tt.args.componentContext, fs); (err != nil) != tt.wantErr { | ||||
| 				t.Errorf("RemoveDevfileURIContents() error = %v, wantErr %v", err, tt.wantErr) | ||||
| 			} | ||||
|  | ||||
| 			if !tt.wantErr { | ||||
| 				files, err := fs.ReadDir(uriFolderName) | ||||
| 				if err != nil { | ||||
| 					t.Errorf("unexpected error: %v", err) | ||||
| 				} | ||||
| 				if len(files) != 0 { | ||||
| 					t.Errorf("some files were not removed from the folder %v", uriFolderName) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			_ = fs.RemoveAll(uriFolderName) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import ( | ||||
| 	"k8s.io/utils/pointer" | ||||
|  | ||||
| 	"github.com/devfile/library/pkg/devfile/generator" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/component" | ||||
| 	componentlabels "github.com/redhat-developer/odo/pkg/component/labels" | ||||
| 	"github.com/redhat-developer/odo/pkg/devfile" | ||||
| @@ -317,7 +318,7 @@ func (a Adapter) Push(parameters common.PushParameters) (err error) { | ||||
| 	// PostStart events from the devfile will only be executed when the component | ||||
| 	// didn't previously exist | ||||
| 	if !componentExists && libdevfile.HasPostStartEvents(a.Devfile) { | ||||
| 		err = libdevfile.ExecPostStartEvents(a.Devfile, a.ComponentName, component.NewExecHandler(a.Client, a.pod.Name, parameters.Show)) | ||||
| 		err = libdevfile.ExecPostStartEvents(a.Devfile, component.NewExecHandler(a.Client, a.pod.Name, parameters.Show)) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import ( | ||||
|  | ||||
| 	"github.com/devfile/library/pkg/devfile" | ||||
| 	"github.com/devfile/library/pkg/devfile/parser" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/devfile/validate" | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
| ) | ||||
| @@ -46,12 +47,6 @@ func ParseAndValidateFromFile(devfilePath string) (parser.DevfileObj, error) { | ||||
| 	return parseDevfile(parser.ParserArgs{Path: devfilePath}) | ||||
| } | ||||
|  | ||||
| // ParseAndValidateFromURL parses devfile from given url and does all the validation | ||||
| // if there are warning it logs them on stdout | ||||
| func ParseAndValidateFromURL(url string) (parser.DevfileObj, error) { | ||||
| 	return parseDevfile(parser.ParserArgs{URL: url}) | ||||
| } | ||||
|  | ||||
| func variableWarning(section string, variable string, messages []string) string { | ||||
| 	quotedVars := []string{} | ||||
| 	for _, v := range messages { | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import ( | ||||
| 	devfile "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" | ||||
| 	"github.com/devfile/library/pkg/devfile/parser" | ||||
| 	"github.com/devfile/library/pkg/devfile/parser/data/v2/common" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/libdevfile" | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
| ) | ||||
| @@ -54,9 +55,9 @@ func BuildPushImages(devfileObj parser.DevfileObj, path string, push bool) error | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // BuildPushSpecificImage build an image defined in the devfile | ||||
| // BuildPushSpecificImage build an image defined in the devfile present in devfilePath | ||||
| // If push is true, also push the image to its registry | ||||
| func BuildPushSpecificImage(devfileObj parser.DevfileObj, devfilePath string, component devfile.Component, push bool) error { | ||||
| func BuildPushSpecificImage(devfilePath string, component devfile.Component, push bool) error { | ||||
| 	backend, err := selectBackend() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|   | ||||
| @@ -46,7 +46,7 @@ func (o *FlagsBackend) Validate(flags map[string]string, fs filesystem.Filesyste | ||||
| 	} | ||||
|  | ||||
| 	if flags[FLAG_DEVFILE_REGISTRY] != "" && !o.preferenceClient.RegistryNameExists(flags[FLAG_DEVFILE_REGISTRY]) { | ||||
| 		return fmt.Errorf("registry %q not found in the list of devfile registries. Please use `odo registry` command to configure devfile registries", flags[FLAG_DEVFILE_REGISTRY]) | ||||
| 		return fmt.Errorf("registry %q not found in the list of devfile registries. Please use `odo preference registry` command to configure devfile registries", flags[FLAG_DEVFILE_REGISTRY]) | ||||
| 	} | ||||
|  | ||||
| 	if flags[FLAG_DEVFILE_PATH] != "" && flags[FLAG_DEVFILE_REGISTRY] != "" { | ||||
|   | ||||
| @@ -8,7 +8,6 @@ import ( | ||||
| 	"sort" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
| 	appsv1 "k8s.io/api/apps/v1" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	kerrors "k8s.io/apimachinery/pkg/api/errors" | ||||
| @@ -17,15 +16,17 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/types" | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	componentlabels "github.com/redhat-developer/odo/pkg/component/labels" | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
|  | ||||
| 	apiMachineryWatch "k8s.io/apimachinery/pkg/watch" | ||||
|  | ||||
| 	componentlabels "github.com/redhat-developer/odo/pkg/component/labels" | ||||
| ) | ||||
|  | ||||
| func boolPtr(b bool) *bool { | ||||
| 	return &b | ||||
| } | ||||
|  | ||||
| // constants for deployments | ||||
| const ( | ||||
| 	DeploymentKind       = "Deployment" | ||||
| 	DeploymentAPIVersion = "apps/v1" | ||||
| @@ -33,13 +34,6 @@ const ( | ||||
| 	// TimedOutReason is added in a deployment when its newest replica set fails to show any progress | ||||
| 	// within the given deadline (progressDeadlineSeconds). | ||||
| 	timedOutReason = "ProgressDeadlineExceeded" | ||||
|  | ||||
| 	// Hardcoded variables since we can't install SBO on k8s using OLM | ||||
| 	// (https://github.com/redhat-developer/service-binding-operator/issues/536) | ||||
| 	ServiceBindingGroup    = "binding.operators.coreos.com" | ||||
| 	ServiceBindingVersion  = "v1alpha1" | ||||
| 	ServiceBindingKind     = "ServiceBinding" | ||||
| 	ServiceBindingResource = "servicebindings" | ||||
| ) | ||||
|  | ||||
| // GetDeploymentByName gets a deployment by querying by name | ||||
|   | ||||
| @@ -4,10 +4,11 @@ import ( | ||||
| 	"context" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
| ) | ||||
|  | ||||
| // We use a mutex here in order to make 100% sure that functions such as CollectEvents | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	time "time" | ||||
| 	"time" | ||||
|  | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
|   | ||||
| @@ -1,15 +0,0 @@ | ||||
| package fake | ||||
|  | ||||
| import ( | ||||
| 	v1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| ) | ||||
|  | ||||
| func GetSecret(secretName string) *v1.Secret { | ||||
| 	return &v1.Secret{ | ||||
| 		TypeMeta: metav1.TypeMeta{}, | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: secretName, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| @@ -81,7 +81,7 @@ func (c *Client) GetResourceSpecDefinition(group, version, kind string) (*spec.S | ||||
| // getResourceSpecDefinitionFromSwagger returns the OpenAPI v2 definition of the Kubernetes resource of a given group/version/kind, for a given swagger data | ||||
| func getResourceSpecDefinitionFromSwagger(data []byte, group, version, kind string) (*spec.Schema, error) { | ||||
| 	schema := new(spec.Schema) | ||||
| 	err := json.Unmarshal([]byte(data), schema) | ||||
| 	err := json.Unmarshal(data, schema) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|   | ||||
| @@ -10,9 +10,10 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
|  | ||||
| 	// api resource types | ||||
|  | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
|   | ||||
| @@ -1,66 +0,0 @@ | ||||
| package unions | ||||
|  | ||||
| import ( | ||||
| 	"github.com/devfile/library/pkg/devfile/generator" | ||||
| 	v1 "k8s.io/api/networking/v1" | ||||
| ) | ||||
|  | ||||
| // TODO: These functions are replicated from devfile library generators and it makes more sense that they reside there | ||||
| // getNetworkingV1IngressSpec gets an networking v1 ingress spec | ||||
| func getNetworkingV1IngressSpec(ingressSpecParams generator.IngressSpecParams) *v1.IngressSpec { | ||||
| 	path := "/" | ||||
| 	pathType := v1.PathTypeImplementationSpecific | ||||
| 	if ingressSpecParams.Path != "" { | ||||
| 		path = ingressSpecParams.Path | ||||
| 	} | ||||
| 	ingressSpec := &v1.IngressSpec{ | ||||
| 		Rules: []v1.IngressRule{ | ||||
| 			{ | ||||
| 				Host: ingressSpecParams.IngressDomain, | ||||
| 				IngressRuleValue: v1.IngressRuleValue{ | ||||
| 					HTTP: &v1.HTTPIngressRuleValue{ | ||||
| 						Paths: []v1.HTTPIngressPath{ | ||||
| 							{ | ||||
| 								Path: path, | ||||
| 								Backend: v1.IngressBackend{ | ||||
| 									Service: &v1.IngressServiceBackend{ | ||||
| 										Name: ingressSpecParams.ServiceName, | ||||
| 										Port: v1.ServiceBackendPort{ | ||||
| 											Number: ingressSpecParams.PortNumber.IntVal, | ||||
| 										}, | ||||
| 									}, | ||||
| 									Resource: nil, | ||||
| 								}, | ||||
| 								PathType: &pathType, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	secretNameLength := len(ingressSpecParams.TLSSecretName) | ||||
| 	if secretNameLength != 0 { | ||||
| 		ingressSpec.TLS = []v1.IngressTLS{ | ||||
| 			{ | ||||
| 				Hosts: []string{ | ||||
| 					ingressSpecParams.IngressDomain, | ||||
| 				}, | ||||
| 				SecretName: ingressSpecParams.TLSSecretName, | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ingressSpec | ||||
| } | ||||
|  | ||||
| func getNetworkingV1Ingress(ingressParams generator.IngressParams) *v1.Ingress { | ||||
| 	var ip *v1.Ingress | ||||
| 	ingressSpec := getNetworkingV1IngressSpec(ingressParams.IngressSpecParams) | ||||
| 	ip = &v1.Ingress{ | ||||
| 		TypeMeta:   ingressParams.TypeMeta, | ||||
| 		ObjectMeta: ingressParams.ObjectMeta, | ||||
| 		Spec:       *ingressSpec, | ||||
| 	} | ||||
| 	return ip | ||||
| } | ||||
| @@ -1,118 +0,0 @@ | ||||
| package unions | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" | ||||
| 	"github.com/devfile/library/pkg/devfile/generator" | ||||
| 	"k8s.io/api/extensions/v1beta1" | ||||
| 	v1 "k8s.io/api/networking/v1" | ||||
| ) | ||||
|  | ||||
| type KubernetesIngress struct { | ||||
| 	NetworkingV1Ingress     *v1.Ingress | ||||
| 	ExtensionV1Beta1Ingress *v1beta1.Ingress | ||||
| 	isGenerated             bool | ||||
| } | ||||
|  | ||||
| //NewNonGeneratedKubernetesIngress returns a new empty KubernetesIngress to be populated by caller. It is not genrated | ||||
| func NewNonGeneratedKubernetesIngress() *KubernetesIngress { | ||||
| 	return &KubernetesIngress{ | ||||
| 		NetworkingV1Ingress:     nil, | ||||
| 		ExtensionV1Beta1Ingress: nil, | ||||
| 		isGenerated:             false, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //NewGeneratedKubernetesIngress returns a generated KubernetesIngress to be populated by caller | ||||
| func NewGeneratedKubernetesIngress() *KubernetesIngress { | ||||
| 	return &KubernetesIngress{ | ||||
| 		NetworkingV1Ingress:     nil, | ||||
| 		ExtensionV1Beta1Ingress: nil, | ||||
| 		isGenerated:             true, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //NewKubernetesIngressFromParams generates a new KubernetesIngress from the ingress params | ||||
| func NewKubernetesIngressFromParams(ingressParams generator.IngressParams) *KubernetesIngress { | ||||
| 	ki := NewGeneratedKubernetesIngress() | ||||
| 	ki.NetworkingV1Ingress = getNetworkingV1Ingress(ingressParams) | ||||
| 	ki.ExtensionV1Beta1Ingress = generator.GetIngress(v1alpha2.Endpoint{}, ingressParams) | ||||
| 	return ki | ||||
| } | ||||
|  | ||||
| //IsGenerated returns true if ths KubernetesIngress was generated using generators | ||||
| func (ki *KubernetesIngress) IsGenerated() bool { | ||||
| 	return ki.isGenerated | ||||
| } | ||||
|  | ||||
| //GetName returns the name of underlying networking v1 or extensions v1 ingress | ||||
| func (ki *KubernetesIngress) GetName() string { | ||||
| 	if ki.NetworkingV1Ingress != nil { | ||||
| 		return ki.NetworkingV1Ingress.GetName() | ||||
| 	} else if ki.ExtensionV1Beta1Ingress != nil { | ||||
| 		return ki.ExtensionV1Beta1Ingress.GetName() | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| //GetProtocol returns `https` if tls is configured on either networking v1 or extensions v1 ingress, else `http` | ||||
| func (ki *KubernetesIngress) GetProtocol() string { | ||||
| 	if (ki.NetworkingV1Ingress != nil && len(ki.NetworkingV1Ingress.Spec.TLS) > 0) || (ki.ExtensionV1Beta1Ingress != nil && len(ki.ExtensionV1Beta1Ingress.Spec.TLS) > 0) { | ||||
| 		return "https" | ||||
| 	} | ||||
| 	return "http" | ||||
| } | ||||
|  | ||||
| //GetHost returns the host of underlying networking v1 or extensions v1 ingress | ||||
| func (ki *KubernetesIngress) GetHost() string { | ||||
| 	if ki.NetworkingV1Ingress != nil { | ||||
| 		return ki.NetworkingV1Ingress.Spec.Rules[0].Host | ||||
| 	} else if ki.ExtensionV1Beta1Ingress != nil { | ||||
| 		return ki.ExtensionV1Beta1Ingress.Spec.Rules[0].Host | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| //GetURLString returns the fully formed url of the form `GetProtocol()://GetHost()` | ||||
| func (ki *KubernetesIngress) GetURLString() string { | ||||
| 	return fmt.Sprintf("%v://%v", ki.GetProtocol(), ki.GetHost()) | ||||
| } | ||||
|  | ||||
| type KubernetesIngressList struct { | ||||
| 	Items []*KubernetesIngress | ||||
| } | ||||
|  | ||||
| func NewEmptyKubernetesIngressList() *KubernetesIngressList { | ||||
| 	return &KubernetesIngressList{} | ||||
| } | ||||
|  | ||||
| //GetNetworkingV1IngressList returns a v1.IngressList populated by networking v1 ingresses | ||||
| //if skipIfExtensionV1Set it true, then if both networking v1 and extension v1 are set for | ||||
| //specific KubernetesIngress, then it will be skipped form the returned list | ||||
| func (kil *KubernetesIngressList) GetNetworkingV1IngressList(skipIfExtensionV1Set bool) *v1.IngressList { | ||||
| 	il := v1.IngressList{} | ||||
| 	for _, it := range kil.Items { | ||||
| 		if !skipIfExtensionV1Set || (skipIfExtensionV1Set && it.ExtensionV1Beta1Ingress == nil) { | ||||
| 			if it.NetworkingV1Ingress != nil { | ||||
| 				il.Items = append(il.Items, *it.NetworkingV1Ingress) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return &il | ||||
| } | ||||
|  | ||||
| //GetExtensionV1Beta1IngresList returns a v1beta1.IngressList populated by extensions v1 beta1 ingresses | ||||
| //if skipIfNetworkingV1Set it true, then if both networking v1 and extension v1 are set for | ||||
| //specific KubernetesIngress, then it will be skipped form the returned list | ||||
| func (kil *KubernetesIngressList) GetExtensionV1Beta1IngresList(skipIfNetworkingV1Set bool) *v1beta1.IngressList { | ||||
| 	il := v1beta1.IngressList{} | ||||
| 	for _, it := range kil.Items { | ||||
| 		if !skipIfNetworkingV1Set || (skipIfNetworkingV1Set && it.NetworkingV1Ingress == nil) { | ||||
| 			if it.ExtensionV1Beta1Ingress != nil { | ||||
| 				il.Items = append(il.Items, *it.ExtensionV1Beta1Ingress) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return &il | ||||
| } | ||||
| @@ -74,16 +74,6 @@ func (e ComponentsWithSameNameError) Error() string { | ||||
| 	return "more than one component with the same name, should not happen" | ||||
| } | ||||
|  | ||||
| type NotAContainerError struct{} | ||||
|  | ||||
| func NewNotAContainerError() NotAContainerError { | ||||
| 	return NotAContainerError{} | ||||
| } | ||||
|  | ||||
| func (e NotAContainerError) Error() string { | ||||
| 	return "component not a container" | ||||
| } | ||||
|  | ||||
| // ComponentTypeNotFoundError is returned when no component with the specified type has been found in Devfile | ||||
| type ComponentTypeNotFoundError struct { | ||||
| 	componentType v1alpha2.ComponentType | ||||
|   | ||||
| @@ -82,12 +82,12 @@ func HasPreStopEvents(devfileObj parser.DevfileObj) bool { | ||||
| 	return len(preStopEvents) > 0 | ||||
| } | ||||
|  | ||||
| func ExecPostStartEvents(devfileObj parser.DevfileObj, componentName string, handler Handler) error { | ||||
| func ExecPostStartEvents(devfileObj parser.DevfileObj, handler Handler) error { | ||||
| 	postStartEvents := devfileObj.Data.GetEvents().PostStart | ||||
| 	return execDevfileEvent(devfileObj, postStartEvents, handler) | ||||
| } | ||||
|  | ||||
| func ExecPreStopEvents(devfileObj parser.DevfileObj, componentName string, handler Handler) error { | ||||
| func ExecPreStopEvents(devfileObj parser.DevfileObj, handler Handler) error { | ||||
| 	preStopEvents := devfileObj.Data.GetEvents().PreStop | ||||
| 	return execDevfileEvent(devfileObj, preStopEvents, handler) | ||||
| } | ||||
|   | ||||
| @@ -3,15 +3,7 @@ package libdevfile | ||||
| type DevfileEventType string | ||||
|  | ||||
| const ( | ||||
| 	// PreStart is a devfile event | ||||
| 	PreStart DevfileEventType = "preStart" | ||||
|  | ||||
| 	// PostStart is a devfile event | ||||
| 	PostStart DevfileEventType = "postStart" | ||||
|  | ||||
| 	// PreStop is a devfile event | ||||
| 	PreStop DevfileEventType = "preStop" | ||||
|  | ||||
| 	// PostStop is a devfile event | ||||
| 	PostStop DevfileEventType = "postStop" | ||||
| ) | ||||
|   | ||||
| @@ -32,9 +32,10 @@ import ( | ||||
|  | ||||
| 	"github.com/fatih/color" | ||||
| 	"github.com/mattn/go-colorable" | ||||
| 	"github.com/redhat-developer/odo/pkg/log/fidget" | ||||
| 	"github.com/spf13/pflag" | ||||
| 	"golang.org/x/term" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/log/fidget" | ||||
| ) | ||||
|  | ||||
| // Spacing for logging | ||||
| @@ -218,14 +219,6 @@ func (s *Status) End(success bool) { | ||||
| 	s.status = "" | ||||
| } | ||||
|  | ||||
| // Namef will output the name of the component / application / project in a *bolded* manner | ||||
| func Namef(format string, a ...interface{}) { | ||||
| 	if !IsJSON() { | ||||
| 		bold := color.New(color.Bold).SprintFunc() | ||||
| 		fmt.Fprintf(GetStdout(), "%s\n", bold(fmt.Sprintf(format, a...))) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Printf will output in an appropriate "information" manner; for e.g. | ||||
| // • <message> | ||||
| func Printf(format string, a ...interface{}) { | ||||
| @@ -270,13 +263,6 @@ func Warningf(format string, a ...interface{}) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Swarningf (like Sprintf) will return a string in the "warning" manner | ||||
| //	⚠ <message> | ||||
| func Swarningf(format string, a ...interface{}) string { | ||||
| 	yellow := color.New(color.FgYellow).SprintFunc() | ||||
| 	return fmt.Sprintf(" %s%s%s", yellow(getWarningString()), suffixSpacing, fmt.Sprintf(format, a...)) | ||||
| } | ||||
|  | ||||
| // Title Prints the logo as well as the first line being BLUE (indicator of the command information) | ||||
| // the second and third lines are optional and provide information with regards to what is being ran | ||||
| // 	 __ | ||||
| @@ -355,25 +341,6 @@ func Error(a ...interface{}) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Italic will simply print out information on a new italic line | ||||
| // *Line in italic* | ||||
| func Italic(a ...interface{}) { | ||||
| 	if !IsJSON() { | ||||
| 		italic := color.New(color.Italic).SprintFunc() | ||||
| 		fmt.Fprintf(GetStdout(), "%s", italic(fmt.Sprintln(a...))) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Italicf will simply print out information on a new italic line | ||||
| // this is **normally** used as a way to describe what's next within odo. | ||||
| // *Line in italic* | ||||
| func Italicf(format string, a ...interface{}) { | ||||
| 	if !IsJSON() { | ||||
| 		italic := color.New(color.Italic).SprintFunc() | ||||
| 		fmt.Fprintf(GetStdout(), "%s\n", italic(fmt.Sprintf(format, a...))) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Info will simply print out information on a new (bolded) line | ||||
| // this is intended as information *after* something has been deployed | ||||
| // **Line in bold** | ||||
| @@ -413,27 +380,6 @@ func Finfof(w io.Writer, format string, a ...interface{}) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Describef will print out the first variable as BOLD and then the second not.. | ||||
| // this is intended to be used with `odo describe` and other outputs that list | ||||
| // a lot of information | ||||
| // **Line in bold** | ||||
| // Line not in bold | ||||
| func Describef(title string, format string, a ...interface{}) { | ||||
| 	if !IsJSON() { | ||||
| 		bold := color.New(color.Bold).SprintFunc() | ||||
| 		fmt.Fprintf(GetStdout(), "%s%s\n", bold(title), fmt.Sprintf(format, a...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Askf will print out information, but in an "Ask" way (without newline) | ||||
| // Line-without-newline | ||||
| func Askf(format string, a ...interface{}) { | ||||
| 	if !IsJSON() { | ||||
| 		bold := color.New(color.Bold).SprintFunc() | ||||
| 		fmt.Fprintf(GetStdout(), "%s", bold(fmt.Sprintf(format, a...))) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Spinner creates a spinner, sets the prefix then returns it. | ||||
| // Remember to use .End(bool) to stop the spin / when you're done. | ||||
| // For example: defer s.End(false) | ||||
|   | ||||
| @@ -95,14 +95,6 @@ func NewConsoleMachineEventLoggingClient() *ConsoleMachineEventLoggingClient { | ||||
| 	return &ConsoleMachineEventLoggingClient{} | ||||
| } | ||||
|  | ||||
| // NewConsoleMachineEventLoggingClientWithFunction creates a new instance with a custom logging functional, | ||||
| // suitable for, for example, unit testing. | ||||
| func NewConsoleMachineEventLoggingClientWithFunction(logFunc func(machineOutput MachineEventWrapper)) *ConsoleMachineEventLoggingClient { | ||||
| 	return &ConsoleMachineEventLoggingClient{ | ||||
| 		logFunc: logFunc, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var _ MachineEventLoggingClient = &ConsoleMachineEventLoggingClient{} | ||||
|  | ||||
| // DevFileCommandExecutionBegin outputs the provided event as JSON to the console. | ||||
| @@ -324,20 +316,6 @@ const ( | ||||
| 	TypeKubernetesPodStatus MachineEventLogEntryType = 7 | ||||
| ) | ||||
|  | ||||
| // GetCommandName returns a command if the MLE supports that field (otherwise empty string is returned). | ||||
| // Currently used for test purposes only. | ||||
| func GetCommandName(entry MachineEventLogEntry) string { | ||||
|  | ||||
| 	if entry.GetType() == TypeDevFileCommandExecutionBegin { | ||||
| 		return entry.(*DevFileCommandExecutionBegin).CommandID | ||||
| 	} else if entry.GetType() == TypeDevFileCommandExecutionComplete { | ||||
| 		return entry.(*DevFileCommandExecutionComplete).CommandID | ||||
| 	} else { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // createWriterAndChannel is similar to the exec.CreateConsoleOutputWriterAndChannel(); see that function's comment for details. | ||||
| func createWriterAndChannel(stderr bool) (*io.PipeWriter, chan interface{}) { | ||||
| 	reader, writer := io.Pipe() | ||||
|   | ||||
| @@ -236,7 +236,7 @@ func populateWorkingDir(fs filesystem.Filesystem, workingDir, compName, projectN | ||||
| 	if err != nil { | ||||
| 		return env | ||||
| 	} | ||||
| 	_ = fs.WriteFile(filepath.Join(workingDir, "devfile.yaml"), []byte(devfileYAML), 0644) | ||||
| 	_ = fs.WriteFile(filepath.Join(workingDir, "devfile.yaml"), devfileYAML, 0644) | ||||
| 	env.SetDevfileObj(devfileObj) | ||||
| 	return env | ||||
| } | ||||
| @@ -258,7 +258,7 @@ func prepareKubeClient(ctrl *gomock.Controller, projectName string) kclient.Clie | ||||
| func prepareContext(ctrl *gomock.Controller, kubeClient kclient.ClientInterface, info *envinfo.EnvSpecificInfo, workingDir string) *genericclioptions.Context { | ||||
| 	cmdline := cmdline.NewMockCmdline(ctrl) | ||||
| 	cmdline.EXPECT().GetWorkingDirectory().Return(workingDir, nil).AnyTimes() | ||||
| 	cmdline.EXPECT().CheckIfConfigurationNeeded().Return(false, nil).AnyTimes() | ||||
| 	cmdline.EXPECT().CheckIfConfigurationNeeded().Return(true, nil).AnyTimes() | ||||
| 	cmdline.EXPECT().FlagValueIfSet("project").Return("").AnyTimes() | ||||
| 	cmdline.EXPECT().FlagValueIfSet("app").Return("").AnyTimes() | ||||
| 	cmdline.EXPECT().FlagValueIfSet("component").Return("").AnyTimes() | ||||
|   | ||||
| @@ -4,19 +4,21 @@ import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/jedib0t/go-pretty/v6/table" | ||||
| 	"github.com/jedib0t/go-pretty/v6/text" | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/devfile" | ||||
| 	"github.com/redhat-developer/odo/pkg/devfile/location" | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	dfutil "github.com/devfile/library/pkg/util" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/component" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/cmdline" | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/genericclioptions" | ||||
| @@ -146,7 +148,7 @@ func (lo *ListOptions) Run(ctx context.Context) error { | ||||
| 		devfileComponents = append(devfileComponents, lo.localComponent) | ||||
| 	} | ||||
|  | ||||
| 	lo.HumanReadableOutput(log.GetStdout(), devfileComponents) | ||||
| 	lo.HumanReadableOutput(devfileComponents) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -176,7 +178,7 @@ func NewCmdList(name, fullName string) *cobra.Command { | ||||
| 	return listCmd | ||||
| } | ||||
|  | ||||
| func (lo *ListOptions) HumanReadableOutput(wr io.Writer, components []component.OdoComponent) { | ||||
| func (lo *ListOptions) HumanReadableOutput(components []component.OdoComponent) { | ||||
| 	if len(components) == 0 { | ||||
| 		log.Info("There are no components deployed.") | ||||
| 		return | ||||
|   | ||||
| @@ -22,7 +22,7 @@ import ( | ||||
|  | ||||
| const addCommandName = "add" | ||||
|  | ||||
| // "odo registry add" command description and examples | ||||
| // "odo preference registry add" command description and examples | ||||
| var ( | ||||
| 	addLongDesc = ktemplates.LongDesc(`Add devfile registry`) | ||||
|  | ||||
| @@ -33,7 +33,7 @@ var ( | ||||
| 	`) | ||||
| ) | ||||
|  | ||||
| // AddOptions encapsulates the options for the "odo registry add" command | ||||
| // AddOptions encapsulates the options for the "odo preference registry add" command | ||||
| type AddOptions struct { | ||||
| 	// Clients | ||||
| 	clientset *clientset.Clientset | ||||
| @@ -79,7 +79,7 @@ func (o *AddOptions) Validate() (err error) { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Run contains the logic for "odo registry add" command | ||||
| // Run contains the logic for "odo preference registry add" command | ||||
| func (o *AddOptions) Run(ctx context.Context) (err error) { | ||||
| 	isSecure := false | ||||
| 	if o.tokenFlag != "" { | ||||
| @@ -102,7 +102,7 @@ func (o *AddOptions) Run(ctx context.Context) (err error) { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // NewCmdAdd implements the "odo registry add" command | ||||
| // NewCmdAdd implements the "odo preference registry add" command | ||||
| func NewCmdAdd(name, fullName string) *cobra.Command { | ||||
| 	o := NewAddOptions() | ||||
| 	registryAddCmd := &cobra.Command{ | ||||
|   | ||||
| @@ -20,7 +20,7 @@ import ( | ||||
|  | ||||
| const deleteCommandName = "delete" | ||||
|  | ||||
| // "odo registry delete" command description and examples | ||||
| // "odo preference registry delete" command description and examples | ||||
| var ( | ||||
| 	deleteLongDesc = ktemplates.LongDesc(`Delete devfile registry`) | ||||
|  | ||||
| @@ -29,7 +29,7 @@ var ( | ||||
| 	`) | ||||
| ) | ||||
|  | ||||
| // DeleteOptions encapsulates the options for the "odo registry delete" command | ||||
| // DeleteOptions encapsulates the options for the "odo preference registry delete" command | ||||
| type DeleteOptions struct { | ||||
| 	// Clients | ||||
| 	clientset *clientset.Clientset | ||||
| @@ -68,7 +68,7 @@ func (o *DeleteOptions) Validate() (err error) { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Run contains the logic for "odo registry delete" command | ||||
| // Run contains the logic for "odo preference registry delete" command | ||||
| func (o *DeleteOptions) Run(ctx context.Context) (err error) { | ||||
| 	isSecure := registryUtil.IsSecure(o.clientset.PreferenceClient, o.registryName) | ||||
| 	err = o.clientset.PreferenceClient.RegistryHandler(o.operation, o.registryName, o.registryURL, o.forceFlag, false) | ||||
| @@ -86,7 +86,7 @@ func (o *DeleteOptions) Run(ctx context.Context) (err error) { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // NewCmdDelete implements the "odo registry delete" command | ||||
| // NewCmdDelete implements the "odo preference registry delete" command | ||||
| func NewCmdDelete(name, fullName string) *cobra.Command { | ||||
| 	o := NewDeleteOptions() | ||||
| 	registryDeleteCmd := &cobra.Command{ | ||||
|   | ||||
| @@ -8,11 +8,11 @@ import ( | ||||
| 	"os" | ||||
| 	"text/tabwriter" | ||||
|  | ||||
| 	// Third-party packages | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
| 	ktemplates "k8s.io/kubectl/pkg/util/templates" | ||||
|  | ||||
| 	// Third-party packages | ||||
|  | ||||
| 	// odo packages | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/cli/preference/registry/util" | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/cmdline" | ||||
| @@ -23,7 +23,7 @@ import ( | ||||
|  | ||||
| const listCommandName = "list" | ||||
|  | ||||
| // "odo registry list" command description and examples | ||||
| // "odo preference registry list" command description and examples | ||||
| var ( | ||||
| 	listDesc = ktemplates.LongDesc(`List devfile registry`) | ||||
|  | ||||
| @@ -32,7 +32,7 @@ var ( | ||||
| 	`) | ||||
| ) | ||||
|  | ||||
| // ListOptions encapsulates the options for "odo registry list" command | ||||
| // ListOptions encapsulates the options for "odo preference registry list" command | ||||
| type ListOptions struct { | ||||
| 	// Clients | ||||
| 	clientset *clientset.Clientset | ||||
| @@ -59,11 +59,11 @@ func (o *ListOptions) Validate() (err error) { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Run contains the logic for "odo registry list" command | ||||
| // Run contains the logic for "odo preference registry list" command | ||||
| func (o *ListOptions) Run(ctx context.Context) (err error) { | ||||
| 	registryList := o.clientset.PreferenceClient.RegistryList() | ||||
| 	if registryList == nil || len(*registryList) == 0 { | ||||
| 		return fmt.Errorf("No devfile registries added to the configuration. Refer `odo registry add -h` to add one") | ||||
| 		return fmt.Errorf("No devfile registries added to the configuration. Refer `odo preference registry add -h` to add one") | ||||
| 	} | ||||
|  | ||||
| 	w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent) | ||||
| @@ -96,7 +96,7 @@ func (o *ListOptions) printRegistryList(w io.Writer, registryList *[]preference. | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewCmdList implements the "odo registry list" command | ||||
| // NewCmdList implements the "odo preference registry list" command | ||||
| func NewCmdList(name, fullName string) *cobra.Command { | ||||
| 	o := NewListOptions() | ||||
| 	registryListCmd := &cobra.Command{ | ||||
|   | ||||
| @@ -21,7 +21,7 @@ import ( | ||||
|  | ||||
| const updateCommandName = "update" | ||||
|  | ||||
| // "odo registry update" command description and examples | ||||
| // "odo preference registry update" command description and examples | ||||
| var ( | ||||
| 	updateLongDesc = ktemplates.LongDesc(`Update devfile registry URL`) | ||||
|  | ||||
| @@ -30,7 +30,7 @@ var ( | ||||
| 	`) | ||||
| ) | ||||
|  | ||||
| // UpdateOptions encapsulates the options for the "odo registry update" command | ||||
| // UpdateOptions encapsulates the options for the "odo preference registry update" command | ||||
| type UpdateOptions struct { | ||||
| 	// Clients | ||||
| 	clientset *clientset.Clientset | ||||
| @@ -77,7 +77,7 @@ func (o *UpdateOptions) Validate() (err error) { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Run contains the logic for "odo registry update" command | ||||
| // Run contains the logic for "odo preference registry update" command | ||||
| func (o *UpdateOptions) Run(ctx context.Context) (err error) { | ||||
| 	secureBeforeUpdate := false | ||||
| 	secureAfterUpdate := false | ||||
| @@ -111,7 +111,7 @@ func (o *UpdateOptions) Run(ctx context.Context) (err error) { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // NewCmdUpdate implements the "odo registry update" command | ||||
| // NewCmdUpdate implements the "odo preference registry update" command | ||||
| func NewCmdUpdate(name, fullName string) *cobra.Command { | ||||
| 	o := NewUpdateOptions() | ||||
| 	registryUpdateCmd := &cobra.Command{ | ||||
|   | ||||
| @@ -34,7 +34,7 @@ func TestCreate(t *testing.T) { | ||||
| 		args               []string | ||||
| 		//		existingApps       []string | ||||
| 		wantProjectName string | ||||
| 		//wantErrValidate string | ||||
| 		// wantErrValidate string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "project from args", | ||||
| @@ -71,7 +71,7 @@ func TestCreate(t *testing.T) { | ||||
| 			cmdline.EXPECT().FlagValueIfSet("app").Return("").AnyTimes() | ||||
| 			cmdline.EXPECT().FlagValueIfSet("component").Return("").AnyTimes() | ||||
| 			cmdline.EXPECT().FlagValueIfSet("o").Return("").AnyTimes() | ||||
| 			cmdline.EXPECT().CheckIfConfigurationNeeded().Return(false, nil).AnyTimes() | ||||
| 			cmdline.EXPECT().CheckIfConfigurationNeeded().Return(true, nil).AnyTimes() | ||||
| 			cmdline.EXPECT().Context().Return(context.Background()).AnyTimes() | ||||
|  | ||||
| 			// Fake odo Kube client | ||||
|   | ||||
| @@ -91,7 +91,7 @@ func TestDelete(t *testing.T) { | ||||
| 			cmdline.EXPECT().FlagValueIfSet("app").Return("").AnyTimes() | ||||
| 			cmdline.EXPECT().FlagValueIfSet("component").Return("").AnyTimes() | ||||
| 			cmdline.EXPECT().FlagValueIfSet("o").Return("").AnyTimes() | ||||
| 			cmdline.EXPECT().CheckIfConfigurationNeeded().Return(false, nil).AnyTimes() | ||||
| 			cmdline.EXPECT().CheckIfConfigurationNeeded().Return(true, nil).AnyTimes() | ||||
| 			cmdline.EXPECT().Context().Return(context.Background()).AnyTimes() | ||||
|  | ||||
| 			// Fake odo Kube client | ||||
|   | ||||
| @@ -56,87 +56,3 @@ func NewCmdProject(name, fullName string) *cobra.Command { | ||||
|  | ||||
| 	return projectCmd | ||||
| } | ||||
|  | ||||
| // AddProjectFlag adds a `project` flag to the given cobra command | ||||
| // Also adds a completion handler to the flag | ||||
| func AddProjectFlag(cmd *cobra.Command) { | ||||
| 	cmd.Flags().String(odoutil.ProjectFlagName, "", "Project, defaults to active project") | ||||
| 	completion.RegisterCommandFlagHandler(cmd, "project", completion.ProjectNameCompletionHandler) | ||||
| } | ||||
|  | ||||
| // TODO: The following function should work with devfile components, and use good abstraction that will make it possible to share | ||||
| // code with 'odo describe' and 'odo list' | ||||
| // printDeleteProjectInfo prints objects affected by project deletion | ||||
| // func printDeleteProjectInfo(client *occlient.Client, projectName string) error { | ||||
| // 	localConfig, err := config.New() | ||||
| // 	if err != nil { | ||||
| // 		return errors.Wrapf(err, "unable to get the local config") | ||||
| // 	} | ||||
| // 	// Fetch and List the applications | ||||
| // 	applicationList, err := application.ListInProject(client) | ||||
| // 	if err != nil { | ||||
| // 		return errors.Wrap(err, "failed to get application list") | ||||
| // 	} | ||||
| // 	if len(applicationList) != 0 { | ||||
| // 		log.Info("This project contains the following applications, which will be deleted") | ||||
| // 		for _, app := range applicationList { | ||||
| // 			log.Info("Application", app) | ||||
|  | ||||
| // 			// List the components | ||||
| //			var selector string | ||||
| //			if app != "" { | ||||
| //				selector = applabels.GetSelector(app) | ||||
| //			} | ||||
| // 			componentList, err := component.List(client, selector, nil) | ||||
| // 			if err != nil { | ||||
| // 				return errors.Wrap(err, "failed to get Component list") | ||||
| // 			} | ||||
| // 			if len(componentList.Items) != 0 { | ||||
| // 				log.Info("This application has following components that will be deleted") | ||||
|  | ||||
| // 				for _, currentComponent := range componentList.Items { | ||||
| // 					componentDesc, err := component.GetComponent(client, currentComponent.Name, app, projectName) | ||||
| // 					if err != nil { | ||||
| // 						return errors.Wrap(err, "unable to get component description") | ||||
| // 					} | ||||
| // 					log.Info("component named", componentDesc.Name) | ||||
|  | ||||
| // 					if len(componentDesc.Spec.URL) != 0 { | ||||
| // 						ul, err := url.ListPushed(client, componentDesc.Name, app) | ||||
| // 						if err != nil { | ||||
| // 							return errors.Wrap(err, "Could not get url list") | ||||
| // 						} | ||||
| // 						log.Info("This component has following urls that will be deleted with component") | ||||
| // 						for _, u := range ul.Items { | ||||
| // 							log.Info("URL named", u.GetName(), "with host", u.Spec.Host, "having protocol", u.Spec.Protocol, "at port", u.Spec.Port) | ||||
| // 						} | ||||
| // 					} | ||||
|  | ||||
| // 					storages, err := localConfig.StorageList() | ||||
| // 					odoutil.LogErrorAndExit(err, "") | ||||
| // 					if len(storages) != 0 { | ||||
| // 						log.Info("This component has following storages which will be deleted with the component") | ||||
| // 						for _, store := range storages { | ||||
| // 							log.Info("Storage named", store.Name, "of size", store.Size) | ||||
| // 						} | ||||
| // 					} | ||||
| // 				} | ||||
| // 			} | ||||
|  | ||||
| // 			// List services that will be removed | ||||
| // 			serviceList, err := service.List(client, app) | ||||
| // 			if err != nil { | ||||
| // 				log.Info("No services / could not get services") | ||||
| // 				klog.V(4).Info(err.Error()) | ||||
| // 			} | ||||
|  | ||||
| // 			if len(serviceList.Items) != 0 { | ||||
| // 				log.Info("This application has following service that will be deleted") | ||||
| // 				for _, ser := range serviceList.Items { | ||||
| // 					log.Info("service named", ser.ObjectMeta.Name, "of type", ser.Spec.Type) | ||||
| // 				} | ||||
| // 			} | ||||
| // 		} | ||||
| // 	} | ||||
| // 	return nil | ||||
| // } | ||||
|   | ||||
| @@ -24,7 +24,7 @@ type Cmdline interface { | ||||
| 	// IsFlagSet returns true if the flag is explicitely set | ||||
| 	IsFlagSet(flagName string) bool | ||||
|  | ||||
| 	// CheckIfConfigurationNeeded checks against a set of commands that do *NOT* need configuration. | ||||
| 	// CheckIfConfigurationNeeded checks against a set of commands that need configuration. | ||||
| 	CheckIfConfigurationNeeded() (bool, error) | ||||
|  | ||||
| 	// Context returns the context attached to the command | ||||
|   | ||||
| @@ -85,19 +85,14 @@ func (o *Cobra) FlagValueIfSet(flagName string) string { | ||||
| 	return flag | ||||
| } | ||||
|  | ||||
| // CheckIfConfigurationNeeded checks against a set of commands that do *NOT* need configuration. | ||||
| // CheckIfConfigurationNeeded checks against a set of commands that need configuration. | ||||
| func (o *Cobra) CheckIfConfigurationNeeded() (bool, error) { | ||||
| 	// Here we will check for parent commands, if the match a certain criteria, we will skip | ||||
| 	// using the configuration. | ||||
| 	// | ||||
| 	// For example, `odo create` should NOT check to see if there is actually a configuration yet. | ||||
| 	// For example, `odo init` should NOT check to see if there is actually a configuration yet. | ||||
| 	if o.cmd.HasParent() { | ||||
|  | ||||
| 		// Gather necessary preliminary information | ||||
| 		parentCommand := o.cmd.Parent() | ||||
| 		rootCommand := o.cmd.Root() | ||||
| 		flagValue := o.FlagValueIfSet(util.ApplicationFlagName) | ||||
|  | ||||
| 		// Find the first child of the command, as some groups are allowed even with non existent configuration | ||||
| 		firstChildCommand := getFirstChildOfCommand(o.cmd) | ||||
|  | ||||
| @@ -105,58 +100,18 @@ func (o *Cobra) CheckIfConfigurationNeeded() (bool, error) { | ||||
| 		if firstChildCommand == nil { | ||||
| 			return false, fmt.Errorf("Unable to get first child of command") | ||||
| 		} | ||||
| 		// Case 1 : if command is create operation just allow it | ||||
| 		if o.cmd.Name() == "create" && (parentCommand.Name() == "component" || parentCommand.Name() == rootCommand.Name()) { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 2 : if command is describe or delete and app flag is used just allow it | ||||
| 		if (firstChildCommand.Name() == "describe" || firstChildCommand.Name() == "delete") && len(flagValue) > 0 { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 3 : if command is list, just allow it | ||||
| 		if firstChildCommand.Name() == "list" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 4 : Check if firstChildCommand is project. If so, skip validation of context | ||||
| 		if firstChildCommand.Name() == "project" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 5 : Check if specific flags are set for specific first child commands | ||||
| 		if firstChildCommand.Name() == "app" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 6 : Check if firstChildCommand is catalog and request is to list or search | ||||
| 		if firstChildCommand.Name() == "catalog" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 7: Check if firstChildCommand is component and  request is list | ||||
| 		if (firstChildCommand.Name() == "component" || firstChildCommand.Name() == "service") && o.cmd.Name() == "list" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 8 : Check if firstChildCommand is component and app flag is used | ||||
| 		if firstChildCommand.Name() == "component" && len(flagValue) > 0 { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 9 : Check if firstChildCommand is logout and app flag is used | ||||
| 		if firstChildCommand.Name() == "logout" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 10: Check if firstChildCommand is service and command is create or delete. Allow it if that's the case | ||||
| 		if firstChildCommand.Name() == "service" && (o.cmd.Name() == "create" || o.cmd.Name() == "delete") { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 11 : if command is deploy | ||||
| 		if o.cmd.Name() == "deploy" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// Case 12 : if command is dev | ||||
| 		if o.cmd.Name() == "dev" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 	} else { | ||||
| 		return true, nil | ||||
| 	} | ||||
|  | ||||
| 		// Gather necessary preliminary information | ||||
| 		componentNameFlagValue := o.FlagValueIfSet(util.ComponentNameFlagName) | ||||
| 		// if command is `odo delete component` and name flag is not used, require configuration | ||||
| 		if firstChildCommand.Name() == "delete" && o.cmd.Name() == "component" && len(componentNameFlagValue) == 0 { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		// if command is `odo build-images`, require configuration | ||||
| 		if o.cmd.Name() == "build-images" { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return false, nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -338,7 +338,7 @@ func TestNew(t *testing.T) { | ||||
| 			// Fake Cobra | ||||
| 			cmdline := cmdline.NewMockCmdline(ctrl) | ||||
| 			cmdline.EXPECT().GetWorkingDirectory().Return(tt.input.workingDir, nil).AnyTimes() | ||||
| 			cmdline.EXPECT().CheckIfConfigurationNeeded().Return(false, nil).AnyTimes() | ||||
| 			cmdline.EXPECT().CheckIfConfigurationNeeded().Return(true, nil).AnyTimes() | ||||
| 			cmdline.EXPECT().FlagValueIfSet("project").Return(tt.input.projectFlag).AnyTimes() | ||||
| 			cmdline.EXPECT().FlagValueIfSet("app").Return(tt.input.appFlag).AnyTimes() | ||||
| 			cmdline.EXPECT().FlagValueIfSet("component").Return(tt.input.componentFlag).AnyTimes() | ||||
|   | ||||
| @@ -23,11 +23,11 @@ func GetValidEnvInfo(cmdline cmdline.Cmdline) (*envinfo.EnvSpecificInfo, error) | ||||
|  | ||||
| 	// Now we check to see if we can skip gathering the information. | ||||
| 	// Return if we can skip gathering configuration information | ||||
| 	canWeSkip, err := cmdline.CheckIfConfigurationNeeded() | ||||
| 	configIsNeeded, err := cmdline.CheckIfConfigurationNeeded() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if canWeSkip { | ||||
| 	if !configIsNeeded { | ||||
| 		return envInfo, nil | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -11,15 +11,6 @@ import ( | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/util" | ||||
| ) | ||||
|  | ||||
| // ResolveAppFlag resolves the app from the flag | ||||
| func ResolveAppFlag(cmdline cmdline.Cmdline) string { | ||||
| 	appFlag := cmdline.FlagValueIfSet(util.ApplicationFlagName) | ||||
| 	if len(appFlag) > 0 { | ||||
| 		return appFlag | ||||
| 	} | ||||
| 	return defaultAppName | ||||
| } | ||||
|  | ||||
| // resolveProjectAndNamespace resolve project in Context and namespace in Kubernetes and OpenShift clients | ||||
| func (o *internalCxt) resolveProjectAndNamespace(cmdline cmdline.Cmdline, configProvider localConfigProvider.LocalConfigProvider) error { | ||||
| 	var namespace string | ||||
|   | ||||
| @@ -12,26 +12,31 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"gopkg.in/AlecAivazis/survey.v1/terminal" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/machineoutput" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/cmdline" | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset" | ||||
| 	commonutil "github.com/redhat-developer/odo/pkg/util" | ||||
| 	"gopkg.in/AlecAivazis/survey.v1/terminal" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/version" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/cli/ui" | ||||
| 	"gopkg.in/AlecAivazis/survey.v1" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/cli/ui" | ||||
|  | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/preference" | ||||
| 	"github.com/redhat-developer/odo/pkg/segment" | ||||
| 	scontext "github.com/redhat-developer/odo/pkg/segment/context" | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"github.com/spf13/pflag" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/util" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"github.com/spf13/pflag" | ||||
| ) | ||||
|  | ||||
| type Runnable interface { | ||||
| @@ -105,9 +110,6 @@ func GenericRun(o Runnable, cmd *cobra.Command, args []string) { | ||||
| 	// fixes / checks all related machine readable output functions | ||||
| 	util.LogErrorAndExit(CheckMachineReadableOutputCommand(cmd), "") | ||||
|  | ||||
| 	// LogErrorAndExit is used so that we get -o (jsonoutput) for cmds which have json output implemented | ||||
| 	util.LogErrorAndExit(checkConflictingFlags(cmd, args), "") | ||||
|  | ||||
| 	deps, err := clientset.Fetch(cmd) | ||||
| 	if err != nil { | ||||
| 		util.LogErrorAndExit(err, "") | ||||
| @@ -176,39 +178,6 @@ func startTelemetry(cmd *cobra.Command, err error, startTime time.Time) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // checkConflictingFlags checks for conflicting flags. Currently --context cannot be provided | ||||
| // with either --app, --project and --component as that information can be fetched from the local | ||||
| // config. | ||||
| func checkConflictingFlags(cmd *cobra.Command, args []string) error { | ||||
|  | ||||
| 	// we allow providing --context with --app and --project in case of `odo create` or `odo component create` | ||||
| 	if cmd.Name() == "create" { | ||||
| 		if cmd.HasParent() { | ||||
| 			if cmd.Parent().Name() == "odo" || cmd.Parent().Name() == "component" { | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	app := stringFlagLookup(cmd, "app") | ||||
| 	project := stringFlagLookup(cmd, "project") | ||||
| 	context := stringFlagLookup(cmd, "context") | ||||
| 	component := stringFlagLookup(cmd, "component") | ||||
|  | ||||
| 	if (context != "") && (app != "" || project != "" || component != "") { | ||||
| 		return fmt.Errorf("cannot provide --app, --project or --component flag when --context is provided") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func stringFlagLookup(cmd *cobra.Command, flagName string) string { | ||||
| 	flag := cmd.Flags().Lookup(flagName) | ||||
| 	// a check to make sure if the flag is not defined we return blank | ||||
| 	if flag == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return flag.Value.String() | ||||
| } | ||||
|  | ||||
| // CheckMachineReadableOutputCommand performs machine-readable output functions required to | ||||
| // have it work correctly | ||||
| func CheckMachineReadableOutputCommand(cmd *cobra.Command) error { | ||||
|   | ||||
| @@ -41,17 +41,6 @@ func LogErrorAndExit(err error, context string, a ...interface{}) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // CheckOutputFlag validates the -o flag | ||||
| func CheckOutputFlag(outputFlag string) error { | ||||
| 	switch outputFlag { | ||||
| 	case "", "json": | ||||
| 		return nil | ||||
| 	default: | ||||
| 		return fmt.Errorf("Please input valid output format. available format: json") | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // GetFullName generates a command's full name based on its parent's full name and its own name | ||||
| func GetFullName(parentName, name string) string { | ||||
| 	return parentName + " " + name | ||||
|   | ||||
| @@ -1,14 +1,10 @@ | ||||
| package completion | ||||
|  | ||||
| import ( | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/genericclioptions" | ||||
| 	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/testingutil/filesystem" | ||||
|  | ||||
| 	"github.com/posener/complete" | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/odo/genericclioptions" | ||||
| ) | ||||
|  | ||||
| // FileCompletionHandler provides suggestions for files and directories | ||||
| @@ -35,34 +31,3 @@ var ProjectNameCompletionHandler = func(cmd *cobra.Command, args parsedArgs, con | ||||
| 	} | ||||
| 	return completions | ||||
| } | ||||
|  | ||||
| // URLCompletionHandler provides completion for the url commands | ||||
| var URLCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context *genericclioptions.Context) (completions []string) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // CreateCompletionHandler provides component type completion in odo create command | ||||
| var CreateCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context *genericclioptions.Context) (completions []string) { | ||||
| 	completions = make([]string, 0) | ||||
| 	comps := &completions | ||||
|  | ||||
| 	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") | ||||
| 	} | ||||
| 	components, _ := registry.NewRegistryClient(filesystem.DefaultFs{}, prefClient).ListDevfileStacks("") | ||||
| 	for _, devfile := range components.Items { | ||||
| 		if args.commands[devfile.Name] { | ||||
| 			return nil | ||||
| 		} | ||||
| 		*comps = append(*comps, devfile.Name) | ||||
| 	} | ||||
|  | ||||
| 	return completions | ||||
| } | ||||
|  | ||||
| // ComponentNameCompletionHandler provides component name completion | ||||
| var ComponentNameCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context *genericclioptions.Context) (completions []string) { | ||||
| 	completions = make([]string, 0) | ||||
| 	return completions | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,8 @@ const ( | ||||
| 	OutputFlagName = "o" | ||||
| 	// ContextFlagName is the name of the flag allowing a user to specify the location of the component settings | ||||
| 	ContextFlagName = "context" | ||||
| 	// ComponentNameFlagName is the name of the flag allowing a user to specify which component to operate on | ||||
| 	ComponentNameFlagName = "name" | ||||
| ) | ||||
|  | ||||
| // AddContextFlag adds `context` flag to given cobra command | ||||
| @@ -26,13 +28,3 @@ func AddContextFlag(cmd *cobra.Command, setValueTo *string) { | ||||
| 		cmd.Flags().String(ContextFlagName, "", helpMessage) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // AddNowFlag adds `now` flag to given cobra command | ||||
| func AddNowFlag(cmd *cobra.Command, setValueTo *bool) { | ||||
| 	helpMessage := "Push changes to the cluster immediately" | ||||
| 	if setValueTo != nil { | ||||
| 		cmd.Flags().BoolVar(setValueTo, "now", false, helpMessage) | ||||
| 	} else { | ||||
| 		cmd.Flags().Bool("now", false, helpMessage) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,11 +0,0 @@ | ||||
| package validation | ||||
|  | ||||
| // Validatable represents a common ancestor for validatable parameters | ||||
| type Validatable struct { | ||||
| 	// Required indicates whether this Validatable is a required value in the context it's supposed to be used | ||||
| 	Required bool `json:"required,omitempty"` | ||||
| 	// Type specifies the type of values this Validatable accepts so that some validation can be performed based on it | ||||
| 	Type string `json:"type"` | ||||
| 	// AdditionalValidators allows users to specify validators (in addition to default ones) to validate this Validatable's value | ||||
| 	AdditionalValidators []Validator `json:"-"` | ||||
| } | ||||
| @@ -1,31 +0,0 @@ | ||||
| package validation | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/validation" | ||||
| ) | ||||
|  | ||||
| // ValidateName will do validation of application & component names according to DNS (RFC 1123) rules | ||||
| // Criteria for valid name in kubernetes: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/architecture/identifiers.md | ||||
| func ValidateName(name string) error { | ||||
|  | ||||
| 	errorList := validation.IsDNS1123Label(name) | ||||
|  | ||||
| 	if len(errorList) != 0 { | ||||
| 		return fmt.Errorf("%s is not a valid name:  %s", name, strings.Join(errorList, " ")) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ValidateHost validates that the provided host is a valid subdomain according to DNS (RFC 1123) rules | ||||
| func ValidateHost(host string) error { | ||||
| 	errorList := validation.IsDNS1123Subdomain(host) | ||||
| 	if len(errorList) != 0 { | ||||
| 		return fmt.Errorf("%s is not a valid host name:  %s", host, strings.Join(errorList, " ")) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -1,146 +0,0 @@ | ||||
| package validation | ||||
|  | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
|  | ||||
| 	"gopkg.in/AlecAivazis/survey.v1" | ||||
| ) | ||||
|  | ||||
| func Test_validateName(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		testCase string | ||||
| 		name     string | ||||
| 		wantErr  bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			testCase: "Test case - 1", | ||||
| 			name:     "app", | ||||
| 			wantErr:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testCase: "Test case - 2", | ||||
| 			name:     "app123", | ||||
| 			wantErr:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testCase: "Test case - 3", | ||||
| 			name:     "app-123", | ||||
| 			wantErr:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testCase: "Test case - 4", | ||||
| 			name:     "app.123", | ||||
| 			wantErr:  true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testCase: "Test case - 5", | ||||
| 			name:     "app_123", | ||||
| 			wantErr:  true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testCase: "Test case - 6", | ||||
| 			name:     "App", | ||||
| 			wantErr:  true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testCase: "Test case - 7", | ||||
| 			name:     "b7pdkva7ynxf8qoyuh02tbgu2ufcy4jq7udyom7it0g8gouc39x3gy0p1wrsbt6", | ||||
| 			wantErr:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testCase: "Test case - 8", | ||||
| 			name:     "b7pdkva7ynxf8qoyuh02tbgu2ufcy4jq7udyom7it0g8gouc39x3gy0p1wrsbt6p", | ||||
| 			wantErr:  true, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Log("Running test", tt.testCase) | ||||
| 		t.Run(tt.testCase, func(t *testing.T) { | ||||
| 			if err := ValidateName(tt.name); (err != nil) != tt.wantErr { | ||||
| 				t.Errorf("Expected error = %v, But got = %v", tt.wantErr, err) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestGetValidator(t *testing.T) { | ||||
| 	// add test validator | ||||
| 	testValidator := func(ans interface{}) error { return nil } | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		name        string | ||||
| 		validatable Validatable | ||||
| 		expected    []survey.Validator | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:        "default", | ||||
| 			validatable: Validatable{}, | ||||
| 			expected:    []survey.Validator{NilValidator}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:        "required", | ||||
| 			validatable: Validatable{Required: true}, | ||||
| 			expected:    []survey.Validator{survey.Required}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:        "unknown type", | ||||
| 			validatable: Validatable{Type: "foo"}, | ||||
| 			expected:    []survey.Validator{NilValidator}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:        "integer", | ||||
| 			validatable: Validatable{Type: "integer"}, | ||||
| 			expected:    []survey.Validator{IntegerValidator}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:        "integer and required", | ||||
| 			validatable: Validatable{Type: "integer", Required: true}, | ||||
| 			expected:    []survey.Validator{survey.Required, IntegerValidator}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:        "additional validator (name)", | ||||
| 			validatable: Validatable{AdditionalValidators: []Validator{NameValidator}}, | ||||
| 			expected:    []survey.Validator{NameValidator}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:        "integer, required and additional validator (name)", | ||||
| 			validatable: Validatable{Type: "integer", Required: true, AdditionalValidators: []Validator{NameValidator}}, | ||||
| 			expected:    []survey.Validator{survey.Required, IntegerValidator, NameValidator}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:        "test validator", | ||||
| 			validatable: Validatable{AdditionalValidators: []Validator{testValidator}}, | ||||
| 			expected:    []survey.Validator{testValidator}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			validator, chain := internalGetValidatorFor(tt.validatable) | ||||
|  | ||||
| 			// if validator chain is empty, only possible result is NilValidator | ||||
| 			if len(chain) == 0 { | ||||
| 				// check that function pointers are equal | ||||
| 				f1 := reflect.ValueOf(NilValidator).Pointer() | ||||
| 				f2 := reflect.ValueOf(validator).Pointer() | ||||
| 				if f1 != f2 { | ||||
| 					t.Error("test failed, expected NilValidator") | ||||
| 				} | ||||
| 			} else { | ||||
| 				if len(tt.expected) != len(chain) { | ||||
| 					t.Errorf("test failed, validator chains don't have the same length, expected %d, got %d", len(tt.expected), len(chain)) | ||||
| 				} | ||||
|  | ||||
| 				for e := range chain { | ||||
| 					// check that function pointers are equal | ||||
| 					f1 := reflect.ValueOf(tt.expected[e]).Pointer() | ||||
| 					f2 := reflect.ValueOf(chain[e]).Pointer() | ||||
| 					if f1 != f2 { | ||||
| 						t.Errorf("test failed, different validators at position %d, expected %v, got %v", e, tt.expected[e], chain[e]) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @@ -1,125 +0,0 @@ | ||||
| package validation | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"gopkg.in/AlecAivazis/survey.v1" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
|  | ||||
| 	dfutil "github.com/devfile/library/pkg/util" | ||||
| ) | ||||
|  | ||||
| // NameValidator provides a Validator view of the ValidateName function. | ||||
| func NameValidator(name interface{}) error { | ||||
| 	if s, ok := name.(string); ok { | ||||
| 		return ValidateName(s) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Errorf("can only validate strings, got %v", name) | ||||
| } | ||||
|  | ||||
| // Validator is a function that validates that the provided interface conforms to expectations or return an error | ||||
| type Validator func(interface{}) error | ||||
|  | ||||
| // NilValidator always validates | ||||
| func NilValidator(interface{}) error { return nil } | ||||
|  | ||||
| // IntegerValidator validates that the provided object can be properly converted to an int value | ||||
| func IntegerValidator(ans interface{}) error { | ||||
| 	if _, ok := ans.(int); ok { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if s, ok := ans.(string); ok { | ||||
| 		_, err := strconv.Atoi(s) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("invalid integer value '%s': %s", s, err) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Errorf("don't know how to convert %v into an integer", ans) | ||||
| } | ||||
|  | ||||
| // PathValidator validates whether the given path exists on the file system | ||||
| func PathValidator(path interface{}) error { | ||||
| 	if s, ok := path.(string); ok { | ||||
| 		exists := util.CheckPathExists(s) | ||||
| 		if exists { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		return fmt.Errorf("path '%s' does not exist on the file system", s) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Errorf("can only validate strings, got %v", path) | ||||
| } | ||||
|  | ||||
| // PortsValidator validates whether all the parts of the input are valid port declarations | ||||
| // examples of valid input are: | ||||
| // 8080 | ||||
| // 8080, 9090/udp | ||||
| // 8080/tcp, 9090/udp | ||||
| func PortsValidator(portsStr interface{}) error { | ||||
| 	if s, ok := portsStr.(string); ok { | ||||
| 		_, err := dfutil.GetContainerPortsFromStrings(dfutil.GetSplitValuesFromStr(s)) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Errorf("can only validate strings, got %v", portsStr) | ||||
| } | ||||
|  | ||||
| // KeyEqValFormatValidator ensures that all the parts of the input follow the key=value format | ||||
| // examples of valid input are: | ||||
| // NAME=JANE | ||||
| // PORT=8080,PATH=/health | ||||
| func KeyEqValFormatValidator(portsStr interface{}) error { | ||||
| 	if s, ok := portsStr.(string); ok { | ||||
| 		parts := dfutil.GetSplitValuesFromStr(s) | ||||
| 		for _, part := range parts { | ||||
| 			kvParts := strings.Split(part, "=") | ||||
| 			if len(kvParts) != 2 { | ||||
| 				return fmt.Errorf("Part '%s' does not have the correct format", part) | ||||
| 			} | ||||
| 			if len(strings.TrimSpace(kvParts[0])) != len(kvParts[0]) || | ||||
| 				len(strings.TrimSpace(kvParts[1])) != len(kvParts[1]) { | ||||
| 				return fmt.Errorf("Spaces are not allowed in '%s'", part) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Errorf("can only validate strings, got %v", portsStr) | ||||
| } | ||||
|  | ||||
| // internalGetValidatorFor exposed for testing purposes | ||||
| func internalGetValidatorFor(prop Validatable) (validator Validator, chain []survey.Validator) { | ||||
| 	// make sure we don't run into issues when composing validators | ||||
| 	validatorChain := make([]survey.Validator, 0, 5) | ||||
|  | ||||
| 	if prop.Required { | ||||
| 		validatorChain = append(validatorChain, survey.Required) | ||||
| 	} | ||||
|  | ||||
| 	switch prop.Type { | ||||
| 	case "integer": | ||||
| 		validatorChain = append(validatorChain, survey.Validator(IntegerValidator)) | ||||
| 	} | ||||
|  | ||||
| 	for i := range prop.AdditionalValidators { | ||||
| 		validatorChain = append(validatorChain, survey.Validator(prop.AdditionalValidators[i])) | ||||
| 	} | ||||
|  | ||||
| 	if len(validatorChain) > 0 { | ||||
| 		return Validator(survey.ComposeValidators(validatorChain...)), validatorChain | ||||
| 	} | ||||
|  | ||||
| 	return NilValidator, validatorChain | ||||
| } | ||||
| @@ -1,100 +0,0 @@ | ||||
| package validation | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestIntegerValidator(t *testing.T) { | ||||
| 	err := IntegerValidator(1) | ||||
| 	if err != nil { | ||||
| 		t.Error("integer validator should validate integers") | ||||
| 	} | ||||
|  | ||||
| 	err = IntegerValidator("1") | ||||
| 	if err != nil { | ||||
| 		t.Error("integer validator should validate integers as string") | ||||
| 	} | ||||
|  | ||||
| 	err = IntegerValidator(new(interface{})) | ||||
| 	if err == nil { | ||||
| 		t.Error("integer validator shouldn't validate unknown types") | ||||
| 	} else { | ||||
| 		if !strings.Contains(err.Error(), "don't know how to convert") { | ||||
| 			t.Error("integer validator should report error that it can't convert unknown type") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestNilValidator(t *testing.T) { | ||||
| 	err := NilValidator(new(interface{})) | ||||
| 	if err != nil { | ||||
| 		t.Error("nil validator should always validate any input") | ||||
| 	} | ||||
|  | ||||
| 	err = NilValidator(nil) | ||||
| 	if err != nil { | ||||
| 		t.Error("nil validator should always validate even nil") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestNameValidator(t *testing.T) { | ||||
| 	// note that we're just testing a single case here since presumably the underlying implementation is already tested in k8s | ||||
| 	err := NameValidator("some-valid-name") | ||||
| 	if err != nil { | ||||
| 		t.Errorf("name validator should have accepted name, but got: %v instead", err) | ||||
| 	} | ||||
|  | ||||
| 	err = NameValidator(new(interface{})) | ||||
| 	if err == nil { | ||||
| 		t.Error("name validator should only attempt to validate non-nil strings") | ||||
| 	} else { | ||||
| 		if !strings.Contains(err.Error(), "can only validate strings") { | ||||
| 			t.Error("name validator should report error that it can only valida strings") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestPathValidator(t *testing.T) { | ||||
| 	ex, err := os.Executable() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	exPath := filepath.Dir(ex) | ||||
|  | ||||
| 	err = PathValidator(exPath) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("path validator should have accepted an existing path, but got: %v instead", err) | ||||
| 	} | ||||
|  | ||||
| 	err = PathValidator("/tmp/1ewfvjnfkhvhbf") | ||||
| 	if err == nil { | ||||
| 		t.Error("path validator should return an error when the path does not exist") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestPortValidator(t *testing.T) { | ||||
| 	err := PortsValidator("8080,9090/udp") | ||||
| 	if err != nil { | ||||
| 		t.Errorf("port validator should have accepted a correct port declaration, but got: %v instead", err) | ||||
| 	} | ||||
|  | ||||
| 	err = PortsValidator("dummy") | ||||
| 	if err == nil { | ||||
| 		t.Error("port validator should return an error when the path does not exist") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestKeyEqValFormatValidator(t *testing.T) { | ||||
| 	err := KeyEqValFormatValidator("NAME=VALUE,K=V") | ||||
| 	if err != nil { | ||||
| 		t.Errorf("key-eq-val-format validator should have accepted an correct port declaration, but got: %v instead", err) | ||||
| 	} | ||||
|  | ||||
| 	err = KeyEqValFormatValidator("dummy") | ||||
| 	if err == nil { | ||||
| 		t.Error("key-eq-val-format validator should return an error when the path does not exist") | ||||
| 	} | ||||
| } | ||||
| @@ -1,23 +0,0 @@ | ||||
| package project | ||||
|  | ||||
| import ( | ||||
| 	applabels "github.com/redhat-developer/odo/pkg/application/labels" | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
| ) | ||||
|  | ||||
| // GetSelector returns a selector to filter resource under the current project created by odo | ||||
| func GetSelector() string { | ||||
| 	labels := map[string]string{ | ||||
| 		applabels.ManagedBy: "odo", | ||||
| 	} | ||||
|  | ||||
| 	return util.ConvertLabelsToSelector(labels) | ||||
| } | ||||
|  | ||||
| func GetNonOdoSelector() string { | ||||
| 	labels := map[string]string{ | ||||
| 		applabels.ManagedBy: "!odo", | ||||
| 	} | ||||
|  | ||||
| 	return util.ConvertLabelsToSelector(labels) | ||||
| } | ||||
| @@ -11,9 +11,10 @@ import ( | ||||
| 	dfutil "github.com/devfile/library/pkg/util" | ||||
| 	indexSchema "github.com/devfile/registry-support/index/generator/schema" | ||||
| 	"github.com/devfile/registry-support/registry-library/library" | ||||
| 	registryUtil "github.com/redhat-developer/odo/pkg/odo/cli/preference/registry/util" | ||||
| 	"github.com/zalando/go-keyring" | ||||
|  | ||||
| 	registryUtil "github.com/redhat-developer/odo/pkg/odo/cli/preference/registry/util" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/component" | ||||
| 	"github.com/redhat-developer/odo/pkg/log" | ||||
| 	"github.com/redhat-developer/odo/pkg/preference" | ||||
| @@ -116,7 +117,7 @@ func (o RegistryClient) ListDevfileStacks(registryName string) (DevfileStackList | ||||
| 		retrieveRegistryIndices.Add(util.ConcurrentTask{ToRun: func(errChannel chan error) { | ||||
| 			registryDevfiles, err := getRegistryStacks(o.preferenceClient, registry) | ||||
| 			if err != nil { | ||||
| 				log.Warningf("Registry %s is not set up properly with error: %v, please check the registry URL and credential (refer `odo registry update --help`)\n", registry.Name, err) | ||||
| 				log.Warningf("Registry %s is not set up properly with error: %v, please check the registry URL and credential (refer `odo preference registry update --help`)\n", registry.Name, err) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/golang/mock/gomock" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/kclient" | ||||
| 	"github.com/redhat-developer/odo/pkg/testingutil/filesystem" | ||||
|  | ||||
| @@ -131,7 +132,7 @@ func TestClientUploadWithConsent(t *testing.T) { | ||||
| 			if err != nil { | ||||
| 				t.Error(err) | ||||
| 			} | ||||
| 			uploadData := fakeTelemetryData("odo create", tt.err, context.Background()) | ||||
| 			uploadData := fakeTelemetryData("odo init", tt.err, context.Background()) | ||||
| 			// upload the data to Segment | ||||
| 			if err = c.Upload(uploadData); err != nil { | ||||
| 				t.Error(err) | ||||
| @@ -256,7 +257,7 @@ func TestClientUploadWithContext(t *testing.T) { | ||||
| 		switch k { | ||||
| 		case scontext.ComponentType: | ||||
| 			scontext.SetComponentType(ctx, v) | ||||
| 			uploadData = fakeTelemetryData("odo create", nil, ctx) | ||||
| 			uploadData = fakeTelemetryData("odo init", nil, ctx) | ||||
| 		case scontext.ClusterType: | ||||
| 			fakeClient, _ := kclient.FakeNew() | ||||
| 			scontext.SetClusterType(ctx, fakeClient) | ||||
|   | ||||
| @@ -72,7 +72,7 @@ func convertType(crd *spec.Schema, value string) interface{} { | ||||
| 		if crd.Type.Contains("integer") { | ||||
| 			intv, err := strconv.ParseInt(value, 10, 64) | ||||
| 			if err == nil { | ||||
| 				return int64(intv) | ||||
| 				return intv | ||||
| 			} | ||||
| 		} | ||||
| 		if crd.Type.Contains("number") { | ||||
| @@ -91,7 +91,7 @@ func convertType(crd *spec.Schema, value string) interface{} { | ||||
| 		// no crd information available, guess the type depending on the value | ||||
| 		intv, err := strconv.ParseInt(value, 10, 64) | ||||
| 		if err == nil { | ||||
| 			return int64(intv) | ||||
| 			return intv | ||||
| 		} | ||||
|  | ||||
| 		floatv, err := strconv.ParseFloat(value, 64) | ||||
|   | ||||
| @@ -3,16 +3,14 @@ package service | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
|  | ||||
| 	devfile "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" | ||||
| 	"github.com/devfile/library/pkg/devfile/generator" | ||||
| 	devfilefs "github.com/devfile/library/pkg/testingutil/filesystem" | ||||
| 	"github.com/ghodss/yaml" | ||||
| 	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" | ||||
| 	v1 "k8s.io/api/apps/v1" | ||||
| 	kerrors "k8s.io/apimachinery/pkg/api/errors" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| @@ -22,6 +20,10 @@ import ( | ||||
| 	"k8s.io/klog/v2" | ||||
| 	ctrl "sigs.k8s.io/controller-runtime" | ||||
|  | ||||
| 	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" | ||||
|  | ||||
| 	sboApi "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1" | ||||
| 	sboKubernetes "github.com/redhat-developer/service-binding-operator/pkg/client/kubernetes" | ||||
| 	sboPipeline "github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline" | ||||
| @@ -94,7 +96,6 @@ func pushLinksWithOperator(client kclient.ClientInterface, k8sComponents []devfi | ||||
| 		delete(deployed, u.GetKind()+"/"+crdName) | ||||
| 		if err != nil { | ||||
| 			if strings.Contains(err.Error(), "already exists") { | ||||
| 				// this could be the case when "odo push" was executed after making change to code but there was no change to the service itself | ||||
| 				// TODO: better way to handle this might be introduced by https://github.com/redhat-developer/odo/issues/4553 | ||||
| 				continue // this ensures that services slice is not updated | ||||
| 			} else { | ||||
| @@ -103,8 +104,8 @@ func pushLinksWithOperator(client kclient.ClientInterface, k8sComponents []devfi | ||||
| 		} | ||||
|  | ||||
| 		// uncomment/modify when service linking is enabled in v3 | ||||
| 		//name := u.GetName() | ||||
| 		//log.Successf("Created link %q using Service Binding Operator on the cluster; component will be restarted", name) | ||||
| 		// name := u.GetName() | ||||
| 		// log.Successf("Created link %q using Service Binding Operator on the cluster; component will be restarted", name) | ||||
| 		restartNeeded = true | ||||
| 	} | ||||
|  | ||||
| @@ -226,7 +227,7 @@ func pushLinksWithoutOperator(client kclient.ClientInterface, k8sComponents []de | ||||
| 			} | ||||
| 			restartRequired = true | ||||
| 			// uncomment/modify when service linking is enabled in v3 | ||||
| 			//log.Successf("Deleted link %q on the cluster; component will be restarted", linkName) | ||||
| 			// log.Successf("Deleted link %q on the cluster; component will be restarted", linkName) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -320,7 +321,7 @@ func pushLinksWithoutOperator(client kclient.ClientInterface, k8sComponents []de | ||||
| 			} | ||||
| 			restartRequired = true | ||||
| 			// uncomment/modify when service linking is enabled in v3 | ||||
| 			//log.Successf("Created link %q on the cluster; component will be restarted", linkName) | ||||
| 			// log.Successf("Created link %q on the cluster; component will be restarted", linkName) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -143,11 +143,6 @@ func SplitServiceKindName(serviceName string) (string, string, error) { | ||||
| 	return kind, name, nil | ||||
| } | ||||
|  | ||||
| // ListDevfileLinks returns the names of the links defined in a Devfile | ||||
| func ListDevfileLinks(devfileObj parser.DevfileObj, context string) ([]string, error) { | ||||
| 	return listDevfileLinks(devfileObj, context, devfilefs.DefaultFs{}) | ||||
| } | ||||
|  | ||||
| func listDevfileLinks(devfileObj parser.DevfileObj, context string, fs devfilefs.Filesystem) ([]string, error) { | ||||
| 	if devfileObj.Data == nil { | ||||
| 		return nil, nil | ||||
|   | ||||
| @@ -61,7 +61,7 @@ func (a Adapter) SyncFiles(syncParameters common.SyncParameters) (bool, error) { | ||||
| 	// | ||||
| 	// 2) For every other push/sync call after the first, don't run the file indexer, instead we use | ||||
| 	//    the watch events to determine what changed, and ensure that the index is then updated based | ||||
| 	//    on the watch events (to ensure future 'odo push' calls are correct) | ||||
| 	//    on the watch events (to ensure future calls are correct) | ||||
|  | ||||
| 	// True if the index was updated based on the deleted/changed files values from the watch (and | ||||
| 	// thus the indexer doesn't need to run), false otherwise | ||||
| @@ -114,7 +114,7 @@ func (a Adapter) SyncFiles(syncParameters common.SyncParameters) (bool, error) { | ||||
|  | ||||
| 		// Run the indexer and find the modified/added/deleted/renamed files | ||||
| 		var err error | ||||
| 		ret, err = util.RunIndexerWithRemote(pushParameters.Path, absIgnoreRules, pushParameters.IgnoredFiles, syncParameters.Files) | ||||
| 		ret, err = util.RunIndexerWithRemote(pushParameters.Path, pushParameters.IgnoredFiles, syncParameters.Files) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return false, fmt.Errorf("unable to run indexer: %w", err) | ||||
|   | ||||
| @@ -77,7 +77,7 @@ func makeTar(srcPath, destPath string, writer io.Writer, files []string, globExp | ||||
| 	uniquePaths := make(map[string]bool) | ||||
| 	klog.V(4).Infof("makeTar arguments: srcPath: %s, destPath: %s, files: %+v", srcPath, destPath, files) | ||||
| 	if len(files) != 0 { | ||||
| 		//watchTar | ||||
| 		// watchTar | ||||
| 		for _, fileName := range files { | ||||
|  | ||||
| 			if _, ok := uniquePaths[fileName]; ok { | ||||
| @@ -99,8 +99,7 @@ func makeTar(srcPath, destPath string, writer io.Writer, files []string, globExp | ||||
| 				// Fetch path of source file relative to that of source base path so that it can be passed to recursiveTar | ||||
| 				// which uses path relative to base path for taro header to correctly identify file location when untarred | ||||
|  | ||||
| 				// Yes, now that the file exists, now we need to get the absolute path.. if we don't, then when we pass in: | ||||
| 				// 'odo push --context foobar' instead of 'odo push --context ~/foobar' it will NOT work.. | ||||
| 				// now that the file exists, now we need to get the absolute path | ||||
| 				fileAbsolutePath, err := dfutil.GetAbsPath(fileName) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| @@ -164,7 +163,7 @@ func linearTar(srcBase, srcFile, destBase, destFile string, tw *taro.Writer, fs | ||||
| 			return err | ||||
| 		} | ||||
| 		if len(files) == 0 { | ||||
| 			//case empty directory | ||||
| 			// case empty directory | ||||
| 			hdr, _ := taro.FileInfoHeader(stat, joinedPath) | ||||
| 			hdr.Name = destFile | ||||
| 			if err := tw.WriteHeader(hdr); err != nil { | ||||
| @@ -173,7 +172,7 @@ func linearTar(srcBase, srcFile, destBase, destFile string, tw *taro.Writer, fs | ||||
| 		} | ||||
| 		return nil | ||||
| 	} else if stat.Mode()&os.ModeSymlink != 0 { | ||||
| 		//case soft link | ||||
| 		// case soft link | ||||
| 		hdr, _ := taro.FileInfoHeader(stat, joinedPath) | ||||
| 		target, err := os.Readlink(joinedPath) | ||||
| 		if err != nil { | ||||
| @@ -186,7 +185,7 @@ func linearTar(srcBase, srcFile, destBase, destFile string, tw *taro.Writer, fs | ||||
| 			return err | ||||
| 		} | ||||
| 	} else { | ||||
| 		//case regular file or other file type like pipe | ||||
| 		// case regular file or other file type like pipe | ||||
| 		hdr, err := taro.FileInfoHeader(stat, joinedPath) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
|   | ||||
| @@ -11,7 +11,6 @@ import ( | ||||
| 	devfileCtx "github.com/devfile/library/pkg/devfile/parser/context" | ||||
| 	"github.com/devfile/library/pkg/devfile/parser/data" | ||||
| 	devfilefs "github.com/devfile/library/pkg/testingutil/filesystem" | ||||
| 	"github.com/redhat-developer/odo/pkg/util" | ||||
| ) | ||||
|  | ||||
| // GetFakeContainerComponent returns a fake container component for testing | ||||
| @@ -100,186 +99,6 @@ func GetTestDevfileObj(fs devfilefs.Filesystem) parser.DevfileObj { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetTestDevfileObjWithMultipleEndpoints returns a devfile object with multiple endpoints for testing | ||||
| func GetTestDevfileObjWithMultipleEndpoints(fs devfilefs.Filesystem) parser.DevfileObj { | ||||
| 	devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion200)) | ||||
| 	_ = devfileData.AddComponents([]v1.Component{ | ||||
| 		{ | ||||
| 			Name: "runtime", | ||||
| 			ComponentUnion: v1.ComponentUnion{ | ||||
| 				Container: &v1.ContainerComponent{ | ||||
| 					Endpoints: []v1.Endpoint{ | ||||
| 						{ | ||||
| 							Name:       "port-3030", | ||||
| 							TargetPort: 3030, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name:       "port-3000", | ||||
| 							TargetPort: 3000, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name: "runtime-debug", | ||||
| 			ComponentUnion: v1.ComponentUnion{ | ||||
| 				Container: &v1.ContainerComponent{ | ||||
| 					Endpoints: []v1.Endpoint{ | ||||
| 						{ | ||||
| 							Name:       "port-8080", | ||||
| 							TargetPort: 8080, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	return parser.DevfileObj{ | ||||
| 		Ctx:  devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath), | ||||
| 		Data: devfileData, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // DevfileObjWithInternalNoneEndpoints returns a devfile object with internal endpoints for testing | ||||
| func DevfileObjWithInternalNoneEndpoints(fs devfilefs.Filesystem) parser.DevfileObj { | ||||
| 	devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion200)) | ||||
|  | ||||
| 	_ = devfileData.AddComponents([]v1.Component{ | ||||
| 		{ | ||||
| 			Name: "runtime", | ||||
| 			ComponentUnion: v1.ComponentUnion{ | ||||
| 				Container: &v1.ContainerComponent{ | ||||
| 					Endpoints: []v1.Endpoint{ | ||||
| 						{ | ||||
| 							Name:       "port-3030", | ||||
| 							TargetPort: 3030, | ||||
| 							Exposure:   v1.NoneEndpointExposure, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name:       "port-3000", | ||||
| 							TargetPort: 3000, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name: "runtime-debug", | ||||
| 			ComponentUnion: v1.ComponentUnion{ | ||||
| 				Container: &v1.ContainerComponent{ | ||||
| 					Endpoints: []v1.Endpoint{ | ||||
| 						{ | ||||
| 							Name:       "port-8080", | ||||
| 							TargetPort: 8080, | ||||
| 							Exposure:   v1.InternalEndpointExposure, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
|  | ||||
| 	return parser.DevfileObj{ | ||||
| 		Ctx:  devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath), | ||||
| 		Data: devfileData, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // DevfileObjWithSecureEndpoints returns a devfile object with internal endpoints for testing | ||||
| func DevfileObjWithSecureEndpoints(fs devfilefs.Filesystem) parser.DevfileObj { | ||||
| 	devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion200)) | ||||
|  | ||||
| 	_ = devfileData.AddComponents([]v1.Component{ | ||||
| 		{ | ||||
| 			Name: "runtime", | ||||
| 			ComponentUnion: v1.ComponentUnion{ | ||||
| 				Container: &v1.ContainerComponent{ | ||||
| 					Endpoints: []v1.Endpoint{ | ||||
| 						{ | ||||
| 							Name:       "port-3030", | ||||
| 							TargetPort: 3030, | ||||
| 							Protocol:   v1.WSSEndpointProtocol, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name:       "port-3000", | ||||
| 							TargetPort: 3000, | ||||
| 							Protocol:   v1.HTTPSEndpointProtocol, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name: "runtime-debug", | ||||
| 			ComponentUnion: v1.ComponentUnion{ | ||||
| 				Container: &v1.ContainerComponent{ | ||||
| 					Endpoints: []v1.Endpoint{ | ||||
| 						{ | ||||
| 							Name:       "port-8080", | ||||
| 							TargetPort: 8080, | ||||
| 							Secure:     util.GetBoolPtr(true), | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	return parser.DevfileObj{ | ||||
| 		Ctx:  devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath), | ||||
| 		Data: devfileData, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetTestDevfileObjWithPath returns a devfile object for testing | ||||
| func GetTestDevfileObjWithPath(fs devfilefs.Filesystem) parser.DevfileObj { | ||||
| 	devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion200)) | ||||
|  | ||||
| 	_ = devfileData.AddCommands([]v1.Command{ | ||||
| 		{ | ||||
| 			Id: "devbuild", | ||||
| 			CommandUnion: v1.CommandUnion{ | ||||
| 				Exec: &v1.ExecCommand{ | ||||
| 					WorkingDir: "/projects/nodejs-starter", | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	_ = devfileData.AddComponents([]v1.Component{ | ||||
| 		{ | ||||
| 			Name: "runtime", | ||||
| 			ComponentUnion: v1.ComponentUnion{ | ||||
| 				Container: &v1.ContainerComponent{ | ||||
| 					Container: v1.Container{ | ||||
| 						Image: "quay.io/nodejs-12", | ||||
| 					}, | ||||
| 					Endpoints: []v1.Endpoint{ | ||||
| 						{ | ||||
| 							Name:       "port-3030", | ||||
| 							TargetPort: 3000, | ||||
| 							Path:       "/test", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name: "loadbalancer", | ||||
| 			ComponentUnion: v1.ComponentUnion{ | ||||
| 				Container: &v1.ContainerComponent{ | ||||
| 					Container: v1.Container{ | ||||
| 						Image: "quay.io/nginx", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	return parser.DevfileObj{ | ||||
| 		Ctx:  devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath), | ||||
| 		Data: devfileData, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetTestDevfileObjWithPreStopEvents returns a devfile object with preStop event. | ||||
| // This function can further be extended to accept other type of events. | ||||
| func GetTestDevfileObjWithPreStopEvents(fs devfilefs.Filesystem, preStopId, preStopCMD string) parser.DevfileObj { | ||||
|   | ||||
| @@ -1,130 +0,0 @@ | ||||
| package testingutil | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| ) | ||||
|  | ||||
| // TempMkdir creates a temporary directory | ||||
| func TempMkdir(parentDir string, newDirPrefix string) (string, error) { | ||||
| 	parentDir = filepath.FromSlash(parentDir) | ||||
| 	dir, err := ioutil.TempDir(parentDir, newDirPrefix) | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("failed to create dir with prefix %s in directory %s: %w", newDirPrefix, parentDir, err) | ||||
| 	} | ||||
| 	return dir, nil | ||||
| } | ||||
|  | ||||
| // TempMkFile creates a temporary file. | ||||
| func TempMkFile(dir string, fileName string) (string, error) { | ||||
| 	dir = filepath.FromSlash(dir) | ||||
| 	f, err := ioutil.TempFile(dir, fileName) | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("failed to create test file %s in dir %s: %w", fileName, dir, err) | ||||
| 	} | ||||
| 	if err := f.Close(); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return f.Name(), nil | ||||
| } | ||||
|  | ||||
| // FileType custom type to indicate type of file | ||||
| type FileType int | ||||
|  | ||||
| const ( | ||||
| 	// RegularFile enum to represent regular file | ||||
| 	RegularFile FileType = 0 | ||||
| 	// Directory enum to represent directory | ||||
| 	Directory FileType = 1 | ||||
| ) | ||||
|  | ||||
| // ModificationType custom type to indicate file modification type | ||||
| type ModificationType string | ||||
|  | ||||
| const ( | ||||
| 	// UPDATE enum representing update operation on a file | ||||
| 	UPDATE ModificationType = "update" | ||||
| 	// CREATE enum representing create operation for a file/folder | ||||
| 	CREATE ModificationType = "create" | ||||
| 	// DELETE enum representing delete operation for a file/folder | ||||
| 	DELETE ModificationType = "delete" | ||||
| 	// APPEND enum representing append operation on a file | ||||
| 	APPEND ModificationType = "append" | ||||
| ) | ||||
|  | ||||
| // FileProperties to contain meta-data of a file like, file/folder name, file/folder parent dir, file type and desired file modification type | ||||
| type FileProperties struct { | ||||
| 	FilePath         string | ||||
| 	FileParent       string | ||||
| 	FileType         FileType | ||||
| 	ModificationType ModificationType | ||||
| } | ||||
|  | ||||
| // SimulateFileModifications mock function to simulate requested file/folder operation | ||||
| // Parameters: | ||||
| //	basePath: The parent directory for file/folder involved in desired file operation | ||||
| //	fileModification: Meta-data of file/folder | ||||
| // Returns: | ||||
| //	path to file/folder involved in the operation | ||||
| //	error if any or nil | ||||
| func SimulateFileModifications(basePath string, fileModification FileProperties) (string, error) { | ||||
| 	// Files/folders intended to be directly under basepath will be indicated by fileModification.FileParent set to empty string | ||||
| 	if fileModification.FileParent != "" { | ||||
| 		// If fileModification.FileParent is not empty, use it to generate file/folder absolute path | ||||
| 		basePath = filepath.Join(basePath, fileModification.FileParent) | ||||
| 	} | ||||
|  | ||||
| 	switch fileModification.ModificationType { | ||||
| 	case CREATE: | ||||
| 		if fileModification.FileType == Directory { | ||||
| 			filePath, err := TempMkdir(basePath, fileModification.FilePath) | ||||
| 			// t.Logf("In simulateFileModifications, Attempting to create folder %s in %s. Error : %v", fileModification.filePath, basePath, err) | ||||
| 			return filePath, err | ||||
| 		} else if fileModification.FileType == RegularFile { | ||||
| 			folderPath, err := TempMkFile(basePath, fileModification.FilePath) | ||||
| 			// t.Logf("In simulateFileModifications, Attempting to create file %s in %s", fileModification.filePath, basePath) | ||||
| 			return folderPath, err | ||||
| 		} | ||||
| 	case DELETE: | ||||
| 		if fileModification.FileType == Directory { | ||||
| 			return filepath.Join(basePath, fileModification.FilePath), os.RemoveAll(filepath.Join(basePath, fileModification.FilePath)) | ||||
| 		} else if fileModification.FileType == RegularFile { | ||||
| 			return filepath.Join(basePath, fileModification.FilePath), os.Remove(filepath.Join(basePath, fileModification.FilePath)) | ||||
| 		} | ||||
| 	case UPDATE: | ||||
| 		if fileModification.FileType == Directory { | ||||
| 			return "", fmt.Errorf("Updating directory %s is not supported", fileModification.FilePath) | ||||
| 		} else if fileModification.FileType == RegularFile { | ||||
| 			f, err := os.Open(filepath.Join(basePath, fileModification.FilePath)) | ||||
| 			if err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 			if _, err := f.WriteString("Hello from Odo"); err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 			if err := f.Sync(); err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 			if err := f.Close(); err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 			return filepath.Join(basePath, fileModification.FilePath), nil | ||||
| 		} | ||||
| 	case APPEND: | ||||
| 		if fileModification.FileType == RegularFile { | ||||
| 			err := ioutil.WriteFile(filepath.Join(basePath, fileModification.FilePath), []byte("// Check watch command"), os.ModeAppend) | ||||
| 			if err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 			return filepath.Join(basePath, fileModification.FilePath), nil | ||||
| 		} | ||||
|  | ||||
| 		return "", fmt.Errorf("Append not supported for file of type %v", fileModification.FileType) | ||||
|  | ||||
| 	default: | ||||
| 		return "", fmt.Errorf("Unsupported file operation %s", fileModification.ModificationType) | ||||
| 	} | ||||
| 	return "", nil | ||||
| } | ||||
| @@ -1,16 +1,5 @@ | ||||
| package util | ||||
|  | ||||
| func GetStringOrEmpty(ptr *string) string { | ||||
| 	return GetStringOrDefault(ptr, "") | ||||
| } | ||||
|  | ||||
| func GetStringOrDefault(ptr *string, defaultValue string) string { | ||||
| 	if ptr == nil { | ||||
| 		return defaultValue | ||||
| 	} | ||||
| 	return *ptr | ||||
| } | ||||
|  | ||||
| func GetIntOrDefault(ptr *int, defaultValue int) int { | ||||
| 	if ptr == nil { | ||||
| 		return defaultValue | ||||
|   | ||||
| @@ -209,7 +209,7 @@ func WriteFile(newFileMap map[string]FileData, resolvedPath string) error { | ||||
| // RunIndexerWithRemote reads the existing index from the given directory and runs the indexer on it | ||||
| // with the given ignore rules | ||||
| // it also adds the file index to the .gitignore file and resolves the path | ||||
| func RunIndexerWithRemote(directory string, absoluteIgnoreRules []string, originalIgnoreRules []string, remoteDirectories map[string]string) (ret IndexerRet, err error) { | ||||
| func RunIndexerWithRemote(directory string, originalIgnoreRules []string, remoteDirectories map[string]string) (ret IndexerRet, err error) { | ||||
| 	directory = filepath.FromSlash(directory) | ||||
| 	ret.ResolvedPath, err = ResolveIndexFilePath(directory) | ||||
| 	if err != nil { | ||||
| @@ -293,8 +293,7 @@ func runIndexerWithExistingFileIndex(directory string, ignoreRules []string, rem | ||||
| 				// Fetch path of source file relative to that of source base path so that it can be passed to recursiveTar | ||||
| 				// which uses path relative to base path for taro header to correctly identify file location when untarred | ||||
|  | ||||
| 				// Yes, now that the file exists, now we need to get the absolute path.. if we don't, then when we pass in: | ||||
| 				// 'odo push --context foobar' instead of 'odo push --context ~/foobar' it will NOT work.. | ||||
| 				// now that the file exists, now we need to get the absolute path | ||||
| 				fileAbsolutePath, err := dfutil.GetAbsPath(fileName) | ||||
| 				if err != nil { | ||||
| 					return IndexerRet{}, err | ||||
|   | ||||
| @@ -7,7 +7,6 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"hash/adler32" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| @@ -759,11 +758,6 @@ func SafeGetBool(b *bool) bool { | ||||
| 	return *b | ||||
| } | ||||
|  | ||||
| // GetAdler32Value returns an adler32 hash of a string on 8 hexadecimal characters | ||||
| func GetAdler32Value(s string) string { | ||||
| 	return fmt.Sprintf("%08x", adler32.Checksum([]byte(s))) | ||||
| } | ||||
|  | ||||
| // IsPortFree checks if the port on localhost is free to use | ||||
| func IsPortFree(port int) bool { | ||||
| 	address := fmt.Sprintf("localhost:%d", port) | ||||
| @@ -776,7 +770,7 @@ func IsPortFree(port int) bool { | ||||
| 	return err == nil | ||||
| } | ||||
|  | ||||
| //WriteToJSONFile writes a struct to json file | ||||
| // WriteToJSONFile writes a struct to json file | ||||
| func WriteToJSONFile(c interface{}, filename string) error { | ||||
| 	data, err := json.Marshal(c) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -69,7 +69,7 @@ func (cw *CmdWrapper) Runner() *CmdWrapper { | ||||
| 			// we retry on success because the user has set “ShouldFail” as true | ||||
| 			// if exit code is 0 which means the program succeeded and hence we retry | ||||
| 			if cw.session.ExitCode() == 0 { | ||||
| 				time.Sleep(time.Duration(cw.intervalSeconds) * time.Second) | ||||
| 				time.Sleep(cw.intervalSeconds * time.Second) | ||||
| 				cw.maxRetry = cw.maxRetry - 1 | ||||
| 				cw.Runner() | ||||
| 			} | ||||
| @@ -77,7 +77,7 @@ func (cw *CmdWrapper) Runner() *CmdWrapper { | ||||
| 		} else { | ||||
| 			// if exit code is not 0 which means the program Failed and hence we retry | ||||
| 			if cw.session.ExitCode() != 0 { | ||||
| 				time.Sleep(time.Duration(cw.intervalSeconds) * time.Second) | ||||
| 				time.Sleep(cw.intervalSeconds * time.Second) | ||||
| 				cw.maxRetry = cw.maxRetry - 1 | ||||
| 				cw.Runner() | ||||
| 			} | ||||
| @@ -114,13 +114,13 @@ func (cw *CmdWrapper) ShouldRun() *CmdWrapper { | ||||
| } | ||||
|  | ||||
| func (cw *CmdWrapper) WithTerminate(timeoutAfter time.Duration, stop chan bool) *CmdWrapper { | ||||
| 	cw.timeout = time.Duration(timeoutAfter) * time.Second | ||||
| 	cw.timeout = timeoutAfter * time.Second | ||||
| 	cw.stopChan = stop | ||||
| 	return cw | ||||
| } | ||||
|  | ||||
| func (cw *CmdWrapper) WithTimeout(timeoutAfter time.Duration) *CmdWrapper { | ||||
| 	cw.timeout = time.Duration(timeoutAfter) * time.Second | ||||
| 	cw.timeout = timeoutAfter * time.Second | ||||
| 	return cw | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -6,21 +6,8 @@ import ( | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	. "github.com/onsi/gomega" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/envinfo" | ||||
| ) | ||||
|  | ||||
| const configFileDirectory = ".odo" | ||||
| const envInfoFile = "env.yaml" | ||||
|  | ||||
| func LocalEnvInfo(context string) *envinfo.EnvSpecificInfo { | ||||
| 	info, err := envinfo.NewEnvSpecificInfo(filepath.Join(context, configFileDirectory, envInfoFile)) | ||||
| 	if err != nil { | ||||
| 		Expect(err).To(Equal(nil)) | ||||
| 	} | ||||
| 	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) { | ||||
|   | ||||
| @@ -85,12 +85,6 @@ func DeleteFile(filepath string) { | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| } | ||||
|  | ||||
| // RenameFile renames a file from oldFileName to newFileName | ||||
| func RenameFile(oldFileName, newFileName string) { | ||||
| 	err := os.Rename(oldFileName, newFileName) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| } | ||||
|  | ||||
| // Chdir change current working dir | ||||
| func Chdir(dir string) { | ||||
| 	fmt.Fprintf(GinkgoWriter, "Setting current dir to: %s\n", dir) | ||||
| @@ -112,22 +106,6 @@ func Getwd() string { | ||||
| 	return dir | ||||
| } | ||||
|  | ||||
| // CopyExampleFile copies an example file from tests/examples/<file-path> | ||||
| // into targetDst | ||||
| func CopyExampleFile(filePath, targetDst string) { | ||||
| 	// filename of this file | ||||
| 	_, filename, _, _ := runtime.Caller(0) | ||||
| 	// path to the examples directory | ||||
| 	examplesDir := filepath.Join(filepath.Dir(filename), "..", "examples") | ||||
|  | ||||
| 	src := filepath.Join(examplesDir, filePath) | ||||
| 	info, err := os.Stat(src) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
|  | ||||
| 	err = dfutil.CopyFile(src, targetDst, info) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| } | ||||
|  | ||||
| // CopyExample copies an example from tests/examples/<binaryOrSource>/<componentName>/<exampleName> into targetDir | ||||
| func CopyExample(exampleName string, targetDir string) { | ||||
| 	// filename of this file | ||||
| @@ -143,21 +121,6 @@ func CopyExample(exampleName string, targetDir string) { | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| } | ||||
|  | ||||
| func CopyManifestFile(fileName, targetDst string) { | ||||
| 	// filename of this file | ||||
| 	_, filename, _, _ := runtime.Caller(0) | ||||
| 	// path to the examples directory | ||||
| 	manifestsDir := filepath.Join(filepath.Dir(filename), "..", "examples", "manifests") | ||||
|  | ||||
| 	src := filepath.Join(manifestsDir, fileName) | ||||
| 	info, err := os.Stat(src) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
|  | ||||
| 	err = dfutil.CopyFile(src, targetDst, info) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
|  | ||||
| } | ||||
|  | ||||
| func GetExamplePath(args ...string) string { | ||||
| 	_, filename, _, _ := runtime.Caller(0) | ||||
| 	path := append([]string{filepath.Dir(filename), "..", "examples"}, args...) | ||||
| @@ -258,12 +221,6 @@ func ListFilesInDir(directoryName string) []string { | ||||
| 	return filesInDirectory | ||||
| } | ||||
|  | ||||
| // CreateSymLink creates a symlink between the oldFile and the newFile | ||||
| func CreateSymLink(oldFileName, newFileName string) { | ||||
| 	err := os.Symlink(oldFileName, newFileName) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| } | ||||
|  | ||||
| // VerifyFileExists receives a path to a file, and returns whether or not | ||||
| // it points to an existing file | ||||
| func VerifyFileExists(filename string) bool { | ||||
| @@ -274,47 +231,6 @@ func VerifyFileExists(filename string) bool { | ||||
| 	return !info.IsDir() | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| // field is 'type' and there are two types throughout the devfile, only one | ||||
| // is replaced with the newValue | ||||
| func ReplaceDevfileField(devfileLocation, field, newValue string) error { | ||||
| 	file, err := ioutil.ReadFile(devfileLocation) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	lines := strings.Split(string(file), "\n") | ||||
| 	for i, line := range lines { | ||||
| 		if strings.Contains(line, field) { | ||||
| 			lineSplit := strings.SplitN(lines[i], ":", 2) | ||||
| 			lineSplit[1] = newValue | ||||
| 			lines[i] = strings.Join(lineSplit, ": ") | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	output := strings.Join(lines, "\n") | ||||
| 	err = ioutil.WriteFile(devfileLocation, []byte(output), 0600) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // FileIsEmpty checks if the file is empty | ||||
| func FileIsEmpty(filename string) (bool, error) { | ||||
| 	file, err := os.Stat(filename) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	if file.Size() > 0 { | ||||
| 		return false, nil | ||||
| 	} | ||||
|  | ||||
| 	return true, nil | ||||
| } | ||||
|  | ||||
| // ReadFile reads the file from the filePath | ||||
| func ReadFile(filePath string) (string, error) { | ||||
| 	data, err := ioutil.ReadFile(filePath) | ||||
|   | ||||
| @@ -2,13 +2,9 @@ package helper | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| @@ -16,9 +12,10 @@ import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/tidwall/gjson" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/preference" | ||||
| 	"github.com/redhat-developer/odo/pkg/segment" | ||||
| 	"github.com/tidwall/gjson" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/tests/helper/reporter" | ||||
|  | ||||
| @@ -67,7 +64,7 @@ func WaitForCmdOut(program string, args []string, timeout int, errOnFail bool, c | ||||
| 				output += "\n" | ||||
| 				output += string(session.Err.Contents()) | ||||
| 			} | ||||
| 			if check(strings.TrimSpace(string(output))) { | ||||
| 			if check(strings.TrimSpace(output)) { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| @@ -103,18 +100,6 @@ func Unindented(jsonStr string) (string, error) { | ||||
| 	return string(obj), err | ||||
| } | ||||
|  | ||||
| // ExtractSubString extracts substring from output, beginning at start and before end | ||||
| func ExtractSubString(output, start, end string) string { | ||||
| 	i := strings.Index(output, start) | ||||
| 	if i >= 0 { | ||||
| 		j := strings.Index(output[i:], end) | ||||
| 		if j >= 0 { | ||||
| 			return output[i : i+j] | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // ExtractLines returns all lines of the given `output` string | ||||
| func ExtractLines(output string) ([]string, error) { | ||||
| 	scanner := bufio.NewScanner(strings.NewReader(output)) | ||||
| @@ -147,132 +132,6 @@ func FindFirstElementIndexMatchingRegExp(slice []string, regularExpression strin | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // WatchNonRetCmdStdOut runs an 'odo watch' command and stores the process' stdout output into buffer. | ||||
| // - startIndicatorFunc should check stdout output and return true when simulation is ready to begin (for example, buffer contains "Waiting for something to change") | ||||
| // - startSimulationCh will be sent a 'true' when startIndicationFunc first returns true, at which point files/directories should be created by associated goroutine | ||||
| // - success function is passed stdout buffer, and should return if the test conditions have passes | ||||
| func WatchNonRetCmdStdOut(cmdStr string, timeout time.Duration, success func(output string) bool, startSimulationCh chan bool, startIndicatorFunc func(output string) bool) (bool, error) { | ||||
| 	var cmd *exec.Cmd | ||||
| 	var buf bytes.Buffer | ||||
| 	var errBuf bytes.Buffer | ||||
|  | ||||
| 	cmdStrParts := strings.Fields(cmdStr) | ||||
|  | ||||
| 	fmt.Fprintln(GinkgoWriter, "Running command: ", cmdStrParts) | ||||
|  | ||||
| 	cmd = exec.Command(cmdStrParts[0], cmdStrParts[1:]...) | ||||
|  | ||||
| 	cmd.Stdout = &buf | ||||
| 	cmd.Stderr = &errBuf | ||||
|  | ||||
| 	ticker := time.NewTicker(10 * time.Second) | ||||
| 	defer ticker.Stop() | ||||
|  | ||||
| 	timeoutCh := make(chan bool) | ||||
| 	go func() { | ||||
| 		time.Sleep(timeout) | ||||
| 		timeoutCh <- true | ||||
| 	}() | ||||
|  | ||||
| 	if err := cmd.Start(); err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	startedFileModification := false | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-timeoutCh: | ||||
| 			if buf.String() != "" { | ||||
| 				_, err := fmt.Fprintln(GinkgoWriter, "Output from stdout ["+cmdStr+"]:") | ||||
| 				Expect(err).To(BeNil()) | ||||
| 				_, err = fmt.Fprintln(GinkgoWriter, buf.String()) | ||||
| 				Expect(err).To(BeNil()) | ||||
| 			} | ||||
| 			errBufStr := errBuf.String() | ||||
| 			if errBufStr != "" { | ||||
| 				_, err := fmt.Fprintln(GinkgoWriter, "Output from stderr ["+cmdStr+"]:") | ||||
| 				Expect(err).To(BeNil()) | ||||
| 				_, err = fmt.Fprintln(GinkgoWriter, errBufStr) | ||||
| 				Expect(err).To(BeNil()) | ||||
| 			} | ||||
| 			Fail(fmt.Sprintf("Timeout after %.2f minutes", timeout.Minutes())) | ||||
| 		case <-ticker.C: // Every 10 seconds... | ||||
|  | ||||
| 			// If we have not yet begun file modification, query the parameter function to see if we should, do so if true | ||||
| 			if !startedFileModification && startIndicatorFunc(buf.String()) { | ||||
| 				startedFileModification = true | ||||
| 				startSimulationCh <- true | ||||
| 			} | ||||
| 			// Call success(...) to determine if stdout contains expected text, exit if true | ||||
| 			if success(buf.String()) { | ||||
| 				if err := cmd.Process.Kill(); err != nil { | ||||
| 					return true, err | ||||
| 				} | ||||
| 				return true, nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // RunCmdWithMatchOutputFromBuffer starts the command, and command stdout is attached to buffer. | ||||
| // we read data from buffer line by line, and if expected string is matched it returns true | ||||
| // It is different from WaitforCmdOut which gives stdout in one go using session.Out.Contents() | ||||
| // for commands like odo log -f which streams continuous data and does not terminate by their own | ||||
| // we need to read the stream data from buffer. | ||||
| func RunCmdWithMatchOutputFromBuffer(timeoutAfter time.Duration, matchString, program string, args ...string) (bool, error) { | ||||
| 	var buf, errBuf bytes.Buffer | ||||
|  | ||||
| 	command := exec.Command(program, args...) | ||||
| 	command.Stdout = &buf | ||||
| 	command.Stderr = &errBuf | ||||
|  | ||||
| 	timeoutCh := time.After(timeoutAfter) | ||||
| 	matchOutputCh := make(chan bool) | ||||
| 	errorCh := make(chan error) | ||||
|  | ||||
| 	_, err := fmt.Fprintln(GinkgoWriter, runningCmd(command)) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	err = command.Start() | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	// go routine which is reading data from buffer until expected string matched | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			line, err := buf.ReadString('\n') | ||||
| 			if err != nil && err != io.EOF { | ||||
| 				errorCh <- err | ||||
| 			} | ||||
| 			if len(line) > 0 { | ||||
| 				_, err = fmt.Fprintln(GinkgoWriter, line) | ||||
| 				if err != nil { | ||||
| 					errorCh <- err | ||||
| 				} | ||||
| 				if strings.Contains(line, matchString) { | ||||
| 					matchOutputCh <- true | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-timeoutCh: | ||||
| 			fmt.Fprintln(GinkgoWriter, errBuf.String()) | ||||
| 			return false, errors.New("Timeout waiting for the condition") | ||||
| 		case <-matchOutputCh: | ||||
| 			return true, nil | ||||
| 		case <-errorCh: | ||||
| 			fmt.Fprintln(GinkgoWriter, errBuf.String()) | ||||
| 			return false, <-errorCh | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // GetUserHomeDir gets the user home directory | ||||
| func GetUserHomeDir() string { | ||||
| 	homeDir, err := os.UserHomeDir() | ||||
| @@ -298,11 +157,6 @@ func GetCliRunner() CliRunner { | ||||
| 	return NewOcRunner("oc") | ||||
| } | ||||
|  | ||||
| // Suffocate the string by removing all the space from it ;-) | ||||
| func Suffocate(s string) string { | ||||
| 	return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(s, " ", ""), "\t", ""), "\n", "") | ||||
| } | ||||
|  | ||||
| // IsJSON returns true if a string is in json format | ||||
| func IsJSON(s string) bool { | ||||
| 	var js map[string]interface{} | ||||
| @@ -430,7 +284,7 @@ func SetProjectName() string { | ||||
| func RunTestSpecs(t *testing.T, description string) { | ||||
| 	os.Setenv(segment.DisableTelemetryEnv, "true") | ||||
| 	RegisterFailHandler(Fail) | ||||
| 	RunSpecsWithDefaultAndCustomReporters(t, description, []Reporter{reporter.JunitReport(t, "../../reports/")}) | ||||
| 	RunSpecsWithDefaultAndCustomReporters(t, description, []Reporter{reporter.JunitReport("../../reports/")}) | ||||
| } | ||||
|  | ||||
| func IsKubernetesCluster() bool { | ||||
|   | ||||
| @@ -1,73 +0,0 @@ | ||||
| package helper | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	. "github.com/onsi/ginkgo" | ||||
| 	. "github.com/onsi/gomega" | ||||
| ) | ||||
|  | ||||
| // HttpWaitForWithStatus periodically (every interval) calls GET to given url | ||||
| // ends when result response contains match string and status code, or after the maxRetry | ||||
| func HttpWaitForWithStatus(url string, match string, maxRetry int, interval int, expectedCode int) { | ||||
| 	fmt.Fprintf(GinkgoWriter, "Checking %s, for %s\n", url, match) | ||||
|  | ||||
| 	var body []byte | ||||
|  | ||||
| 	for i := 0; i < maxRetry; i++ { | ||||
| 		fmt.Fprintf(GinkgoWriter, "try %d of %d\n", i, maxRetry) | ||||
|  | ||||
| 		// #nosec | ||||
| 		// gosec:G107, G402 -> This is safe since it's just used for testing. | ||||
| 		transporter := &http.Transport{ | ||||
| 			TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, | ||||
| 		} | ||||
| 		client := &http.Client{Transport: transporter} | ||||
| 		resp, err := client.Get(url) | ||||
| 		if err != nil { | ||||
| 			// we log the error and sleep again because this could mean the component is not up yet | ||||
| 			fmt.Fprintln(GinkgoWriter, "error while requesting:", err.Error()) | ||||
| 			time.Sleep(time.Duration(interval) * time.Second) | ||||
| 			continue | ||||
| 		} | ||||
| 		defer resp.Body.Close() | ||||
| 		if resp.StatusCode == expectedCode { | ||||
| 			body, _ = ioutil.ReadAll(resp.Body) | ||||
| 			if strings.Contains(string(body), match) { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 		time.Sleep(time.Duration(interval) * time.Second) | ||||
| 	} | ||||
| 	fmt.Fprintf(GinkgoWriter, "Last output from %s: %s\n", url, string(body)) | ||||
| 	Fail(fmt.Sprintf("Failed after %d retries. Content in %s doesn't include '%s'.", maxRetry, url, match)) | ||||
| } | ||||
|  | ||||
| // HttpWaitFor periodically (every interval) calls GET to given url | ||||
| // ends when a 200 HTTP result response contains match string, or after the maxRetry | ||||
| func HttpWaitFor(url string, match string, maxRetry int, interval int) { | ||||
| 	HttpWaitForWithStatus(url, match, maxRetry, interval, 200) | ||||
| } | ||||
|  | ||||
| // HttpFileServer starts a http server with a file handler on the free port provided | ||||
| // the file handler uses the location provided for serving the requests | ||||
| func HttpFileServer(port int, location string) *http.Server { | ||||
| 	addressLook := "localhost:" + strconv.Itoa(port) | ||||
| 	fileHandler := http.FileServer(http.Dir(location)) | ||||
|  | ||||
| 	server := &http.Server{Addr: addressLook, Handler: fileHandler} | ||||
| 	go func() { | ||||
| 		err := server.ListenAndServe() | ||||
| 		if err != http.ErrServerClosed { | ||||
| 			Expect(err).To(BeNil()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return server | ||||
| } | ||||
| @@ -319,7 +319,7 @@ func (kubectl KubectlRunner) WaitForRunnerCmdOut(args []string, timeout int, err | ||||
| 				output += "\n" | ||||
| 				output += string(session.Err.Contents()) | ||||
| 			} | ||||
| 			if check(strings.TrimSpace(string(output))) { | ||||
| 			if check(strings.TrimSpace(output)) { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -16,10 +16,6 @@ import ( | ||||
| 	"github.com/redhat-developer/odo/pkg/component/labels" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	ResourceTypeRoute = "route" | ||||
| ) | ||||
|  | ||||
| type OcRunner struct { | ||||
| 	// path to oc binary | ||||
| 	path string | ||||
| @@ -79,29 +75,6 @@ func (oc OcRunner) GetFirstURL(component string, app string, project string) str | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // StatFileInPodContainer returns stat result of filepath in a container of a pod of given component, in a given app, in a given project. | ||||
| // It also strips access time information as it vaires accross file systems/kernel configs, and we are not interested | ||||
| // in it anyway | ||||
| func StatFileInPodContainer(runner CliRunner, cmpName, containerName, appName, project, filepath string) string { | ||||
| 	podName := runner.GetRunningPodNameByComponent(cmpName, project) | ||||
| 	var result string | ||||
| 	runner.CheckCmdOpInRemoteDevfilePod( | ||||
| 		podName, | ||||
| 		containerName, | ||||
| 		project, | ||||
| 		[]string{"stat", filepath}, | ||||
| 		func(cmdOp string, err error) bool { | ||||
| 			// strip out access info as | ||||
| 			// 1. Touching a file (such as running it in a script) modifies access times. This gives wrong value on mounts without noatime | ||||
| 			// 2. We are not interested in Access info anyway. | ||||
| 			re := regexp.MustCompile("(?m)[\r\n]+^.*Access.*$") | ||||
| 			result = re.ReplaceAllString(cmdOp, "") | ||||
| 			return true | ||||
| 		}, | ||||
| 	) | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| // GetComponentRoutes run command to get the Routes in yaml format for given component | ||||
| func (oc OcRunner) GetComponentRoutes(component string, app string, project string) string { | ||||
| 	session := CmdRunner(oc.path, "get", "route", | ||||
| @@ -486,7 +459,7 @@ func (oc OcRunner) WaitForRunnerCmdOut(args []string, timeout int, errOnFail boo | ||||
| 				output += "\n" | ||||
| 				output += string(session.Err.Contents()) | ||||
| 			} | ||||
| 			if check(strings.TrimSpace(string(output))) { | ||||
| 			if check(strings.TrimSpace(output)) { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -6,60 +6,13 @@ import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	devfilepkg "github.com/devfile/api/v2/pkg/devfile" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/devfile" | ||||
|  | ||||
| 	. "github.com/onsi/ginkgo" | ||||
| 	. "github.com/onsi/gomega" | ||||
| ) | ||||
|  | ||||
| // GetConfigValue returns a local config value of given key or | ||||
| // returns an empty string if value is not set | ||||
| func GetConfigValue(key string) string { | ||||
| 	return GetConfigValueWithContext(key, "") | ||||
| } | ||||
|  | ||||
| // GetConfigValueWithContext returns a local config value of given key and contextdir or | ||||
| // returns an empty string if value is not set | ||||
| func GetConfigValueWithContext(key string, context string) string { | ||||
| 	var stdOut string | ||||
| 	if context != "" { | ||||
| 		stdOut = Cmd("odo", "config", "view", "--context", context).ShouldPass().Out() | ||||
| 	} else { | ||||
| 		stdOut = Cmd("odo", "config", "view").ShouldPass().Out() | ||||
| 	} | ||||
| 	re := regexp.MustCompile(key + `.+`) | ||||
| 	odoConfigKeyValue := re.FindString(stdOut) | ||||
| 	if odoConfigKeyValue == "" { | ||||
| 		return fmt.Sprintf("%s not found", key) | ||||
| 	} | ||||
| 	trimKeyValue := strings.TrimSpace(odoConfigKeyValue) | ||||
| 	if strings.Compare(key, trimKeyValue) != 0 { | ||||
| 		return strings.TrimSpace(strings.SplitN(trimKeyValue, " ", 2)[1]) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // GetLocalEnvInfoValueWithContext returns an envInfo value of given key and contextdir or | ||||
| // returns an empty string if value is not set | ||||
| func GetLocalEnvInfoValueWithContext(key string, context string) string { | ||||
| 	var stdOut string | ||||
| 	if context != "" { | ||||
| 		stdOut = Cmd("odo", "env", "view", "--context", context).ShouldPass().Out() | ||||
| 	} else { | ||||
| 		stdOut = Cmd("odo", "env", "view").ShouldPass().Out() | ||||
| 	} | ||||
| 	re := regexp.MustCompile(key + `.+`) | ||||
| 	odoConfigKeyValue := re.FindString(stdOut) | ||||
| 	if odoConfigKeyValue == "" { | ||||
| 		return fmt.Sprintf("%s not found", key) | ||||
| 	} | ||||
| 	trimKeyValue := strings.TrimSpace(odoConfigKeyValue) | ||||
| 	if strings.Compare(key, trimKeyValue) != 0 { | ||||
| 		return strings.TrimSpace(strings.SplitN(trimKeyValue, " ", 2)[1]) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // GetPreferenceValue a global config value of given key or | ||||
| // returns an empty string if value is not set | ||||
| func GetPreferenceValue(key string) string { | ||||
|   | ||||
| @@ -1,128 +0,0 @@ | ||||
| package reporter | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/onsi/ginkgo/config" | ||||
| 	"github.com/onsi/ginkgo/types" | ||||
|  | ||||
| 	. "github.com/onsi/ginkgo" | ||||
| ) | ||||
|  | ||||
| type HTTPMeasurementReporter struct { | ||||
| 	url string | ||||
| } | ||||
|  | ||||
| func NewHTTPMeasurementReporter(url string) *HTTPMeasurementReporter { | ||||
| 	r := HTTPMeasurementReporter{ | ||||
| 		url: url, | ||||
| 	} | ||||
| 	return &r | ||||
| } | ||||
|  | ||||
| func (r *HTTPMeasurementReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { | ||||
| } | ||||
|  | ||||
| func (r *HTTPMeasurementReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {} | ||||
|  | ||||
| func (r *HTTPMeasurementReporter) SpecWillRun(specSummary *types.SpecSummary) {} | ||||
|  | ||||
| func (r *HTTPMeasurementReporter) SpecDidComplete(specSummary *types.SpecSummary) { | ||||
| 	if specSummary.Passed() && specSummary.IsMeasurement { | ||||
|  | ||||
| 		var pr int | ||||
| 		pr, err := getPRNumber() | ||||
| 		if err != nil { | ||||
| 			fmt.Fprintf(GinkgoWriter, "WARNING: unable to get PR number (%v)\n", err) | ||||
| 		} | ||||
|  | ||||
| 		for k, v := range specSummary.Measurements { | ||||
| 			output := map[string]string{} | ||||
| 			output["Number of Samples"] = strconv.Itoa(specSummary.NumberOfSamples) | ||||
| 			output["Measurement"] = k | ||||
| 			output["PR"] = strconv.Itoa(pr) | ||||
| 			output[v.SmallestLabel] = strconv.FormatFloat(v.Smallest, 'f', -1, 64) | ||||
| 			output[v.LargestLabel] = strconv.FormatFloat(v.Largest, 'f', -1, 64) | ||||
| 			output[v.AverageLabel] = strconv.FormatFloat(v.Average, 'f', -1, 64) | ||||
| 			output["Test"] = strings.Join(specSummary.ComponentTexts, "/") | ||||
| 			err := r.SubmitMeasurement(output) | ||||
| 			if err != nil { | ||||
| 				// Just printing info about error. Error while submiting measurement should cause any failures | ||||
| 				fmt.Fprintf(GinkgoWriter, "WARNING: error in SubmitMeasurement (%v)\n", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *HTTPMeasurementReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) {} | ||||
|  | ||||
| func (r *HTTPMeasurementReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) {} | ||||
|  | ||||
| func (r *HTTPMeasurementReporter) SubmitMeasurement(data map[string]string) error { | ||||
| 	client := &http.Client{} | ||||
|  | ||||
| 	req, err := http.NewRequest("GET", r.url, nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	q := req.URL.Query() | ||||
| 	for k, v := range data { | ||||
| 		q.Add(k, v) | ||||
| 	} | ||||
| 	req.URL.RawQuery = q.Encode() | ||||
|  | ||||
| 	resp, err := client.Do(req) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if resp.StatusCode != 200 { | ||||
| 		return fmt.Errorf("error while submiting measurement (StatusCode: %d)", resp.StatusCode) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
|  | ||||
| } | ||||
|  | ||||
| // getPrNumber returns PR number from json in CLONEREFS_OPTIONS environment variable | ||||
| // this env variable is specific to Prow jobs | ||||
| func getPRNumber() (int, error) { | ||||
| 	jsonData := os.Getenv("CLONEREFS_OPTIONS") | ||||
|  | ||||
| 	type pulls struct { | ||||
| 		Number int `json:"number"` | ||||
| 	} | ||||
|  | ||||
| 	type refs struct { | ||||
| 		BaseSha string  `json:"base_sha"` | ||||
| 		Pulls   []pulls `json:"pulls"` | ||||
| 	} | ||||
|  | ||||
| 	type clonerefsOptions struct { | ||||
| 		Refs    []refs `json:"refs"` | ||||
| 		SrcRoot string `json:"src_root"` | ||||
| 	} | ||||
|  | ||||
| 	var data clonerefsOptions | ||||
| 	err := json.Unmarshal([]byte(jsonData), &data) | ||||
| 	if err != nil { | ||||
| 		return 0, fmt.Errorf("error in unmarshalling json") | ||||
| 	} | ||||
|  | ||||
| 	if len(data.Refs) < 1 { | ||||
| 		return 0, fmt.Errorf("no refs in the input json") | ||||
| 	} | ||||
|  | ||||
| 	if len(data.Refs[0].Pulls) < 1 { | ||||
| 		return 0, fmt.Errorf("no refs[0].pulls in the input json") | ||||
| 	} | ||||
|  | ||||
| 	return data.Refs[0].Pulls[0].Number, nil | ||||
|  | ||||
| } | ||||
| @@ -4,7 +4,6 @@ import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/onsi/ginkgo/config" | ||||
| @@ -12,7 +11,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| // JunitReport takes test object and filepath as argument, returns junitReporter object | ||||
| func JunitReport(t *testing.T, filePath string) *reporters.JUnitReporter { | ||||
| func JunitReport(filePath string) *reporters.JUnitReporter { | ||||
| 	time := time.Now() | ||||
| 	if _, err := os.Stat(filePath); os.IsNotExist(err) { | ||||
| 		_ = os.Mkdir(filePath, os.ModePerm) | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	. "github.com/onsi/ginkgo" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/tests/helper" | ||||
| ) | ||||
|  | ||||
| @@ -34,7 +35,7 @@ var _ = Describe("odo devfile registry command tests", func() { | ||||
| 	It("Should fail with an error with no registries", func() { | ||||
| 		helper.Cmd("odo", "preference", "registry", "delete", "DefaultDevfileRegistry", "-f").ShouldPass() | ||||
| 		output := helper.Cmd("odo", "preference", "registry", "list").ShouldFail().Err() | ||||
| 		helper.MatchAllInOutput(output, []string{"No devfile registries added to the configuration. Refer `odo registry add -h` to add one"}) | ||||
| 		helper.MatchAllInOutput(output, []string{"No devfile registries added to the configuration. Refer `odo preference registry add -h` to add one"}) | ||||
| 	}) | ||||
|  | ||||
| 	It("Should fail to update the registry, when registry is not present", func() { | ||||
| @@ -55,7 +56,7 @@ var _ = Describe("odo devfile registry command tests", func() { | ||||
| 			helper.MatchAllInOutput(output, []string{registryName, addRegistryURL}) | ||||
| 		}) | ||||
|  | ||||
| 		It("should pass, when doing odo create with --registry flag", func() { | ||||
| 		It("should pass, when doing odo init with --devfile-registry flag", func() { | ||||
| 			helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs", "--devfile-registry", registryName).ShouldPass() | ||||
| 		}) | ||||
|  | ||||
| @@ -100,7 +101,7 @@ var _ = Describe("odo devfile registry command tests", func() { | ||||
| 				co = fmt.Sprintln(out, err) | ||||
| 				helper.MatchAllInOutput(co, []string{deprecated, docLink}) | ||||
|  | ||||
| 				By("odo registry list is executed, should show the warning", func() { | ||||
| 				By("odo preference 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}) | ||||
|   | ||||
| @@ -1,170 +0,0 @@ | ||||
| package utils | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/pkg/machineoutput" | ||||
|  | ||||
| 	. "github.com/onsi/ginkgo" | ||||
| 	. "github.com/onsi/gomega" | ||||
| 	"github.com/onsi/gomega/gexec" | ||||
| ) | ||||
|  | ||||
| // AnalyzePushConsoleOutput analyzes the output of 'odo push -o json' for the machine readable event push test above. | ||||
| func AnalyzePushConsoleOutput(pushConsoleOutput string) { | ||||
|  | ||||
| 	entries, err := ParseMachineEventJSONLines(pushConsoleOutput) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
|  | ||||
| 	// Ensure we pass a sanity test on the minimum expected entries | ||||
| 	if len(entries) < 4 { | ||||
| 		Fail("Expected at least 4 entries, corresponding to command/action execution.") | ||||
| 	} | ||||
|  | ||||
| 	// Ensure that all logText entries are wrapped inside commandExecutionBegin and commandExecutionComplete entries (e.g. no floating logTexts) | ||||
| 	insideCommandExecution := false | ||||
| 	for _, entry := range entries { | ||||
|  | ||||
| 		if entry.GetType() == machineoutput.TypeDevFileCommandExecutionBegin { | ||||
| 			insideCommandExecution = true | ||||
| 		} | ||||
|  | ||||
| 		if entry.GetType() == machineoutput.TypeDevFileCommandExecutionComplete { | ||||
| 			insideCommandExecution = false | ||||
| 		} | ||||
|  | ||||
| 		if entry.GetType() == machineoutput.TypeLogText { | ||||
| 			Expect(insideCommandExecution).To(Equal(true)) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// Ensure that the log output has the given structure: | ||||
| 	// - look for the expected devbuild events, then look for the expected devrun events. | ||||
| 	expectedEventOrder := []struct { | ||||
| 		entryType   machineoutput.MachineEventLogEntryType | ||||
| 		commandName string | ||||
| 	}{ | ||||
| 		// first the devbuild command (and its action) should run | ||||
| 		{ | ||||
| 			machineoutput.TypeDevFileCommandExecutionBegin, | ||||
| 			"devbuild", | ||||
| 		}, | ||||
| 		{ | ||||
| 			// at least one logged line of text | ||||
| 			machineoutput.TypeLogText, | ||||
| 			"", | ||||
| 		}, | ||||
| 		{ | ||||
| 			machineoutput.TypeDevFileCommandExecutionComplete, | ||||
| 			"devbuild", | ||||
| 		}, | ||||
| 		// next the devbuild command (and its action) should run | ||||
| 		{ | ||||
| 			machineoutput.TypeDevFileCommandExecutionBegin, | ||||
| 			"devrun", | ||||
| 		}, | ||||
| 		{ | ||||
| 			// at least one logged line of text | ||||
| 			machineoutput.TypeLogText, | ||||
| 			"", | ||||
| 		}, | ||||
| 		{ | ||||
| 			machineoutput.TypeDevFileCommandExecutionComplete, | ||||
| 			"devrun", | ||||
| 		}, | ||||
| 	} | ||||
| 	currIndex := -1 | ||||
| 	for _, nextEventOrder := range expectedEventOrder { | ||||
| 		entry, newIndex := findNextEntryByType(currIndex, nextEventOrder.entryType, entries) | ||||
| 		Expect(entry).NotTo(BeNil()) | ||||
| 		Expect(newIndex).To(BeNumerically(">=", 0)) | ||||
| 		Expect(newIndex).To(BeNumerically(">", currIndex)) // monotonically increasing index | ||||
|  | ||||
| 		// We should see devbuild for the first set of events, then devrun | ||||
| 		commandName := machineoutput.GetCommandName(entry) | ||||
| 		Expect(commandName).To(Equal(nextEventOrder.commandName)) | ||||
|  | ||||
| 		currIndex = newIndex | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // ParseMachineEventJSONLines parses console output into machine event log entries | ||||
| func ParseMachineEventJSONLines(consoleOutput string) ([]machineoutput.MachineEventLogEntry, error) { | ||||
|  | ||||
| 	lines := strings.Split(strings.Replace(consoleOutput, "\r\n", "\n", -1), "\n") | ||||
|  | ||||
| 	entries := []machineoutput.MachineEventLogEntry{} | ||||
|  | ||||
| 	// Ensure that all lines can be correctly parsed into their expected JSON structure | ||||
| 	for _, line := range lines { | ||||
|  | ||||
| 		if !strings.HasPrefix(line, "{") { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		lineWrapper := machineoutput.MachineEventWrapper{} | ||||
|  | ||||
| 		err := json.Unmarshal([]byte(line), &lineWrapper) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		entry, err := lineWrapper.GetEntry() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		entries = append(entries, entry) | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return entries, nil | ||||
| } | ||||
|  | ||||
| // findNextEntryByType locates the next entry of a given type within a slice. Currently used for test purposes only. | ||||
| func findNextEntryByType(initialIndex int, typeToFind machineoutput.MachineEventLogEntryType, entries []machineoutput.MachineEventLogEntry) (machineoutput.MachineEventLogEntry, int) { | ||||
|  | ||||
| 	for index, entry := range entries { | ||||
| 		if index < initialIndex { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if entry.GetType() == typeToFind { | ||||
| 			return entry, index | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil, -1 | ||||
|  | ||||
| } | ||||
|  | ||||
| func TerminateSession(session *gexec.Session) { | ||||
| 	if runtime.GOOS == "windows" { | ||||
| 		session.Kill() | ||||
| 	} else { | ||||
| 		session.Terminate() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Given a list of entries, find the most recent one of the given type | ||||
| func GetMostRecentEventOfType(entryType machineoutput.MachineEventLogEntryType, entries []machineoutput.MachineEventLogEntry, required bool) machineoutput.MachineEventLogEntry { | ||||
|  | ||||
| 	for index := len(entries) - 1; index >= 0; index-- { | ||||
|  | ||||
| 		if entries[index].GetType() == entryType { | ||||
| 			return entries[index] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Fail the test if we were expecting at least one event of the type | ||||
| 	if required { | ||||
| 		Fail(fmt.Sprintf("Unable to locate any entries with the required type %v", entryType)) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -1,19 +1,10 @@ | ||||
| package utils | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"index/suffixarray" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/tests/helper" | ||||
|  | ||||
| 	dfutil "github.com/devfile/library/pkg/util" | ||||
|  | ||||
| 	. "github.com/onsi/ginkgo" | ||||
| 	. "github.com/onsi/gomega" | ||||
| ) | ||||
|  | ||||
| @@ -25,226 +16,6 @@ type OdoV2Watch struct { | ||||
| 	SrcType               string | ||||
| } | ||||
|  | ||||
| // OdoWatch creates files, dir in the context and watches for the changes to be pushed | ||||
| // Specify OdoV2Watch for odo version 2(devfile) | ||||
| // platform is kube | ||||
| func OdoWatch(odoV2Watch OdoV2Watch, project, context, flag string, runner interface{}, platform string) { | ||||
|  | ||||
| 	// After the watch command has started (indicated via channel), simulate file system changes | ||||
| 	startSimulationCh := make(chan bool) | ||||
| 	go func() { | ||||
| 		startMsg := <-startSimulationCh | ||||
| 		if startMsg { | ||||
| 			err := os.MkdirAll(filepath.Join(context, ".abc"), 0750) | ||||
| 			Expect(err).To(BeNil()) | ||||
|  | ||||
| 			err = os.MkdirAll(filepath.Join(context, "abcd"), 0750) | ||||
| 			Expect(err).To(BeNil()) | ||||
|  | ||||
| 			_, err = os.Create(filepath.Join(context, "a.txt")) | ||||
| 			Expect(err).To(BeNil()) | ||||
|  | ||||
| 			if odoV2Watch.SrcType == "openjdk" { | ||||
| 				helper.ReplaceString(filepath.Join(context, "src", "main", "java", "MessageProducer.java"), "Hello", "Hello odo") | ||||
| 			} else { | ||||
| 				helper.ReplaceString(filepath.Join(context, "server.js"), "Hello", "Hello odo") | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	success, err := helper.WatchNonRetCmdStdOut( | ||||
| 		"odo watch "+flag+" --context "+context, | ||||
| 		time.Duration(5)*time.Minute, | ||||
| 		func(output string) bool { | ||||
| 			// the test hangs up on the CI when the delay is set to 0 | ||||
| 			// so we only check if the start message was displayed correctly or not | ||||
| 			if strings.Contains(flag, "delay 0") { | ||||
| 				return true | ||||
| 			} | ||||
| 			// Returns true if the test has succeeded, false if not yet | ||||
|  | ||||
| 			stringsMatched := true | ||||
|  | ||||
| 			for _, stringToBeMatched := range odoV2Watch.StringsToBeMatched { | ||||
| 				if !strings.Contains(output, stringToBeMatched) { | ||||
| 					fmt.Fprintln(GinkgoWriter, "Missing string: ", stringToBeMatched) | ||||
| 					stringsMatched = false | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if stringsMatched { | ||||
|  | ||||
| 				// first push is successful | ||||
| 				// now delete a folder and check if the deletion is propagated properly | ||||
| 				// and the file is removed from the cluster | ||||
| 				index := suffixarray.New([]byte(output)) | ||||
| 				offsets := index.Lookup([]byte(filepath.Join(context, "abcd")+" changed"), -1) | ||||
|  | ||||
| 				// the first occurrence of '<target-dir> changed' means the creation of it was pushed to the cluster | ||||
| 				// and the first push was successful | ||||
| 				if len(offsets) == 1 { | ||||
| 					helper.DeleteDir(filepath.Join(context, "abcd")) | ||||
| 				} else if len(offsets) > 1 { | ||||
| 					// the occurrence of 'target-directory' more than once indicates that the deletion was propagated too | ||||
| 					// Verify directory deleted from component pod | ||||
| 					err := validateContainerExecListDir(odoV2Watch, runner, platform, project) | ||||
| 					Expect(err).To(BeNil()) | ||||
| 					return true | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			return false | ||||
| 		}, | ||||
| 		startSimulationCh, | ||||
| 		func(output string) bool { | ||||
| 			// Returns true to indicate the test should begin file system file change simulation | ||||
| 			return strings.Contains(output, "Waiting for something to change") | ||||
| 		}) | ||||
|  | ||||
| 	Expect(success).To(Equal(true)) | ||||
| 	Expect(err).To(BeNil()) | ||||
| } | ||||
|  | ||||
| // OdoWatchWithDebug changes files in the context and watches for the changes to be pushed | ||||
| // It checks if the push is in debug mode or not | ||||
| // After a successful push with watch, it tries to start a debug session | ||||
| func OdoWatchWithDebug(odoV2Watch OdoV2Watch, context, flag string) { | ||||
|  | ||||
| 	startSimulationCh := make(chan bool) | ||||
| 	go func() { | ||||
| 		startMsg := <-startSimulationCh | ||||
| 		if startMsg { | ||||
| 			helper.ReplaceString(filepath.Join(context, "server.js"), "Hello", "Hello odo") | ||||
| 			helper.ReplaceString(filepath.Join(context, "package.json"), "application", "app") | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	success, err := helper.WatchNonRetCmdStdOut( | ||||
| 		"odo watch "+flag+" --context "+context, | ||||
| 		time.Duration(5)*time.Minute, | ||||
| 		func(output string) bool { | ||||
| 			stringsMatched := true | ||||
|  | ||||
| 			for _, stringToBeMatched := range odoV2Watch.StringsToBeMatched { | ||||
| 				if !strings.Contains(output, stringToBeMatched) { | ||||
| 					stringsMatched = false | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if stringsMatched { | ||||
| 				httpPort, err := dfutil.HTTPGetFreePort() | ||||
| 				Expect(err).NotTo(HaveOccurred()) | ||||
| 				freePort := strconv.Itoa(httpPort) | ||||
|  | ||||
| 				stopChannel := make(chan bool) | ||||
| 				go func() { | ||||
| 					helper.Cmd("odo", "debug", "port-forward", "--local-port", freePort).WithTerminate(60*time.Second, stopChannel).ShouldRun() | ||||
| 				}() | ||||
|  | ||||
| 				// 400 response expected because the endpoint expects a websocket request and we are doing a HTTP GET | ||||
| 				// We are just using this to validate if nodejs agent is listening on the other side | ||||
| 				helper.HttpWaitForWithStatus("http://localhost:"+freePort, "WebSockets request was expected", 12, 5, 400) | ||||
| 				stopChannel <- true | ||||
| 				return true | ||||
| 			} | ||||
|  | ||||
| 			return false | ||||
| 		}, | ||||
| 		startSimulationCh, | ||||
| 		func(output string) bool { | ||||
| 			return strings.Contains(output, "Waiting for something to change") | ||||
| 		}) | ||||
|  | ||||
| 	Expect(success).To(Equal(true)) | ||||
| 	Expect(err).To(BeNil()) | ||||
| } | ||||
|  | ||||
| // OdoWatchWithIgnore checks if odo watch ignores the specified files and | ||||
| // it also checks if odo-file-index.json and .git are ignored | ||||
| // when --ignores is used | ||||
| func OdoWatchWithIgnore(odoV2Watch OdoV2Watch, context, flag string) { | ||||
|  | ||||
| 	startSimulationCh := make(chan bool) | ||||
| 	go func() { | ||||
| 		startMsg := <-startSimulationCh | ||||
| 		if startMsg { | ||||
| 			_, err := os.Create(filepath.Join(context, "doignoreme.txt")) | ||||
| 			Expect(err).To(BeNil()) | ||||
|  | ||||
| 			_, err = os.Create(filepath.Join(context, "donotignoreme.txt")) | ||||
| 			Expect(err).To(BeNil()) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	success, err := helper.WatchNonRetCmdStdOut( | ||||
| 		"odo watch "+flag+" --context "+context, | ||||
| 		time.Duration(5)*time.Minute, | ||||
| 		func(output string) bool { | ||||
| 			stringsMatched := true | ||||
| 			for _, stringToBeMatched := range odoV2Watch.StringsToBeMatched { | ||||
| 				if !strings.Contains(output, stringToBeMatched) { | ||||
| 					stringsMatched = false | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			stringsNotMatched := true | ||||
| 			for _, stringNotToBeMatched := range odoV2Watch.StringsNotToBeMatched { | ||||
| 				if strings.Contains(output, stringNotToBeMatched) { | ||||
| 					stringsNotMatched = false | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if stringsMatched && stringsNotMatched { | ||||
| 				return true | ||||
| 			} | ||||
|  | ||||
| 			return false | ||||
| 		}, | ||||
| 		startSimulationCh, | ||||
| 		func(output string) bool { | ||||
| 			return strings.Contains(output, "Waiting for something to change") | ||||
| 		}) | ||||
|  | ||||
| 	Expect(success).To(Equal(true)) | ||||
| 	Expect(err).To(BeNil()) | ||||
| } | ||||
|  | ||||
| func validateContainerExecListDir(odoV2Watch OdoV2Watch, runner interface{}, platform, project string) error { | ||||
| 	var folderToCheck, podName string | ||||
| 	cliRunner := runner.(helper.CliRunner) | ||||
| 	switch platform { | ||||
| 	case "kube": | ||||
| 		folderToCheck = "/projects" | ||||
| 		if odoV2Watch.FolderToCheck != "" { | ||||
| 			folderToCheck = odoV2Watch.FolderToCheck | ||||
| 		} | ||||
| 		podName = cliRunner.GetRunningPodNameByComponent(odoV2Watch.CmpName, project) | ||||
|  | ||||
| 	default: | ||||
| 		return fmt.Errorf("Platform %s is not supported", platform) | ||||
| 	} | ||||
|  | ||||
| 	// check if contains a.txt, .abc && abcd is deleted | ||||
| 	cliRunner.WaitForRunnerCmdOut([]string{"exec", podName, "--namespace", project, | ||||
| 		"--", "ls", "-lai", folderToCheck}, 5, true, func(output string) bool { | ||||
| 		return !(strings.Contains(output, "abcd")) && (strings.Contains(output, "a.txt")) && (strings.Contains(output, ".abc")) | ||||
| 	}) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // DeleteLocalConfig helps user to delete local config files with flags | ||||
| func DeleteLocalConfig(args ...string) { | ||||
| 	helper.Cmd("odo", args...).ShouldFail() | ||||
| 	output := helper.Cmd("odo", append(args, "-af")...).ShouldPass().Out() | ||||
| 	expectedOutput := []string{ | ||||
| 		"Successfully deleted env file", | ||||
| 		"Successfully deleted devfile.yaml file", | ||||
| 	} | ||||
| 	helper.MatchAllInOutput(output, expectedOutput) | ||||
| } | ||||
|  | ||||
| // VerifyContainerSyncEnv verifies the sync env in the container | ||||
| func VerifyContainerSyncEnv(podName, containerName, namespace, projectSourceValue, projectsRootValue string, cliRunner helper.CliRunner) { | ||||
| 	envProjectsRoot, envProjectSource := "PROJECTS_ROOT", "PROJECT_SOURCE" | ||||
|   | ||||
| @@ -1,82 +0,0 @@ | ||||
| package template | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	. "github.com/onsi/ginkgo" | ||||
| 	. "github.com/onsi/gomega" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/tests/helper" | ||||
| ) | ||||
|  | ||||
| // following command will run tests in Describe section below in parallel (in 2 nodes) | ||||
| // ginkgo -nodes=2 -focus="Example of a clean test" slowSpecThreshold=120 -randomizeAllSpecs  tests/e2e/ | ||||
| var _ = Describe("Example of a clean test", func() { | ||||
| 	// new clean project and context for each test | ||||
| 	var project string | ||||
| 	var context string | ||||
|  | ||||
| 	// current directory and component name (before any test runs) so that it can be restored  after all testing is done | ||||
| 	var originalDir string | ||||
| 	var cmpName string | ||||
|  | ||||
| 	BeforeEach(func() { | ||||
| 		// Set default timeout for Eventually assertions | ||||
| 		// commands like odo push, might take a long time | ||||
| 		SetDefaultEventuallyTimeout(10 * time.Minute) | ||||
| 		context = helper.CreateNewContext() | ||||
| 		os.Setenv("GLOBALODOCONFIG", filepath.Join(context, "preference.yaml")) | ||||
| 		project = helper.CreateRandProject() | ||||
|  | ||||
| 		// we will be testing components that are created from the current directory | ||||
| 		// switch to the clean context dir before each test | ||||
| 		originalDir = helper.Getwd() | ||||
| 		helper.Chdir(context) | ||||
|  | ||||
| 	}) | ||||
|  | ||||
| 	AfterEach(func() { | ||||
| 		helper.DeleteProject(project) | ||||
| 		// go back to original directory after each test | ||||
| 		helper.Chdir(originalDir) | ||||
| 		helper.DeleteDir(context) | ||||
| 		os.Unsetenv("GLOBALODOCONFIG") | ||||
| 	}) | ||||
|  | ||||
| 	Context("when --project flag is used", func() { | ||||
| 		JustBeforeEach(func() { | ||||
| 			cmpName = "nodejs" | ||||
| 		}) | ||||
|  | ||||
| 		It("create local nodejs component and push code", func() { | ||||
| 			helper.CopyExample(filepath.Join("source", "nodejs"), context) | ||||
| 			helper.Cmd("odo", "component", "create", "nodejs", cmpName, "--project", project).ShouldPass() | ||||
| 			// verify that config was properly created | ||||
| 			info := helper.LocalEnvInfo(context) | ||||
| 			Expect(info.GetApplication(), "app") | ||||
| 			Expect(info.GetName(), cmpName) | ||||
|  | ||||
| 			output := helper.Cmd("odo", "push").ShouldPass().Out() | ||||
| 			Expect(output).To(ContainSubstring("Changes successfully pushed to component")) | ||||
| 		}) | ||||
|  | ||||
| 		It("create, push and list local nodejs component", func() { | ||||
| 			appName := "testing" | ||||
| 			helper.CopyExample(filepath.Join("source", "nodejs"), context) | ||||
| 			helper.Cmd("odo", "component", "create", "nodejs", cmpName, "--app", appName, "--project", project, "--context", context).ShouldPass() | ||||
|  | ||||
| 			// verify that config was properly created | ||||
| 			info := helper.LocalEnvInfo(context) | ||||
| 			Expect(info.GetApplication(), appName) | ||||
| 			Expect(info.GetName(), cmpName) | ||||
| 			helper.Cmd("odo", "push").ShouldPass() | ||||
|  | ||||
| 			// list the component name | ||||
| 			cmpListOutput := helper.Cmd("odo", "list", "--app", appName, "--project", project).ShouldPass().Out() | ||||
| 			Expect(cmpListOutput).To(ContainSubstring(cmpName)) | ||||
| 		}) | ||||
|  | ||||
| 	}) | ||||
| }) | ||||
| @@ -1,12 +0,0 @@ | ||||
| package template | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/redhat-developer/odo/tests/helper" | ||||
| ) | ||||
|  | ||||
| func TestTemplate(t *testing.T) { | ||||
| 	helper.RunTestSpecs(t, "TestTemplate Suite") | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Parthvi Vala
					Parthvi Vala