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:
Parthvi Vala
2022-04-20 10:24:15 +05:30
committed by GitHub
parent 8be39961a5
commit abc4b59369
76 changed files with 132 additions and 2828 deletions

View File

@@ -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

View File

@@ -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())
}

View File

@@ -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"

View File

@@ -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)
}

View File

@@ -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,
}
}

View File

@@ -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

View 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
}

View File

@@ -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)
})
}
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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] != "" {

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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,
},
}
}

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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"
)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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{

View File

@@ -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{

View File

@@ -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{

View File

@@ -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{

View File

@@ -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

View File

@@ -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

View File

@@ -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
// }

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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:"-"`
}

View File

@@ -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
}

View File

@@ -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])
}
}
}
})
}
}

View File

@@ -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
}

View File

@@ -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")
}
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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})

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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))
})
})
})

View File

@@ -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")
}