mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
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:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
15
pkg/project/labels.go
Normal 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)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: 1.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
-
|
||||
name: nodejs-starter
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schemaVersion: 2.0.0
|
||||
metadata:
|
||||
name: test-devfile
|
||||
name: nodejs
|
||||
projects:
|
||||
- name: nodejs-starter
|
||||
git:
|
||||
|
||||
@@ -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", "")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user