List created components for devfile v2 (#3505)

* initial work on adding AppName

* completed the implementation and tests

* resolved failing tests

* reverting an un-necessary change

* dryed the tests

* added test for having both devfile and s2i together

* uncommented some stuff

* skip s2i test on kuberenetes

* resolved charlie's comments

* addressed mrinal's comments

* resolved the panic

* use client and dc support for specific cases

* resolved final tests and odo list now worked for unpushed components

* resolved failing unit tests

* update the component adapter in other places

* resolved all comments

* removed the created context at the end of the test

* addressed tomas's comment

* resolved all comments

* updated all devfiles with proper name

* resolved rebase

* addressed a missed error and more validation in the tests

* use random cmpName for tests
This commit is contained in:
Girish Ramnani
2020-07-22 19:08:38 +05:30
committed by GitHub
parent 55cc0bd005
commit f40bf97099
33 changed files with 427 additions and 92 deletions

View File

@@ -1,6 +1,9 @@
package labels
import "github.com/openshift/odo/pkg/version"
import (
"github.com/openshift/odo/pkg/util"
"github.com/openshift/odo/pkg/version"
)
// ApplicationLabel is label key that is used to group all object that belong to one application
// It should be save to use just this label to filter application
@@ -36,3 +39,14 @@ func GetLabels(application string, additional bool) map[string]string {
return labels
}
// GetSelector are supposed to be used for selection of any resource part of an application
func GetSelector(application string) string {
labels := map[string]string{
ApplicationLabel: application,
App: application,
OdoManagedBy: "odo",
}
return util.ConvertLabelsToSelector(labels)
}

View File

@@ -10,6 +10,7 @@ import (
type AdapterContext struct {
ComponentName string // ComponentName is the odo component name, it is NOT related to any devfile components
Context string // Context is the given directory containing the source code and configs
AppName string // the application name associated to a component
Devfile devfileParser.DevfileObj // Devfile is the object returned by the Devfile parser
}

View File

@@ -13,11 +13,12 @@ import (
)
// NewComponentAdapter returns a Devfile adapter for the targeted platform
func NewComponentAdapter(componentName string, context string, devObj devfileParser.DevfileObj, platformContext interface{}) (common.ComponentAdapter, error) {
func NewComponentAdapter(componentName string, context string, appName string, devObj devfileParser.DevfileObj, platformContext interface{}) (common.ComponentAdapter, error) {
adapterContext := common.AdapterContext{
ComponentName: componentName,
Context: context,
AppName: appName,
Devfile: devObj,
}

View File

@@ -6,6 +6,7 @@ import (
"reflect"
"strings"
componentlabels "github.com/openshift/odo/pkg/component/labels"
"github.com/openshift/odo/pkg/exec"
corev1 "k8s.io/api/core/v1"
@@ -213,9 +214,11 @@ func (a Adapter) DoesComponentExist(cmpName string) (bool, error) {
func (a Adapter) createOrUpdateComponent(componentExists bool) (err error) {
componentName := a.ComponentName
labels := map[string]string{
"component": componentName,
}
componentType := strings.TrimSuffix(a.AdapterContext.Devfile.Data.GetMetadata().Name, "-")
labels := componentlabels.GetLabels(componentName, a.AppName, true)
labels["component"] = componentName
labels[componentlabels.ComponentTypeLabel] = componentType
containers, err := utils.GetContainers(a.Devfile)
if err != nil {
@@ -281,7 +284,9 @@ func (a Adapter) createOrUpdateComponent(componentExists bool) (err error) {
return err
}
deploymentSpec := kclient.GenerateDeploymentSpec(*podTemplateSpec)
deploymentSpec := kclient.GenerateDeploymentSpec(*podTemplateSpec, map[string]string{
"component": componentName,
})
var containerPorts []corev1.ContainerPort
for _, c := range deploymentSpec.Template.Spec.Containers {
if len(containerPorts) == 0 {

View File

@@ -54,7 +54,7 @@ type Command struct {
VscodeTask *VscodeTask `json:"vscodeTask,omitempty"`
}
// ComponentsItems
// Component contains all the configuration related to component
type Component struct {
// Allows adding and configuring workspace-related containers

View File

@@ -16,10 +16,14 @@ import (
// ComponentSettings holds all component related information
type ComponentSettings struct {
Name string `yaml:"Name,omitempty"`
Name string `yaml:"Name,omitempty"`
Namespace string `yaml:"Namespace,omitempty"`
URL *[]EnvInfoURL `yaml:"Url,omitempty"`
PushCommand *EnvInfoPushCommand `yaml:"PushCommand,omitempty"`
// AppName is the application name. Application is a virtual concept present in odo used
// for grouping of components. A namespace can contain multiple applications
AppName string `yaml:"AppName,omitempty" json:"AppName,omitempty"`
// DebugPort controls the port used by the pod to run the debugging agent on
DebugPort *int `yaml:"DebugPort,omitempty"`
@@ -64,7 +68,15 @@ type EnvInfoPushCommand struct {
Run string `yaml:"Run,omitempty"`
}
// EnvInfo holds all the env specific information relevant to a specific Component.
// LocalConfigProvider is an interface which all local config providers need to implement
// currently for openshift there is localConfigInfo and for devfile its EnvInfo.
// The reason this interface is declared here instead of config package is because
// some day local config would get deprecated and hence to keep the interfaces in the new package
type LocalConfigProvider interface {
GetApplication() string
}
// EnvInfo holds all the env specific information relavent to a specific Component.
type EnvInfo struct {
componentSettings ComponentSettings `yaml:"ComponentSettings,omitempty"`
}
@@ -311,6 +323,11 @@ func (ei *EnvInfo) GetNamespace() string {
return ei.componentSettings.Namespace
}
// GetApplication returns the application name
func (ei *EnvInfo) GetApplication() string {
return ei.componentSettings.AppName
}
const (
// URL parameter
URL = "URL"

View File

@@ -42,6 +42,14 @@ func getDeploymentCondition(status appsv1.DeploymentStatus, condType appsv1.Depl
return nil
}
// ListDeployments lists all deployments by selector
func (c *Client) ListDeployments(selector string) (*appsv1.DeploymentList, error) {
return c.KubeClient.AppsV1().Deployments(c.Namespace).List(metav1.ListOptions{
LabelSelector: selector,
})
}
// WaitForDeploymentRollout waits for deployment to finish rollout. Returns the state of the deployment after rollout.
func (c *Client) WaitForDeploymentRollout(deploymentName string) (*appsv1.Deployment, error) {
klog.V(4).Infof("Waiting for %s deployment rollout", deploymentName)

View File

@@ -41,7 +41,7 @@ func createFakeDeployment(fkclient *Client, fkclientset *FakeClientset, podName
return true, &deployment, nil
})
deploymentSpec := GenerateDeploymentSpec(*podTemplateSpec)
deploymentSpec := GenerateDeploymentSpec(*podTemplateSpec, podTemplateSpec.Labels)
createdDeployment, err := fkclient.CreateDeployment(*deploymentSpec)
if err != nil {
return nil, err
@@ -207,7 +207,7 @@ func TestUpdateDeployment(t *testing.T) {
return true, &deployment, nil
})
deploymentSpec := GenerateDeploymentSpec(*podTemplateSpec)
deploymentSpec := GenerateDeploymentSpec(*podTemplateSpec, podTemplateSpec.Labels)
updatedDeployment, err := fkclient.UpdateDeployment(*deploymentSpec)
// Checks for unexpected error cases

View File

@@ -81,14 +81,13 @@ func GeneratePodTemplateSpec(objectMeta metav1.ObjectMeta, containers []corev1.C
}
// GenerateDeploymentSpec creates a deployment spec
func GenerateDeploymentSpec(podTemplateSpec corev1.PodTemplateSpec) *appsv1.DeploymentSpec {
labels := podTemplateSpec.ObjectMeta.Labels
func GenerateDeploymentSpec(podTemplateSpec corev1.PodTemplateSpec, podSelectorLabels map[string]string) *appsv1.DeploymentSpec {
deploymentSpec := &appsv1.DeploymentSpec{
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RecreateDeploymentStrategyType,
},
Selector: &metav1.LabelSelector{
MatchLabels: labels,
MatchLabels: podSelectorLabels,
},
Template: podTemplateSpec,
}

View File

@@ -3256,6 +3256,14 @@ func injectS2IPaths(existingVars []corev1.EnvVar, s2iPaths S2IPaths) []corev1.En
}
// IsDeploymentConfigSupported checks if DeploymentConfig type is present on the cluster
func (c *Client) IsDeploymentConfigSupported() (bool, error) {
const Group = "apps.openshift.io"
const Version = "v1"
return c.isResourceSupported(Group, Version, "deploymentconfigs")
}
func isSubDir(baseDir, otherDir string) bool {
cleanedBaseDir := filepath.Clean(baseDir)
cleanedOtherDir := filepath.Clean(otherDir)

View File

@@ -18,7 +18,7 @@ import (
"github.com/spf13/cobra"
)
// RecommendedComponentCommandName is the recommended component command name
// RecommendedCommandName is the recommended component command name
const RecommendedCommandName = "component"
// ComponentOptions encapsulates basic component options

View File

@@ -48,13 +48,17 @@ type CreateOptions struct {
componentPorts []string
componentEnvVars []string
memoryMax string
memoryMin string
memory string
cpuMax string
cpuMin string
cpu string
interactive bool
now bool
memoryMin string
appName string
memory string
cpuMax string
cpuMin string
cpu string
interactive bool
now bool
*CommonPushOptions
devfileMetadata DevfileMetadata
}
@@ -353,6 +357,8 @@ func (co *CreateOptions) Complete(name string, cmd *cobra.Command, args []string
return errors.New("This directory already contains a devfile, you can't specify devfile via --devfile")
}
co.appName = genericclioptions.ResolveAppFlag(cmd)
// Validate user specify devfile path
if co.devfileMetadata.devfilePath.value != "" {
fileErr := util.ValidateFile(co.devfileMetadata.devfilePath.value)
@@ -999,7 +1005,11 @@ func (co *CreateOptions) Run() (err error) {
}
// Generate env file
err = co.EnvSpecificInfo.SetComponentSettings(envinfo.ComponentSettings{Name: co.devfileMetadata.componentName, Namespace: co.devfileMetadata.componentNamespace})
err = co.EnvSpecificInfo.SetComponentSettings(envinfo.ComponentSettings{
Name: co.devfileMetadata.componentName,
Namespace: co.devfileMetadata.componentNamespace,
AppName: co.appName,
})
if err != nil {
return errors.Wrap(err, "failed to create env file for devfile component")
}

View File

@@ -196,7 +196,7 @@ func (do *DeleteOptions) Run() (err error) {
return
}
// Run has the logic to perform the required actions as part of command for devfiles
// DevFileRun has the logic to perform the required actions as part of command for devfiles
func (do *DeleteOptions) DevFileRun() (err error) {
// devfile delete
if do.componentForceDeleteFlag || ui.Proceed(fmt.Sprintf("Are you sure you want to delete the devfile component: %s?", do.EnvSpecificInfo.GetName())) {

View File

@@ -87,7 +87,7 @@ func (po *PushOptions) devfilePushInner() (err error) {
platformContext = kc
}
devfileHandler, err := adapters.NewComponentAdapter(componentName, po.componentContext, devObj, platformContext)
devfileHandler, err := adapters.NewComponentAdapter(componentName, po.componentContext, po.Application, devObj, platformContext)
if err != nil {
return err
@@ -146,7 +146,7 @@ func (lo LogOptions) DevfileComponentLog() error {
platformContext = kc
}
devfileHandler, err := adapters.NewComponentAdapter(componentName, lo.componentContext, devObj, platformContext)
devfileHandler, err := adapters.NewComponentAdapter(componentName, lo.componentContext, lo.Application, devObj, platformContext)
if err != nil {
return err
@@ -207,7 +207,7 @@ func (do *DeleteOptions) DevfileComponentDelete() error {
labels := map[string]string{
"component": componentName,
}
devfileHandler, err := adapters.NewComponentAdapter(componentName, do.componentContext, devObj, kc)
devfileHandler, err := adapters.NewComponentAdapter(componentName, do.componentContext, do.Application, devObj, kc)
if err != nil {
return err
}
@@ -232,7 +232,7 @@ func (to *TestOptions) RunTestCommand() error {
platformContext = kc
}
devfileHandler, err := adapters.NewComponentAdapter(componentName, to.componentContext, to.devObj, platformContext)
devfileHandler, err := adapters.NewComponentAdapter(componentName, to.componentContext, to.Application, to.devObj, platformContext)
if err != nil {
return err
}
@@ -278,7 +278,7 @@ func (eo *ExecOptions) DevfileComponentExec(command []string) error {
Namespace: eo.namespace,
}
devfileHandler, err := adapters.NewComponentAdapter(componentName, eo.componentContext, devObj, kc)
devfileHandler, err := adapters.NewComponentAdapter(componentName, eo.componentContext, eo.componentOptions.Application, devObj, kc)
if err != nil {
return err
}

View File

@@ -2,15 +2,19 @@ package component
import (
"fmt"
appCmd "github.com/openshift/odo/pkg/odo/cli/application"
projectCmd "github.com/openshift/odo/pkg/odo/cli/project"
"github.com/openshift/odo/pkg/odo/genericclioptions"
odoutil "github.com/openshift/odo/pkg/odo/util"
"github.com/openshift/odo/pkg/odo/util/completion"
"github.com/openshift/odo/pkg/odo/util/experimental"
"github.com/openshift/odo/pkg/odo/util/pushtarget"
"path/filepath"
"github.com/spf13/cobra"
ktemplates "k8s.io/kubectl/pkg/util/templates"
"path/filepath"
)
// ExecRecommendedCommandName is the recommended exec command name
@@ -105,5 +109,8 @@ func NewCmdExec(name, fullName string) *cobra.Command {
//Adding `--project` flag
projectCmd.AddProjectFlag(execCmd)
// Adding `--app` flag
appCmd.AddApplicationFlag(execCmd)
return execCmd
}

View File

@@ -6,13 +6,20 @@ import (
"path/filepath"
"text/tabwriter"
appsv1 "k8s.io/api/apps/v1"
"github.com/openshift/odo/pkg/application"
"github.com/openshift/odo/pkg/devfile"
"github.com/openshift/odo/pkg/machineoutput"
"github.com/openshift/odo/pkg/project"
"github.com/openshift/odo/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"k8s.io/klog"
applabels "github.com/openshift/odo/pkg/application/labels"
componentlabels "github.com/openshift/odo/pkg/component/labels"
"github.com/openshift/odo/pkg/component"
"github.com/openshift/odo/pkg/log"
appCmd "github.com/openshift/odo/pkg/odo/cli/application"
@@ -20,6 +27,7 @@ import (
"github.com/openshift/odo/pkg/odo/genericclioptions"
odoutil "github.com/openshift/odo/pkg/odo/util"
"github.com/openshift/odo/pkg/odo/util/completion"
"github.com/openshift/odo/pkg/odo/util/experimental"
ktemplates "k8s.io/kubectl/pkg/util/templates"
)
@@ -27,15 +35,24 @@ import (
// ListRecommendedCommandName is the recommended watch command name
const ListRecommendedCommandName = "list"
const UnpushedCompState = "Unpushed"
const PushedCompState = "Pushed"
var listExample = ktemplates.Examples(` # List all components in the application
%[1]s
`)
// ListOptions is a dummy container to attach complete, validate and run pattern
type ListOptions struct {
pathFlag string
allAppsFlag bool
componentContext string
pathFlag string
allAppsFlag bool
componentContext string
componentType string
hasDCSupport bool
hasDevfileComponents bool
hasS2IComponents bool
isExperimentalMode bool
devfilePath string
*genericclioptions.Context
}
@@ -46,15 +63,45 @@ func NewListOptions() *ListOptions {
// Complete completes log args
func (lo *ListOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) {
lo.isExperimentalMode = experimental.IsExperimentalModeEnabled()
lo.devfilePath = filepath.Join(lo.componentContext, DevfilePath)
if lo.isExperimentalMode && util.CheckPathExists(lo.devfilePath) {
// Add a disclaimer that we are in *experimental mode*
log.Experimental("Experimental mode is enabled, use at your own risk")
lo.Context = genericclioptions.NewDevfileContext(cmd)
lo.Client = genericclioptions.Client(cmd)
lo.hasDCSupport, err = lo.Client.IsDeploymentConfigSupported()
if err != nil {
return err
}
devfile, err := devfile.ParseAndValidate(lo.devfilePath)
if err != nil {
return err
}
lo.componentType = devfile.Data.GetMetadata().Name
if util.CheckKubeConfigExist() {
klog.V(4).Infof("New Context")
lo.Context = genericclioptions.NewContext(cmd)
} else {
klog.V(4).Infof("New Config Context")
lo.Context = genericclioptions.NewConfigContext(cmd)
// here we use the config.yaml derived context if its present, else we use information from user's kubeconfig
// as odo list should work in a non-component directory too
if util.CheckKubeConfigExist() {
klog.V(4).Infof("New Context")
lo.Context = genericclioptions.NewContext(cmd)
lo.hasDCSupport, err = lo.Client.IsDeploymentConfigSupported()
if err != nil {
return err
}
} else {
klog.V(4).Infof("New Config Context")
lo.Context = genericclioptions.NewConfigContext(cmd)
// for disconnected situation we just assume we have DC support
lo.hasDCSupport = true
}
}
return
}
@@ -62,12 +109,18 @@ func (lo *ListOptions) Complete(name string, cmd *cobra.Command, args []string)
// Validate validates the list parameters
func (lo *ListOptions) Validate() (err error) {
var project, app string
if len(lo.Application) != 0 && lo.allAppsFlag {
klog.V(4).Infof("either --app and --all-apps both provided or provided --all-apps in a folder has app, use --all-apps anyway")
}
if lo.isExperimentalMode {
if lo.Context.Application == "" && lo.Context.KClient.Namespace == "" {
return odoutil.ThrowContextError()
}
return nil
}
var project, app string
if !util.CheckKubeConfigExist() {
project = lo.LocalConfigInfo.GetProject()
app = lo.LocalConfigInfo.GetApplication()
@@ -81,12 +134,19 @@ func (lo *ListOptions) Validate() (err error) {
return odoutil.ThrowContextError()
}
return nil
}
// Run has the logic to perform the required actions as part of command
func (lo *ListOptions) Run() (err error) {
// --path workflow
if len(lo.pathFlag) != 0 {
if lo.isExperimentalMode && util.CheckPathExists(lo.devfilePath) {
log.Experimental("--path flag is not supported for devfile components")
}
components, err := component.ListIfPathGiven(lo.Context.Client, filepath.SplitList(lo.pathFlag))
if err != nil {
return err
@@ -104,57 +164,137 @@ func (lo *ListOptions) Run() (err error) {
}
return nil
}
var components component.ComponentList
if lo.allAppsFlag {
// retrieve list of application
apps, err := application.List(lo.Client)
// non --path workflow below
// read the code like
// -> experimental
// or |-> --all-apps
// |-> the current app
// -> non-experimental
// or |-> --all-apps
// |-> the current app
// experimental workflow
if lo.isExperimentalMode && util.CheckPathExists(lo.devfilePath) {
var deploymentList *appsv1.DeploymentList
var err error
var selector string
// TODO: wrap this into a component list for docker support
if lo.allAppsFlag {
selector = project.GetSelector()
} else {
selector = applabels.GetSelector(lo.Application)
}
deploymentList, err = lo.KClient.ListDeployments(selector)
if err != nil {
return err
}
var componentList []component.Component
// Json output is not implemented yet for devfile
if !log.IsJSON() {
envinfo := lo.EnvSpecificInfo.EnvInfo
if len(deploymentList.Items) != 0 || envinfo.GetApplication() == lo.Application {
if len(apps) == 0 && lo.LocalConfigInfo.ConfigFileExists() {
comps, err := component.List(lo.Client, lo.LocalConfigInfo.GetApplication(), lo.LocalConfigInfo)
if err != nil {
return err
currentComponentState := UnpushedCompState
currentComponentName := envinfo.GetName()
lo.hasDevfileComponents = true
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "Devfile Components: ")
fmt.Fprintln(w, "APP", "\t", "NAME", "\t", "PROJECT", "\t", "TYPE", "\t", "STATE")
for _, comp := range deploymentList.Items {
app := comp.Labels[applabels.ApplicationLabel]
cmpType := comp.Labels[componentlabels.ComponentTypeLabel]
if comp.Name == currentComponentName && app == envinfo.GetApplication() && comp.Namespace == envinfo.GetNamespace() {
currentComponentState = PushedCompState
}
fmt.Fprintln(w, app, "\t", comp.Name, "\t", comp.Namespace, "\t", cmpType, "\t", "Pushed")
}
// 1st condition - only if we are using the same application or all-apps are provided should we show the current component
// 2nd condition - if the currentComponentState is unpushed that means it didn't show up in the list above
if (envinfo.GetApplication() == lo.Application || lo.allAppsFlag) && currentComponentState == UnpushedCompState {
fmt.Fprintln(w, envinfo.GetApplication(), "\t", currentComponentName, "\t", envinfo.GetNamespace(), "\t", lo.componentType, "\t", currentComponentState)
}
w.Flush()
}
componentList = append(componentList, comps.Items...)
}
// interating over list of application and get list of all components
for _, app := range apps {
comps, err := component.List(lo.Client, app, lo.LocalConfigInfo)
if err != nil {
return err
}
componentList = append(componentList, comps.Items...)
}
// Get machine readable component list format
components = component.GetMachineReadableFormatForList(componentList)
} else {
components, err = component.List(lo.Client, lo.Application, lo.LocalConfigInfo)
if err != nil {
return errors.Wrapf(err, "failed to fetch components list")
}
}
klog.V(4).Infof("the components are %+v", components)
if log.IsJSON() {
machineoutput.OutputSuccess(components)
} else {
if len(components.Items) == 0 {
log.Errorf("There are no components deployed.")
// non-experimental workflow
// we now check if DC is supported
if lo.hasDCSupport {
var components component.ComponentList
if lo.allAppsFlag {
// retrieve list of application
apps, err := application.List(lo.Client)
if err != nil {
return err
}
var componentList []component.Component
if len(apps) == 0 && lo.LocalConfigInfo.ConfigFileExists() {
comps, err := component.List(lo.Client, lo.LocalConfigInfo.GetApplication(), lo.LocalConfigInfo)
if err != nil {
return err
}
componentList = append(componentList, comps.Items...)
}
// interating over list of application and get list of all components
for _, app := range apps {
comps, err := component.List(lo.Client, app, lo.LocalConfigInfo)
if err != nil {
return err
}
componentList = append(componentList, comps.Items...)
}
// Get machine readable component list format
components = component.GetMachineReadableFormatForList(componentList)
} else {
components, err = component.List(lo.Client, lo.Application, lo.LocalConfigInfo)
if err != nil {
return errors.Wrapf(err, "failed to fetch component list")
}
}
if log.IsJSON() {
machineoutput.OutputSuccess(components)
} else {
if len(components.Items) != 0 {
if lo.hasDevfileComponents {
fmt.Println()
}
lo.hasS2IComponents = true
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "Openshift Components: ")
fmt.Fprintln(w, "APP", "\t", "NAME", "\t", "PROJECT", "\t", "TYPE", "\t", "SOURCETYPE", "\t", "STATE")
for _, comp := range components.Items {
fmt.Fprintln(w, comp.Spec.App, "\t", comp.Name, "\t", comp.Namespace, "\t", comp.Spec.Type, "\t", comp.Spec.SourceType, "\t", comp.Status.State)
}
w.Flush()
}
}
// if we dont have any of the components
if !lo.hasDevfileComponents && !lo.hasS2IComponents {
log.Error("There are no components deployed.")
return
}
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "APP", "\t", "NAME", "\t", "PROJECT", "\t", "TYPE", "\t", "SOURCETYPE", "\t", "STATE")
for _, comp := range components.Items {
fmt.Fprintln(w, comp.Spec.App, "\t", comp.Name, "\t", comp.Namespace, "\t", comp.Spec.Type, "\t", comp.Spec.SourceType, "\t", comp.Status.State)
}
w.Flush()
}
return
}

View File

@@ -8,6 +8,8 @@ import (
"github.com/pkg/errors"
"github.com/openshift/odo/pkg/devfile"
appCmd "github.com/openshift/odo/pkg/odo/cli/application"
devfileParser "github.com/openshift/odo/pkg/devfile/parser"
projectCmd "github.com/openshift/odo/pkg/odo/cli/project"
"github.com/openshift/odo/pkg/odo/genericclioptions"
@@ -93,6 +95,8 @@ func NewCmdTest(name, fullName string) *cobra.Command {
genericclioptions.AddContextFlag(testCmd, &to.componentContext)
//Adding `--project` flag
projectCmd.AddProjectFlag(testCmd)
// Adding `--app` flag
appCmd.AddApplicationFlag(testCmd)
completion.RegisterCommandHandler(testCmd, completion.ComponentNameCompletionHandler)
return testCmd
}

View File

@@ -115,7 +115,7 @@ func (wo *WatchOptions) Complete(name string, cmd *cobra.Command, args []string)
} else {
platformContext = nil
}
wo.devfileHandler, err = adapters.NewComponentAdapter(wo.componentName, wo.componentContext, devObj, platformContext)
wo.devfileHandler, err = adapters.NewComponentAdapter(wo.componentName, wo.componentContext, wo.Application, devObj, platformContext)
return err
}

View File

@@ -271,7 +271,7 @@ func resolveNamespace(command *cobra.Command, client *kclient.Client, envSpecifi
}
// resolveApp resolves the app
func resolveApp(command *cobra.Command, createAppIfNeeded bool, localConfiguration *config.LocalConfigInfo) string {
func resolveApp(command *cobra.Command, createAppIfNeeded bool, localConfiguration envinfo.LocalConfigProvider) string {
var app string
appFlag := FlagValueIfSet(command, ApplicationFlagName)
if len(appFlag) > 0 {
@@ -287,6 +287,15 @@ func resolveApp(command *cobra.Command, createAppIfNeeded bool, localConfigurati
return app
}
// ResolveAppFlag resolves the app from the flag
func ResolveAppFlag(command *cobra.Command) string {
appFlag := FlagValueIfSet(command, ApplicationFlagName)
if len(appFlag) > 0 {
return appFlag
}
return DefaultAppName
}
// resolveComponent resolves component
func resolveComponent(command *cobra.Command, localConfiguration *config.LocalConfigInfo, context *Context) string {
var cmp string
@@ -328,7 +337,7 @@ func newContext(command *cobra.Command, createAppIfNeeded bool, ignoreMissingCon
// Resolve project
namespace := resolveProject(command, client, localConfiguration)
// Resolve application
// resolve application
app := resolveApp(command, createAppIfNeeded, localConfiguration)
// Resolve output flag
@@ -365,6 +374,8 @@ func newDevfileContext(command *cobra.Command) *Context {
internalCxt := internalCxt{
OutputFlag: outputFlag,
command: command,
// this is only so we can make devfile and s2i work together for certain cases
LocalConfigInfo: &config.LocalConfigInfo{},
}
// Get valid env information
@@ -374,6 +385,7 @@ func newDevfileContext(command *cobra.Command) *Context {
}
internalCxt.EnvSpecificInfo = envInfo
internalCxt.Application = resolveApp(command, true, envInfo)
// If the push target is NOT Docker we will set the client to Kubernetes.
if !pushtarget.IsPushTargetDocker() {

15
pkg/project/labels.go Normal file
View File

@@ -0,0 +1,15 @@
package project
import (
applabels "github.com/openshift/odo/pkg/application/labels"
"github.com/openshift/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.OdoManagedBy: "odo",
}
return util.ConvertLabelsToSelector(labels)
}

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
apiVersion: 1.0.0
metadata:
name: test-devfile
name: nodejs
projects:
-
name: nodejs-starter

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -1,6 +1,6 @@
schemaVersion: 2.0.0
metadata:
name: test-devfile
name: nodejs
projects:
- name: nodejs-starter
git:

View File

@@ -191,3 +191,8 @@ 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(s, " ", ""), "\t", "")
}

View File

@@ -1,6 +1,7 @@
package devfile
import (
"fmt"
"os"
"path/filepath"
"strings"
@@ -462,4 +463,92 @@ var _ = Describe("odo devfile push command tests", func() {
})
})
Context("push with listing the devfile component", func() {
It("checks components in a specific app and all apps", func() {
// component created in "app" application
helper.CmdShouldPass("odo", "create", "nodejs", "--project", namespace, "--context", context, cmpName)
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), context)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(context, "devfile.yaml"))
output := helper.CmdShouldPass("odo", "list", "--context", context)
Expect(helper.Suffocate(output)).To(ContainSubstring(helper.Suffocate(fmt.Sprintf("%s%s%s%sUnpushed", "app", cmpName, namespace, "nodejs"))))
output = helper.CmdShouldPass("odo", "push", "--context", context)
Expect(output).To(ContainSubstring("Changes successfully pushed to component"))
// component created in different application
context2 := helper.CreateNewContext()
cmpName2 := helper.RandString(6)
appName := helper.RandString(6)
helper.CmdShouldPass("odo", "create", "nodejs", "--project", namespace, "--app", appName, "--context", context2, cmpName2)
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), context2)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(context2, "devfile.yaml"))
output = helper.CmdShouldPass("odo", "list", "--context", context2)
Expect(helper.Suffocate(output)).To(ContainSubstring(helper.Suffocate(fmt.Sprintf("%s%s%s%sUnpushed", appName, cmpName2, namespace, "nodejs"))))
output2 := helper.CmdShouldPass("odo", "push", "--context", context2)
Expect(output2).To(ContainSubstring("Changes successfully pushed to component"))
output = helper.CmdShouldPass("odo", "list", "--project", namespace)
Expect(output).To(ContainSubstring(cmpName))
Expect(output).ToNot(ContainSubstring(cmpName2))
output = helper.CmdShouldPass("odo", "list", "--all-apps", "--project", namespace)
Expect(output).To(ContainSubstring(cmpName))
Expect(output).To(ContainSubstring(cmpName2))
helper.CmdShouldPass("odo", "preference", "set", "Experimental", "false")
helper.DeleteDir(context2)
})
It("checks devfile and s2i components together", func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("Skipping test because s2i image is not supported on Kubernetes cluster")
}
// component created in "app" application
helper.CmdShouldPass("odo", "create", "nodejs", "--project", namespace, "--context", context, cmpName)
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), context)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(context, "devfile.yaml"))
output := helper.CmdShouldPass("odo", "list", "--context", context)
Expect(helper.Suffocate(output)).To(ContainSubstring(helper.Suffocate(fmt.Sprintf("%s%s%s%sUnpushed", "app", cmpName, namespace, "nodejs"))))
output = helper.CmdShouldPass("odo", "push", "--context", context)
Expect(output).To(ContainSubstring("Changes successfully pushed to component"))
// component created in different application
context2 := helper.CreateNewContext()
cmpName2 := helper.RandString(6)
appName := helper.RandString(6)
helper.CmdShouldPass("odo", "preference", "set", "--force", "Experimental", "false")
helper.CopyExample(filepath.Join("source", "nodejs"), context2)
helper.CmdShouldPass("odo", "create", "nodejs", "--project", namespace, "--app", appName, "--context", context2, cmpName2)
output2 := helper.CmdShouldPass("odo", "push", "--context", context2)
Expect(output2).To(ContainSubstring("Changes successfully pushed to component"))
helper.CmdShouldPass("odo", "preference", "set", "--force", "Experimental", "true")
output = helper.CmdShouldPass("odo", "list", "--all-apps", "--project", namespace)
Expect(output).To(ContainSubstring(cmpName))
Expect(output).To(ContainSubstring(cmpName2))
output = helper.CmdShouldPass("odo", "list", "--app", appName, "--project", namespace)
Expect(output).To(Not(ContainSubstring(cmpName))) // cmpName component hasn't been created under appName
Expect(output).To(ContainSubstring(cmpName2))
helper.DeleteDir(context2)
})
})
})