mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Adds app commands for devfile components (#4007)
* Adds app commands for devfile components * Fixes log messages and comments * Fixes s2i app test script * Fixes s2i local config components * Removes devfile test for help * Adds unit test cases for List() * Updates integration tests * Fixed alias in import
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -87,4 +87,4 @@ tags
|
||||
_site/
|
||||
.sass-cache/
|
||||
.jekyll-cache/
|
||||
.jekyll-metadata
|
||||
.jekyll-metadata
|
||||
5
Makefile
5
Makefile
@@ -240,6 +240,11 @@ test-cmd-docker-devfile-exec:
|
||||
test-cmd-devfile-watch:
|
||||
ginkgo $(GINKGO_FLAGS) -focus="odo devfile watch command tests" tests/integration/devfile/
|
||||
|
||||
# Run odo devfile app command tests
|
||||
.PHONY: test-cmd-devfile-app
|
||||
test-cmd-devfile-app:
|
||||
ginkgo $(GINKGO_FLAGS) -focus="odo devfile app command tests" tests/integration/devfile/
|
||||
|
||||
# Run odo devfile delete command tests
|
||||
.PHONY: test-cmd-devfile-delete
|
||||
test-cmd-devfile-delete:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"github.com/openshift/odo/pkg/kclient"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/klog"
|
||||
|
||||
@@ -33,13 +34,27 @@ func ListInProject(client *occlient.Client) ([]string, error) {
|
||||
return appNames, nil
|
||||
}
|
||||
|
||||
// Get all DeploymentConfigs with the "app" label
|
||||
deploymentConfigAppNames, err := client.GetDeploymentConfigLabelValues(applabels.ApplicationLabel, applabels.ApplicationLabel)
|
||||
deploymentSupported, err := client.IsDeploymentConfigSupported()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to list applications from deployment config")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appNames = append(appNames, deploymentConfigAppNames...)
|
||||
// Get all DeploymentConfigs with the "app" label
|
||||
deploymentAppNames, err := client.GetKubeClient().GetDeploymentLabelValues(applabels.ApplicationLabel, applabels.ApplicationLabel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to list applications from deployments")
|
||||
}
|
||||
|
||||
appNames = append(appNames, deploymentAppNames...)
|
||||
|
||||
if deploymentSupported {
|
||||
// Get all DeploymentConfigs with the "app" label
|
||||
deploymentConfigAppNames, err := client.GetDeploymentConfigLabelValues(applabels.ApplicationLabel, applabels.ApplicationLabel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to list applications from deployment config")
|
||||
}
|
||||
appNames = append(appNames, deploymentConfigAppNames...)
|
||||
}
|
||||
|
||||
// Get all ServiceInstances with the "app" label
|
||||
// Okay, so there is an edge-case here.. if Service Catalog is *not* enabled in the cluster, we shouldn't error out..
|
||||
@@ -48,7 +63,7 @@ func ListInProject(client *occlient.Client) ([]string, error) {
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to list Service Catalog instances: %s", err)
|
||||
} else {
|
||||
appNames = append(deploymentConfigAppNames, serviceInstanceAppNames...)
|
||||
appNames = append(appNames, serviceInstanceAppNames...)
|
||||
}
|
||||
|
||||
// Filter out any names, as there could be multiple components but within the same application
|
||||
@@ -56,9 +71,10 @@ func ListInProject(client *occlient.Client) ([]string, error) {
|
||||
}
|
||||
|
||||
// Exists checks whether the given app exist or not
|
||||
func Exists(app string, client *occlient.Client) (bool, error) {
|
||||
func Exists(app string, client *occlient.Client, kClient *kclient.Client) (bool, error) {
|
||||
|
||||
appList, err := List(client)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -90,8 +106,21 @@ func Delete(client *occlient.Client, name string) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
supported, err := client.IsDeploymentConfigSupported()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if supported {
|
||||
// delete application from cluster
|
||||
err = client.Delete(labels, false)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to delete application %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
// delete application from cluster
|
||||
err = client.Delete(labels, false)
|
||||
err = client.GetKubeClient().Delete(labels, false)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to delete application %s", name)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -612,6 +613,7 @@ func ApplyConfig(client *occlient.Client, kClient *kclient.Client, componentConf
|
||||
applicationName = componentConfig.GetApplication()
|
||||
} else {
|
||||
componentName = envSpecificInfo.GetName()
|
||||
applicationName = envSpecificInfo.GetApplication()
|
||||
}
|
||||
|
||||
isRouteSupported := false
|
||||
@@ -861,33 +863,52 @@ func List(client *occlient.Client, applicationName string, localConfigInfo *conf
|
||||
applicationSelector = fmt.Sprintf("%s=%s", applabels.ApplicationLabel, applicationName)
|
||||
}
|
||||
|
||||
deploymentConfigSupported := false
|
||||
var err error
|
||||
var deploymentList []v1.Deployment
|
||||
|
||||
var components []Component
|
||||
componentNamesMap := make(map[string]bool)
|
||||
|
||||
if client != nil {
|
||||
project, err := client.GetProject(client.Namespace)
|
||||
deploymentConfigSupported, err = client.IsDeploymentConfigSupported()
|
||||
if err != nil {
|
||||
return ComponentList{}, err
|
||||
}
|
||||
|
||||
if project != nil {
|
||||
// retrieve all the deployment configs that are associated with this application
|
||||
dcList, err := client.GetDeploymentConfigsFromSelector(applicationSelector)
|
||||
if err != nil {
|
||||
return ComponentList{}, errors.Wrapf(err, "unable to list components")
|
||||
}
|
||||
// retrieve all the deployments that are associated with this application
|
||||
deploymentList, err = client.GetKubeClient().GetDeploymentFromSelector(applicationSelector)
|
||||
if err != nil {
|
||||
return ComponentList{}, errors.Wrapf(err, "unable to list components")
|
||||
}
|
||||
}
|
||||
|
||||
// extract the labels we care about from each component
|
||||
for _, elem := range dcList {
|
||||
component, err := GetComponent(client, elem.Labels[componentlabels.ComponentLabel], applicationName, client.Namespace)
|
||||
if err != nil {
|
||||
return ComponentList{}, errors.Wrap(err, "Unable to get component")
|
||||
}
|
||||
components = append(components, component)
|
||||
componentNamesMap[component.Name] = true
|
||||
}
|
||||
// extract the labels we care about from each component
|
||||
for _, elem := range deploymentList {
|
||||
component, err := GetComponent(client, elem.Labels[componentlabels.ComponentLabel], applicationName, client.Namespace)
|
||||
if err != nil {
|
||||
return ComponentList{}, errors.Wrap(err, "Unable to get component")
|
||||
}
|
||||
components = append(components, component)
|
||||
componentNamesMap[component.Name] = true
|
||||
}
|
||||
|
||||
if deploymentConfigSupported && client != nil {
|
||||
// retrieve all the deployment configs that are associated with this application
|
||||
dcList, err := client.GetDeploymentConfigsFromSelector(applicationSelector)
|
||||
if err != nil {
|
||||
return ComponentList{}, errors.Wrapf(err, "unable to list components")
|
||||
}
|
||||
|
||||
// extract the labels we care about from each component
|
||||
for _, elem := range dcList {
|
||||
component, err := GetComponent(client, elem.Labels[componentlabels.ComponentLabel], applicationName, client.Namespace)
|
||||
if err != nil {
|
||||
return ComponentList{}, errors.Wrap(err, "Unable to get component")
|
||||
}
|
||||
components = append(components, component)
|
||||
componentNamesMap[component.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
if localConfigInfo != nil {
|
||||
@@ -1437,6 +1458,7 @@ func getRemoteComponentMetadata(client *occlient.Client, componentName string, a
|
||||
if err != nil {
|
||||
return Component{}, err
|
||||
}
|
||||
component.Spec.URLSpec = urls
|
||||
urlsNb := len(urls)
|
||||
if urlsNb > 0 {
|
||||
res := make([]string, 0, urlsNb)
|
||||
@@ -1449,15 +1471,17 @@ func getRemoteComponentMetadata(client *occlient.Client, componentName string, a
|
||||
|
||||
// Storage
|
||||
if getStorage {
|
||||
appStore, err := storage.List(client, componentName, applicationName)
|
||||
appStore, err := fromCluster.GetStorage()
|
||||
if err != nil {
|
||||
return Component{}, errors.Wrap(err, "unable to get storage list")
|
||||
}
|
||||
var storage []string
|
||||
for _, store := range appStore.Items {
|
||||
storage = append(storage, store.Name)
|
||||
|
||||
component.Spec.StorageSpec = appStore
|
||||
var storageList []string
|
||||
for _, store := range appStore {
|
||||
storageList = append(storageList, store.Name)
|
||||
}
|
||||
component.Spec.Storage = storage
|
||||
component.Spec.Storage = storageList
|
||||
}
|
||||
|
||||
// Environment Variables
|
||||
|
||||
@@ -3,6 +3,7 @@ package component
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -268,20 +269,37 @@ func TestList(t *testing.T) {
|
||||
getFakeDC("test", "test", "otherApp", "python"),
|
||||
}}
|
||||
|
||||
const caseName = "Case 5: List component when openshift cluster not reachable"
|
||||
deploymentList := v1.DeploymentList{Items: []v1.Deployment{
|
||||
*testingutil.CreateFakeDeployment("comp0"),
|
||||
*testingutil.CreateFakeDeployment("comp1"),
|
||||
}}
|
||||
|
||||
deploymentList.Items[0].Labels[componentlabels.ComponentTypeLabel] = "nodejs"
|
||||
deploymentList.Items[0].Annotations = map[string]string{
|
||||
ComponentSourceTypeAnnotation: "local",
|
||||
}
|
||||
deploymentList.Items[1].Labels[componentlabels.ComponentTypeLabel] = "wildfly"
|
||||
deploymentList.Items[1].Annotations = map[string]string{
|
||||
ComponentSourceTypeAnnotation: "local",
|
||||
}
|
||||
|
||||
const caseName = "Case 4: List component when openshift cluster not reachable"
|
||||
tests := []struct {
|
||||
name string
|
||||
dcList appsv1.DeploymentConfigList
|
||||
projectExists bool
|
||||
wantErr bool
|
||||
existingLocalConfigInfo *LocalConfigInfo
|
||||
output ComponentList
|
||||
name string
|
||||
dcList appsv1.DeploymentConfigList
|
||||
deploymentConfigSupported bool
|
||||
deploymentList v1.DeploymentList
|
||||
projectExists bool
|
||||
wantErr bool
|
||||
existingLocalConfigInfo *LocalConfigInfo
|
||||
output ComponentList
|
||||
}{
|
||||
{
|
||||
name: "Case 1: Components are returned",
|
||||
dcList: dcList,
|
||||
wantErr: false,
|
||||
projectExists: true,
|
||||
name: "Case 1: Components are returned",
|
||||
dcList: dcList,
|
||||
deploymentConfigSupported: true,
|
||||
wantErr: false,
|
||||
projectExists: true,
|
||||
output: ComponentList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "List",
|
||||
@@ -295,23 +313,18 @@ func TestList(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Case 2: projects doesn't exist",
|
||||
wantErr: false,
|
||||
projectExists: false,
|
||||
output: GetMachineReadableFormatForList([]Component{}),
|
||||
},
|
||||
{
|
||||
name: "Case 3: no component and no config exists",
|
||||
name: "Case 2: no component and no config exists",
|
||||
wantErr: false,
|
||||
projectExists: true,
|
||||
output: GetMachineReadableFormatForList([]Component{}),
|
||||
},
|
||||
{
|
||||
name: "Case 4: Components are returned from the config plus and cluster",
|
||||
dcList: dcList,
|
||||
wantErr: false,
|
||||
projectExists: true,
|
||||
existingLocalConfigInfo: &existingSampleLocalConfig,
|
||||
name: "Case 3: Components are returned from the config plus and cluster",
|
||||
dcList: dcList,
|
||||
deploymentConfigSupported: true,
|
||||
wantErr: false,
|
||||
projectExists: true,
|
||||
existingLocalConfigInfo: &existingSampleLocalConfig,
|
||||
output: ComponentList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "List",
|
||||
@@ -332,25 +345,68 @@ func TestList(t *testing.T) {
|
||||
existingLocalConfigInfo: &existingSampleLocalConfig,
|
||||
output: GetMachineReadableFormatForList([]Component{componentConfig2}),
|
||||
},
|
||||
{
|
||||
name: "Case 5: Components are returned from deployments on a kubernetes cluster",
|
||||
dcList: dcList,
|
||||
deploymentList: deploymentList,
|
||||
wantErr: false,
|
||||
projectExists: true,
|
||||
deploymentConfigSupported: false,
|
||||
output: ComponentList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "List",
|
||||
APIVersion: "odo.dev/v1alpha1",
|
||||
},
|
||||
ListMeta: metav1.ListMeta{},
|
||||
Items: []Component{
|
||||
getFakeComponent("comp0", "test", "app", "nodejs", StateTypePushed),
|
||||
getFakeComponent("comp1", "test", "app", "wildfly", StateTypePushed),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Case 6: Components are returned from both",
|
||||
dcList: dcList,
|
||||
deploymentList: deploymentList,
|
||||
wantErr: false,
|
||||
projectExists: true,
|
||||
deploymentConfigSupported: true,
|
||||
output: ComponentList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "List",
|
||||
APIVersion: "odo.dev/v1alpha1",
|
||||
},
|
||||
ListMeta: metav1.ListMeta{},
|
||||
Items: []Component{
|
||||
getFakeComponent("comp0", "test", "app", "nodejs", StateTypePushed),
|
||||
getFakeComponent("comp1", "test", "app", "wildfly", StateTypePushed),
|
||||
getFakeComponent("frontend", "test", "app", "nodejs", StateTypePushed),
|
||||
getFakeComponent("backend", "test", "app", "java", StateTypePushed),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if !tt.deploymentConfigSupported {
|
||||
os.Setenv("KUBERNETES", "true")
|
||||
defer os.Unsetenv("KUBERNETES")
|
||||
}
|
||||
|
||||
client, fakeClientSet := occlient.FakeNew()
|
||||
client.Namespace = "test"
|
||||
|
||||
fakeClientSet.ProjClientset.PrependReactor("get", "projects", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
if !tt.projectExists {
|
||||
return true, nil, nil
|
||||
}
|
||||
return true, &testingutil.FakeOnlyOneExistingProjects().Items[0], nil
|
||||
})
|
||||
|
||||
//fake the dcs
|
||||
fakeClientSet.AppsClientset.PrependReactor("list", "deploymentconfigs", func(action ktesting.Action) (bool, runtime.Object, error) {
|
||||
return true, &tt.dcList, nil
|
||||
})
|
||||
|
||||
//fake the deployments
|
||||
fakeClientSet.Kubernetes.PrependReactor("list", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
|
||||
return true, &tt.deploymentList, nil
|
||||
})
|
||||
|
||||
// Prepend reactor returns the last matched reactor added
|
||||
// We need to return errorNotFound for localconfig only component
|
||||
fakeClientSet.AppsClientset.PrependReactor("get", "deploymentconfigs", func(action ktesting.Action) (bool, runtime.Object, error) {
|
||||
@@ -376,8 +432,18 @@ func TestList(t *testing.T) {
|
||||
// simulate unavailable cluster
|
||||
return true, nil, errors.NewUnauthorized("user unauthorized")
|
||||
}
|
||||
// the only other time this is called is when attempting to retrieve the state of the local component that is not pushed yet (case 4)
|
||||
return true, nil, errors.NewNotFound(schema.GroupResource{Resource: "deployments"}, "comp")
|
||||
getAction, ok := action.(ktesting.GetAction)
|
||||
if !ok {
|
||||
return false, nil, fmt.Errorf("expected a GetAction, got %v", action)
|
||||
}
|
||||
switch getAction.GetName() {
|
||||
case "comp0":
|
||||
return true, &tt.deploymentList.Items[0], nil
|
||||
case "comp1":
|
||||
return true, &tt.deploymentList.Items[1], nil
|
||||
default:
|
||||
return true, nil, errors.NewNotFound(schema.GroupResource{Resource: "deploymentconfigs"}, "")
|
||||
}
|
||||
})
|
||||
|
||||
results, err := List(client, "app", tt.existingLocalConfigInfo)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
componentlabels "github.com/openshift/odo/pkg/component/labels"
|
||||
"github.com/openshift/odo/pkg/config"
|
||||
"github.com/openshift/odo/pkg/occlient"
|
||||
"github.com/openshift/odo/pkg/storage"
|
||||
"github.com/openshift/odo/pkg/url"
|
||||
"github.com/openshift/odo/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
@@ -32,11 +33,13 @@ type PushedComponent interface {
|
||||
GetApplication() string
|
||||
GetType() (string, error)
|
||||
GetSource() (string, string, error)
|
||||
GetStorage() ([]storage.Storage, error)
|
||||
}
|
||||
|
||||
type defaultPushedComponent struct {
|
||||
application string
|
||||
urls []url.URL
|
||||
storage []storage.Storage
|
||||
provider provider
|
||||
client *occlient.Client
|
||||
}
|
||||
@@ -84,14 +87,33 @@ func (d defaultPushedComponent) GetURLs() ([]url.URL, error) {
|
||||
if err != nil && !isIgnorableError(err) {
|
||||
return []url.URL{}, err
|
||||
}
|
||||
urls := make([]url.URL, 0, len(routes.Items)+len(ingresses.Items))
|
||||
urls = append(urls, routes.Items...)
|
||||
urls = append(urls, ingresses.Items...)
|
||||
d.urls = urls
|
||||
d.urls = append(routes.Items, ingresses.Items...)
|
||||
}
|
||||
return d.urls, nil
|
||||
}
|
||||
|
||||
func (d defaultPushedComponent) GetStorage() ([]storage.Storage, error) {
|
||||
if d.storage == nil {
|
||||
var storageItems []storage.Storage
|
||||
if _, ok := d.provider.(*s2iComponent); ok {
|
||||
storageList, err := storage.ListMounted(d.client, d.GetName(), d.GetApplication())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storageItems = append(storageItems, storageList.Items...)
|
||||
}
|
||||
if _, ok := d.provider.(*devfileComponent); ok {
|
||||
storageList, err := storage.DevfileListMounted(d.client.GetKubeClient(), d.GetName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storageItems = append(storageItems, storageList.Items...)
|
||||
}
|
||||
d.storage = storageItems
|
||||
}
|
||||
return d.storage, nil
|
||||
}
|
||||
|
||||
func (d defaultPushedComponent) GetApplication() string {
|
||||
return d.application
|
||||
}
|
||||
@@ -211,27 +233,29 @@ func getType(component provider) (string, error) {
|
||||
// GetPushedComponents retrieves a map of PushedComponents from the cluster, keyed by their name
|
||||
func GetPushedComponents(c *occlient.Client, applicationName string) (map[string]PushedComponent, error) {
|
||||
applicationSelector := fmt.Sprintf("%s=%s", applabels.ApplicationLabel, applicationName)
|
||||
|
||||
dcList, err := c.GetDeploymentConfigsFromSelector(applicationSelector)
|
||||
if err != nil {
|
||||
if isIgnorableError(err) {
|
||||
dList, err := c.GetKubeClient().ListDeployments(applicationSelector)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to list components")
|
||||
}
|
||||
res := make(map[string]PushedComponent, len(dList.Items))
|
||||
for _, d := range dList.Items {
|
||||
comp := newPushedComponent(applicationName, &devfileComponent{d: d}, c)
|
||||
res[comp.GetName()] = comp
|
||||
}
|
||||
return res, nil
|
||||
if !isIgnorableError(err) {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
res := make(map[string]PushedComponent, len(dcList))
|
||||
for _, dc := range dcList {
|
||||
comp := newPushedComponent(applicationName, &s2iComponent{dc: dc}, c)
|
||||
res[comp.GetName()] = comp
|
||||
}
|
||||
|
||||
deploymentList, err := c.GetKubeClient().ListDeployments(applicationSelector)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to list components")
|
||||
}
|
||||
|
||||
for _, d := range deploymentList.Items {
|
||||
comp := newPushedComponent(applicationName, &devfileComponent{d: d}, c)
|
||||
res[comp.GetName()] = comp
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"github.com/openshift/odo/pkg/storage"
|
||||
"github.com/openshift/odo/pkg/url"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
@@ -15,14 +17,16 @@ type Component struct {
|
||||
|
||||
// ComponentSpec is spec of components
|
||||
type ComponentSpec struct {
|
||||
App string `json:"app,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
SourceType string `json:"sourceType,omitempty"`
|
||||
URL []string `json:"url,omitempty"`
|
||||
Storage []string `json:"storage,omitempty"`
|
||||
Env []corev1.EnvVar `json:"env,omitempty"`
|
||||
Ports []string `json:"ports,omitempty"`
|
||||
App string `json:"app,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
SourceType string `json:"sourceType,omitempty"`
|
||||
URL []string `json:"url,omitempty"`
|
||||
URLSpec []url.URL `json:"-"`
|
||||
Storage []string `json:"storage,omitempty"`
|
||||
StorageSpec []storage.Storage `json:"-"`
|
||||
Env []corev1.EnvVar `json:"env,omitempty"`
|
||||
Ports []string `json:"ports,omitempty"`
|
||||
}
|
||||
|
||||
// ComponentList is list of components
|
||||
|
||||
@@ -3,6 +3,7 @@ package kclient
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/openshift/odo/pkg/log"
|
||||
@@ -327,3 +328,50 @@ func (c *Client) patchDeploymentOfComponent(componentName, applicationName strin
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDeploymentLabelValues get label values of given label from objects in project that are matching selector
|
||||
// returns slice of unique label values
|
||||
func (c *Client) GetDeploymentLabelValues(label string, selector string) ([]string, error) {
|
||||
|
||||
// List DeploymentConfig according to selectors
|
||||
dcList, err := c.appsClient.Deployments(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to list DeploymentConfigs")
|
||||
}
|
||||
|
||||
// Grab all the matched strings
|
||||
var values []string
|
||||
for _, elem := range dcList.Items {
|
||||
for key, val := range elem.Labels {
|
||||
if key == label {
|
||||
values = append(values, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort alphabetically
|
||||
sort.Strings(values)
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// GetDeploymentConfigsFromSelector returns an array of Deployment Config
|
||||
// resources which match the given selector
|
||||
func (c *Client) GetDeploymentConfigsFromSelector(selector string) ([]appsv1.Deployment, error) {
|
||||
var dcList *appsv1.DeploymentList
|
||||
var err error
|
||||
|
||||
if selector != "" {
|
||||
dcList, err = c.appsClient.Deployments(c.Namespace).List(metav1.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
} else {
|
||||
dcList, err = c.appsClient.Deployments(c.Namespace).List(metav1.ListOptions{
|
||||
FieldSelector: fields.Set{"metadata.namespace": c.Namespace}.AsSelector().String(),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to list DeploymentConfigs")
|
||||
}
|
||||
return dcList.Items, nil
|
||||
}
|
||||
|
||||
@@ -13,18 +13,19 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func GetIngressListWithMultiple(componentName string) *extensionsv1.IngressList {
|
||||
func GetIngressListWithMultiple(componentName, appName string) *extensionsv1.IngressList {
|
||||
return &extensionsv1.IngressList{
|
||||
Items: []extensionsv1.Ingress{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "example-0",
|
||||
Labels: map[string]string{
|
||||
applabels.ApplicationLabel: "",
|
||||
applabels.ApplicationLabel: appName,
|
||||
componentlabels.ComponentLabel: componentName,
|
||||
applabels.OdoManagedBy: "odo",
|
||||
applabels.OdoVersion: version.VERSION,
|
||||
labels.URLLabel: "example-0",
|
||||
applabels.App: appName,
|
||||
},
|
||||
},
|
||||
Spec: *kclient.GenerateIngressSpec(kclient.IngressParameter{IngressDomain: "example-0.com", ServiceName: "example-0", PortNumber: intstr.FromInt(8080)}),
|
||||
@@ -33,11 +34,12 @@ func GetIngressListWithMultiple(componentName string) *extensionsv1.IngressList
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "example-1",
|
||||
Labels: map[string]string{
|
||||
applabels.ApplicationLabel: "",
|
||||
applabels.ApplicationLabel: "app",
|
||||
componentlabels.ComponentLabel: componentName,
|
||||
applabels.OdoManagedBy: "odo",
|
||||
applabels.OdoVersion: version.VERSION,
|
||||
labels.URLLabel: "example-1",
|
||||
applabels.App: "app",
|
||||
},
|
||||
},
|
||||
Spec: *kclient.GenerateIngressSpec(kclient.IngressParameter{IngressDomain: "example-1.com", ServiceName: "example-1", PortNumber: intstr.FromInt(9090)}),
|
||||
@@ -46,17 +48,17 @@ func GetIngressListWithMultiple(componentName string) *extensionsv1.IngressList
|
||||
}
|
||||
}
|
||||
|
||||
func GetSingleIngress(urlName, componentName string) *extensionsv1.Ingress {
|
||||
func GetSingleIngress(urlName, componentName, appName string) *extensionsv1.Ingress {
|
||||
return &extensionsv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: urlName,
|
||||
Labels: map[string]string{
|
||||
applabels.ApplicationLabel: "",
|
||||
applabels.ApplicationLabel: appName,
|
||||
componentlabels.ComponentLabel: componentName,
|
||||
applabels.OdoManagedBy: "odo",
|
||||
applabels.OdoVersion: version.VERSION,
|
||||
labels.URLLabel: urlName,
|
||||
applabels.App: "",
|
||||
applabels.App: appName,
|
||||
},
|
||||
},
|
||||
Spec: *kclient.GenerateIngressSpec(kclient.IngressParameter{IngressDomain: fmt.Sprintf("%s.com", urlName), ServiceName: urlName, PortNumber: intstr.FromInt(8080)}),
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package kclient
|
||||
|
||||
import (
|
||||
"github.com/openshift/odo/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
appsclientset "k8s.io/client-go/kubernetes/typed/apps/v1"
|
||||
"k8s.io/klog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/dynamic"
|
||||
@@ -20,6 +27,7 @@ const (
|
||||
Please ensure you have an active kubernetes context to your cluster.
|
||||
Consult your Kubernetes distribution's documentation for more details
|
||||
`
|
||||
waitForComponentDeletionTimeout = 120 * time.Second
|
||||
)
|
||||
|
||||
// Client is a collection of fields used for client configuration and interaction
|
||||
@@ -29,6 +37,7 @@ type Client struct {
|
||||
KubeClientConfig *rest.Config
|
||||
Namespace string
|
||||
OperatorClient *operatorsclientset.OperatorsV1alpha1Client
|
||||
appsClient appsclientset.AppsV1Interface
|
||||
// DynamicClient interacts with client-go's `dynamic` package. It is used
|
||||
// to dynamically create service from an operator. It can take an arbitrary
|
||||
// yaml and create k8s/OpenShift resource from it.
|
||||
@@ -77,6 +86,12 @@ func NewForConfig(config clientcmd.ClientConfig) (client *Client, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appsClient, err := appsclientset.NewForConfig(client.KubeClientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.appsClient = appsClient
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -92,3 +107,76 @@ func CreateObjectMeta(name, namespace string, labels, annotations map[string]str
|
||||
|
||||
return objectMeta
|
||||
}
|
||||
|
||||
// Delete takes labels as a input and based on it, deletes respective resource
|
||||
func (c *Client) Delete(labels map[string]string, wait bool) error {
|
||||
|
||||
// convert labels to selector
|
||||
selector := util.ConvertLabelsToSelector(labels)
|
||||
klog.V(3).Infof("Selectors used for deletion: %s", selector)
|
||||
|
||||
var errorList []string
|
||||
var deletionPolicy = metav1.DeletePropagationBackground
|
||||
|
||||
// for --wait flag, it deletes component dependents first and then delete component
|
||||
if wait {
|
||||
deletionPolicy = metav1.DeletePropagationForeground
|
||||
}
|
||||
// Delete Deployments
|
||||
klog.V(3).Info("Deleting Deployments")
|
||||
err := c.appsClient.Deployments(c.Namespace).DeleteCollection(&metav1.DeleteOptions{PropagationPolicy: &deletionPolicy}, metav1.ListOptions{LabelSelector: selector})
|
||||
if err != nil {
|
||||
errorList = append(errorList, "unable to delete deployments")
|
||||
}
|
||||
|
||||
// for --wait it waits for component to be deleted
|
||||
// TODO: Need to modify for `odo app delete`, currently wait flag is added only in `odo component delete`
|
||||
// so only one component gets passed in selector
|
||||
if wait {
|
||||
err = c.WaitForComponentDeletion(selector)
|
||||
if err != nil {
|
||||
errorList = append(errorList, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Error string
|
||||
errString := strings.Join(errorList, ",")
|
||||
if len(errString) != 0 {
|
||||
return errors.New(errString)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// WaitForComponentDeletion waits for component to be deleted
|
||||
func (c *Client) WaitForComponentDeletion(selector string) error {
|
||||
|
||||
klog.V(3).Infof("Waiting for component to get deleted")
|
||||
|
||||
watcher, err := c.appsClient.Deployments(c.Namespace).Watch(metav1.ListOptions{LabelSelector: selector})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer watcher.Stop()
|
||||
eventCh := watcher.ResultChan()
|
||||
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-eventCh:
|
||||
_, typeOk := event.Object.(*appsv1.Deployment)
|
||||
if !ok || !typeOk {
|
||||
return errors.New("Unable to watch deployments")
|
||||
}
|
||||
if event.Type == watch.Deleted {
|
||||
klog.V(3).Infof("WaitForComponentDeletion, Event Recieved:Deleted")
|
||||
return nil
|
||||
} else if event.Type == watch.Error {
|
||||
klog.V(3).Infof("WaitForComponentDeletion, Event Recieved:Deleted ")
|
||||
return errors.New("Unable to watch deployments")
|
||||
}
|
||||
case <-time.After(waitForComponentDeletionTimeout):
|
||||
klog.V(3).Infof("WaitForComponentDeletion, Timeout")
|
||||
return errors.New("Time out waiting for component to get deleted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ type FakeClientset struct {
|
||||
RouteClientset *fakeRouteClientset.Clientset
|
||||
ProjClientset *fakeProjClientset.Clientset
|
||||
ServiceCatalogClientSet *fakeServiceCatalogClientSet.Clientset
|
||||
DiscoveryClientSet *fake.FakeDiscovery
|
||||
}
|
||||
|
||||
// FakeNew creates new fake client for testing
|
||||
@@ -63,8 +64,11 @@ func FakeNew() (*Client, *FakeClientset) {
|
||||
|
||||
if os.Getenv("KUBERNETES") != "true" {
|
||||
client.SetDiscoveryInterface(fakeDiscoveryWithRoute)
|
||||
client.SetDiscoveryInterface(fakeDiscoveryWithDeploymentConfig)
|
||||
} else {
|
||||
client.SetDiscoveryInterface(&fake.FakeDiscovery{})
|
||||
client.SetDiscoveryInterface(&fakeDiscovery{
|
||||
resourceMap: map[string]*resourceMapEntry{},
|
||||
})
|
||||
}
|
||||
|
||||
return client, fkclientset
|
||||
@@ -98,6 +102,22 @@ var fakeDiscoveryWithRoute = &fakeDiscovery{
|
||||
},
|
||||
}
|
||||
|
||||
var fakeDiscoveryWithDeploymentConfig = &fakeDiscovery{
|
||||
resourceMap: map[string]*resourceMapEntry{
|
||||
"apps.openshift.io/v1": {
|
||||
list: &metav1.APIResourceList{
|
||||
GroupVersion: "apps.openshift.io/v1",
|
||||
APIResources: []metav1.APIResource{{
|
||||
Name: "deploymentconfigs",
|
||||
SingularName: "deploymentconfigs",
|
||||
Namespaced: true,
|
||||
Kind: "deploymentconfigs",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (c *fakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
@@ -2,12 +2,10 @@ package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/openshift/odo/pkg/kclient"
|
||||
"github.com/openshift/odo/pkg/log"
|
||||
"github.com/openshift/odo/pkg/occlient"
|
||||
"github.com/openshift/odo/pkg/odo/genericclioptions"
|
||||
"github.com/openshift/odo/pkg/storage"
|
||||
"github.com/openshift/odo/pkg/url"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/klog"
|
||||
|
||||
@@ -29,7 +27,7 @@ func NewCmdApplication(name, fullName string) *cobra.Command {
|
||||
applicationCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Perform application operations",
|
||||
Long: `Performs application operations related to your OpenShift project.`,
|
||||
Long: `Performs application operations related to your project.`,
|
||||
Example: fmt.Sprintf("%s\n\n%s\n\n%s",
|
||||
delete.Example,
|
||||
describe.Example,
|
||||
@@ -55,8 +53,9 @@ func AddApplicationFlag(cmd *cobra.Command) {
|
||||
completion.RegisterCommandFlagHandler(cmd, "app", completion.AppCompletionHandler)
|
||||
}
|
||||
|
||||
// printDeleteAppInfo will print things which will be deleted
|
||||
func printDeleteAppInfo(client *occlient.Client, appName string, projectName string) error {
|
||||
// printAppInfo will print things which will be deleted
|
||||
func printAppInfo(client *occlient.Client, kClient *kclient.Client, appName string, projectName string) error {
|
||||
|
||||
componentList, err := component.List(client, appName, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get Component list")
|
||||
@@ -65,29 +64,19 @@ func printDeleteAppInfo(client *occlient.Client, appName string, projectName str
|
||||
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, appName, projectName)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to get component description")
|
||||
}
|
||||
log.Info("component named", currentComponent.Name)
|
||||
|
||||
if len(componentDesc.Spec.URL) != 0 {
|
||||
ul, err := url.ListPushed(client, componentDesc.Name, appName)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Could not get url list")
|
||||
}
|
||||
if len(currentComponent.Spec.URL) != 0 {
|
||||
log.Info("This component has following urls that will be deleted with component")
|
||||
for _, u := range ul.Items {
|
||||
for _, u := range currentComponent.Spec.URLSpec {
|
||||
log.Info("URL named", u.GetName(), "with host", u.Spec.Host, "having protocol", u.Spec.Protocol, "at port", u.Spec.Port)
|
||||
}
|
||||
}
|
||||
|
||||
storages, err := storage.List(client, currentComponent.Name, appName)
|
||||
odoutil.LogErrorAndExit(err, "")
|
||||
if len(storages.Items) != 0 {
|
||||
if len(currentComponent.Spec.Storage) != 0 {
|
||||
log.Info("The component has following storages which will be deleted with the component")
|
||||
for _, storageName := range componentDesc.Spec.Storage {
|
||||
store := storages.Get(storageName)
|
||||
for _, storage := range currentComponent.Spec.StorageSpec {
|
||||
store := storage
|
||||
log.Info("Storage named", store.GetName(), "of size", store.Spec.Size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
odoUtil "github.com/openshift/odo/pkg/odo/util"
|
||||
|
||||
@@ -37,7 +38,11 @@ func NewDeleteOptions() *DeleteOptions {
|
||||
|
||||
// Complete completes DeleteOptions after they've been created
|
||||
func (o *DeleteOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) {
|
||||
o.Context = genericclioptions.NewContext(cmd)
|
||||
if util.CheckPathExists(filepath.Join(".odo", "config.yaml")) {
|
||||
o.Context = genericclioptions.NewContext(cmd)
|
||||
} else {
|
||||
o.Context = genericclioptions.NewDevfileContext(cmd)
|
||||
}
|
||||
o.appName = o.Application
|
||||
if len(args) == 1 {
|
||||
// If app name passed, consider it for deletion
|
||||
@@ -56,7 +61,7 @@ func (o *DeleteOptions) Validate() (err error) {
|
||||
return fmt.Errorf("given output format %s is not supported", o.OutputFlag)
|
||||
}
|
||||
|
||||
exist, err := application.Exists(o.appName, o.Client)
|
||||
exist, err := application.Exists(o.appName, o.Client, o.KClient)
|
||||
if !exist {
|
||||
return fmt.Errorf("%s app does not exists", o.appName)
|
||||
}
|
||||
@@ -74,7 +79,7 @@ func (o *DeleteOptions) Run() (err error) {
|
||||
}
|
||||
|
||||
// Print App Information which will be deleted
|
||||
err = printDeleteAppInfo(o.Client, o.appName, o.Project)
|
||||
err = printAppInfo(o.Client, o.KClient, o.appName, o.Project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
odoutil "github.com/openshift/odo/pkg/util"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/openshift/odo/pkg/application"
|
||||
"github.com/openshift/odo/pkg/component"
|
||||
@@ -37,7 +39,11 @@ func NewDescribeOptions() *DescribeOptions {
|
||||
|
||||
// Complete completes DescribeOptions after they've been created
|
||||
func (o *DescribeOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) {
|
||||
o.Context = genericclioptions.NewContext(cmd)
|
||||
if odoutil.CheckPathExists(filepath.Join(".odo", "config.yaml")) {
|
||||
o.Context = genericclioptions.NewContext(cmd)
|
||||
} else {
|
||||
o.Context = genericclioptions.NewDevfileContext(cmd)
|
||||
}
|
||||
o.appName = o.Application
|
||||
if len(args) == 1 {
|
||||
o.appName = args[0]
|
||||
@@ -58,7 +64,7 @@ func (o *DescribeOptions) Validate() (err error) {
|
||||
return fmt.Errorf("There's no active application in project: %v", o.Project)
|
||||
}
|
||||
|
||||
exist, err := application.Exists(o.appName, o.Client)
|
||||
exist, err := application.Exists(o.appName, o.Client, o.KClient)
|
||||
if !exist {
|
||||
return fmt.Errorf("%s app does not exists", o.appName)
|
||||
}
|
||||
@@ -71,7 +77,6 @@ func (o *DescribeOptions) Run() (err error) {
|
||||
appDef := application.GetMachineReadableFormat(o.Client, o.appName, o.Project)
|
||||
machineoutput.OutputSuccess(appDef)
|
||||
} else {
|
||||
// List of Component
|
||||
componentList, err := component.List(o.Client, o.appName, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -87,9 +92,7 @@ func (o *DescribeOptions) Run() (err error) {
|
||||
o.appName, len(componentList.Items), len(serviceList.Items))
|
||||
if len(componentList.Items) > 0 {
|
||||
for _, currentComponent := range componentList.Items {
|
||||
componentDesc, err := component.GetComponent(o.Client, currentComponent.Name, o.appName, o.Project)
|
||||
util.LogErrorAndExit(err, "")
|
||||
util.PrintComponentInfo(o.Client, currentComponent.Name, componentDesc, o.appName, o.Project)
|
||||
util.PrintComponentInfo(o.Client, currentComponent.Name, currentComponent, o.appName, o.Project)
|
||||
fmt.Println("--------------------------------------")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func NewListOptions() *ListOptions {
|
||||
|
||||
// Complete completes ListOptions after they've been created
|
||||
func (o *ListOptions) Complete(name string, cmd *cobra.Command, args []string) (err error) {
|
||||
o.Context = genericclioptions.NewContext(cmd)
|
||||
o.Context = genericclioptions.NewDevfileContext(cmd)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ func (lo *ListOptions) Complete(name string, cmd *cobra.Command, args []string)
|
||||
if util.CheckPathExists(lo.devfilePath) {
|
||||
|
||||
lo.Context = genericclioptions.NewDevfileContext(cmd)
|
||||
lo.Client = genericclioptions.Client(cmd)
|
||||
lo.hasDCSupport, err = lo.Client.IsDeploymentConfigSupported()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
routev1 "github.com/openshift/api/route/v1"
|
||||
"github.com/openshift/odo/pkg/devfile"
|
||||
"github.com/openshift/odo/pkg/envinfo"
|
||||
"github.com/openshift/odo/pkg/occlient"
|
||||
pkgutil "github.com/openshift/odo/pkg/util"
|
||||
|
||||
clicomponent "github.com/openshift/odo/pkg/odo/cli/component"
|
||||
@@ -123,12 +122,8 @@ func (o *URLListOptions) Run() (err error) {
|
||||
}
|
||||
} else {
|
||||
componentName := o.EnvSpecificInfo.GetName()
|
||||
oclient, err := occlient.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oclient.Namespace = o.KClient.Namespace
|
||||
routeSupported, err := oclient.IsRouteSupported()
|
||||
|
||||
routeSupported, err := o.Context.Client.IsRouteSupported()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -138,7 +133,7 @@ func (o *URLListOptions) Run() (err error) {
|
||||
}
|
||||
|
||||
containerComponents := adaptersCommon.GetDevfileContainerComponents(devObj.Data)
|
||||
urls, err := url.ListIngressAndRoute(oclient, o.EnvSpecificInfo, containerComponents, componentName, routeSupported)
|
||||
urls, err := url.ListIngressAndRoute(o.Context.Client, o.EnvSpecificInfo, containerComponents, componentName, routeSupported)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ func NewContext(command *cobra.Command, toggles ...bool) *Context {
|
||||
}
|
||||
|
||||
// NewDevfileContext creates a new Context struct populated with the current state based on flags specified for the provided command
|
||||
func NewDevfileContext(command *cobra.Command, ignoreMissingConfiguration ...bool) *Context {
|
||||
return newDevfileContext(command)
|
||||
func NewDevfileContext(command *cobra.Command) *Context {
|
||||
return newDevfileContext(command, false)
|
||||
}
|
||||
|
||||
// NewContextCreatingAppIfNeeded creates a new Context struct populated with the current state based on flags specified for the
|
||||
@@ -156,11 +156,15 @@ func getValidEnvInfo(command *cobra.Command) (*envinfo.EnvSpecificInfo, error) {
|
||||
// Get details from the env file
|
||||
componentContext := FlagValueIfSet(command, ContextFlagName)
|
||||
|
||||
// Grab the absolute path of the eenv file
|
||||
// Grab the absolute path of the env file
|
||||
if componentContext != "" {
|
||||
fAbs, err := pkgUtil.GetAbsPath(componentContext)
|
||||
util.LogErrorAndExit(err, "")
|
||||
componentContext = fAbs
|
||||
} else {
|
||||
fAbs, err := pkgUtil.GetAbsPath(".")
|
||||
util.LogErrorAndExit(err, "")
|
||||
componentContext = fAbs
|
||||
}
|
||||
|
||||
// Access the env file
|
||||
@@ -273,6 +277,7 @@ func (o *internalCxt) resolveProject(localConfiguration envinfo.LocalConfigProvi
|
||||
checkProjectCreateOrDeleteOnlyOnInvalidNamespace(command, errFormat)
|
||||
}
|
||||
}
|
||||
o.Client.GetKubeClient().Namespace = namespace
|
||||
o.Client.Namespace = namespace
|
||||
o.Project = namespace
|
||||
if o.KClient != nil {
|
||||
@@ -311,6 +316,8 @@ func (o *internalCxt) resolveNamespace(configProvider envinfo.LocalConfigProvide
|
||||
checkProjectCreateOrDeleteOnlyOnInvalidNamespaceNoFmt(command, errFormat)
|
||||
}
|
||||
}
|
||||
o.Client.Namespace = namespace
|
||||
o.Client.GetKubeClient().Namespace = namespace
|
||||
o.KClient.Namespace = namespace
|
||||
o.Project = namespace
|
||||
}
|
||||
@@ -408,7 +415,7 @@ func newContext(command *cobra.Command, createAppIfNeeded bool, ignoreMissingCon
|
||||
}
|
||||
|
||||
// newDevfileContext creates a new context based on command flags for devfile components
|
||||
func newDevfileContext(command *cobra.Command) *Context {
|
||||
func newDevfileContext(command *cobra.Command, createAppIfNeeded bool) *Context {
|
||||
|
||||
// Resolve output flag
|
||||
outputFlag := FlagValueIfSet(command, OutputFlagName)
|
||||
@@ -428,7 +435,7 @@ func newDevfileContext(command *cobra.Command) *Context {
|
||||
}
|
||||
|
||||
internalCxt.EnvSpecificInfo = envInfo
|
||||
internalCxt.resolveApp(true, envInfo)
|
||||
internalCxt.resolveApp(createAppIfNeeded, envInfo)
|
||||
|
||||
// If the push target is NOT Docker we will set the client to Kubernetes.
|
||||
if !pushtarget.IsPushTargetDocker() {
|
||||
@@ -438,6 +445,8 @@ func newDevfileContext(command *cobra.Command) *Context {
|
||||
internalCxt.Client = client(command)
|
||||
|
||||
// Gather the environment information
|
||||
internalCxt.EnvSpecificInfo = envInfo
|
||||
|
||||
internalCxt.resolveNamespace(envInfo)
|
||||
}
|
||||
|
||||
|
||||
@@ -102,13 +102,10 @@ func PrintComponentInfo(client *occlient.Client, currentComponentName string, co
|
||||
if len(componentDesc.Spec.Storage) > 0 {
|
||||
|
||||
var storages storage.StorageList
|
||||
var err error
|
||||
|
||||
if componentDesc.Status.State == "Pushed" {
|
||||
// Retrieve the storage list
|
||||
storages, err = storage.List(client, currentComponentName, applicationName)
|
||||
LogErrorAndExit(err, "")
|
||||
|
||||
storages = storage.StorageList{Items: componentDesc.Spec.StorageSpec}
|
||||
} else {
|
||||
localConfig, err := config.New()
|
||||
LogErrorAndExit(err, "")
|
||||
@@ -145,8 +142,7 @@ func PrintComponentInfo(client *occlient.Client, currentComponentName string, co
|
||||
}
|
||||
} else {
|
||||
// Retrieve the URLs
|
||||
urls, err := urlPkg.ListPushed(client, currentComponentName, applicationName)
|
||||
LogErrorAndExit(err, "")
|
||||
urls := urlPkg.URLList{Items: componentDesc.Spec.URLSpec}
|
||||
|
||||
// Gather the output
|
||||
for _, componentURL := range componentDesc.Spec.URL {
|
||||
|
||||
@@ -569,8 +569,8 @@ func MachineReadableSuccessOutput(storageName string, message string) {
|
||||
machineoutput.OutputSuccess(machineOutput)
|
||||
}
|
||||
|
||||
// devfileListMounted lists the storage which are mounted on a container
|
||||
func devfileListMounted(kClient *kclient.Client, componentName string) (StorageList, error) {
|
||||
// DevfileListMounted lists the storage which are mounted on a container
|
||||
func DevfileListMounted(kClient *kclient.Client, componentName string) (StorageList, error) {
|
||||
pod, err := kClient.GetPodUsingComponentName(componentName)
|
||||
if err != nil {
|
||||
if _, ok := err.(*kclient.PodNotFoundError); ok {
|
||||
@@ -654,7 +654,7 @@ func GetLocalDevfileStorage(devfileData data.DevfileData) StorageList {
|
||||
func DevfileList(kClient *kclient.Client, devfileData data.DevfileData, componentName string) (StorageList, error) {
|
||||
localStorage := GetLocalDevfileStorage(devfileData)
|
||||
|
||||
clusterStorage, err := devfileListMounted(kClient, componentName)
|
||||
clusterStorage, err := DevfileListMounted(kClient, componentName)
|
||||
if err != nil {
|
||||
return StorageList{}, err
|
||||
}
|
||||
|
||||
@@ -1234,7 +1234,7 @@ func TestDevfileListMounted(t *testing.T) {
|
||||
return true, tt.returnedPods, nil
|
||||
})
|
||||
|
||||
got, err := devfileListMounted(fakeClient, tt.args.componentName)
|
||||
got, err := DevfileListMounted(fakeClient, tt.args.componentName)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("devfileListMounted() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package testingutil
|
||||
|
||||
import (
|
||||
applabels "github.com/openshift/odo/pkg/application/labels"
|
||||
componentlabels "github.com/openshift/odo/pkg/component/labels"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -14,6 +16,11 @@ func CreateFakeDeployment(podName string) *appsv1.Deployment {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
UID: fakeUID,
|
||||
Labels: map[string]string{
|
||||
applabels.App: "app",
|
||||
applabels.ApplicationLabel: "app",
|
||||
componentlabels.ComponentLabel: podName,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &deployment
|
||||
|
||||
@@ -151,11 +151,11 @@ func GetIngressOrRoute(client *occlient.Client, kClient *kclient.Client, envSpec
|
||||
}
|
||||
|
||||
// Delete deletes a URL
|
||||
func Delete(client *occlient.Client, kClient *kclient.Client, urlName string, applicationName string, urlType envinfo.URLKind) error {
|
||||
func Delete(client *occlient.Client, kClient *kclient.Client, urlName string, applicationName string, urlType envinfo.URLKind, isS2i bool) error {
|
||||
if urlType == envinfo.INGRESS {
|
||||
return kClient.DeleteIngress(urlName)
|
||||
} else if urlType == envinfo.ROUTE {
|
||||
if applicationName != "" {
|
||||
if isS2i {
|
||||
// Namespace the URL name
|
||||
var err error
|
||||
urlName, err = util.NamespaceOpenShiftObject(urlName, applicationName)
|
||||
@@ -1022,7 +1022,7 @@ func Push(client *occlient.Client, kClient *kclient.Client, parameters PushParam
|
||||
// to avoid error due to duplicate ingress name defined in different devfile components
|
||||
deleteURLName = fmt.Sprintf("%s-%s", urlName, parameters.ComponentName)
|
||||
}
|
||||
err := Delete(client, kClient, deleteURLName, parameters.ApplicationName, urlSpec.Spec.Kind)
|
||||
err := Delete(client, kClient, deleteURLName, parameters.ApplicationName, urlSpec.Spec.Kind, parameters.IsS2I)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -178,13 +178,14 @@ func TestCreate(t *testing.T) {
|
||||
name: "Case 4: Create a ingress, with same name as component,instead of route on openshift cluster",
|
||||
args: args{
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
urlName: "nodejs",
|
||||
portNumber: 8080,
|
||||
host: "com",
|
||||
isRouteSupported: true,
|
||||
urlKind: envinfo.INGRESS,
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("nodejs-nodejs", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("nodejs-nodejs", "nodejs", "app"),
|
||||
want: "http://nodejs.com",
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -192,6 +193,7 @@ func TestCreate(t *testing.T) {
|
||||
name: "Case 5: Create a ingress, with different name as component,instead of route on openshift cluster",
|
||||
args: args{
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
urlName: "example",
|
||||
portNumber: 8080,
|
||||
host: "com",
|
||||
@@ -220,7 +222,7 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs", "app"),
|
||||
want: "http://example.com",
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -228,6 +230,7 @@ func TestCreate(t *testing.T) {
|
||||
name: "Case 6: Create a secure ingress, instead of route on openshift cluster, default tls exists",
|
||||
args: args{
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
urlName: "example",
|
||||
portNumber: 8080,
|
||||
host: "com",
|
||||
@@ -235,7 +238,7 @@ func TestCreate(t *testing.T) {
|
||||
secure: true,
|
||||
urlKind: envinfo.INGRESS,
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs", "app"),
|
||||
defaultTLSExists: true,
|
||||
want: "https://example.com",
|
||||
wantErr: false,
|
||||
@@ -244,6 +247,7 @@ func TestCreate(t *testing.T) {
|
||||
name: "Case 7: Create a secure ingress, instead of route on openshift cluster and default tls doesn't exist",
|
||||
args: args{
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
urlName: "example",
|
||||
portNumber: 8080,
|
||||
host: "com",
|
||||
@@ -251,7 +255,7 @@ func TestCreate(t *testing.T) {
|
||||
secure: true,
|
||||
urlKind: envinfo.INGRESS,
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs", "app"),
|
||||
defaultTLSExists: false,
|
||||
want: "https://example.com",
|
||||
wantErr: false,
|
||||
@@ -259,6 +263,7 @@ func TestCreate(t *testing.T) {
|
||||
{
|
||||
name: "Case 8: Fail when while creating ingress when user given tls secret doesn't exists",
|
||||
args: args{
|
||||
applicationName: "app",
|
||||
componentName: "nodejs",
|
||||
urlName: "example",
|
||||
portNumber: 8080,
|
||||
@@ -268,7 +273,7 @@ func TestCreate(t *testing.T) {
|
||||
tlsSecret: "user-secret",
|
||||
urlKind: envinfo.INGRESS,
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("example", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("example", "nodejs", "app"),
|
||||
defaultTLSExists: false,
|
||||
userGivenTLSExists: false,
|
||||
want: "http://example.com",
|
||||
@@ -277,6 +282,7 @@ func TestCreate(t *testing.T) {
|
||||
{
|
||||
name: "Case 9: Create a secure ingress, instead of route on openshift cluster, user tls secret does exists",
|
||||
args: args{
|
||||
applicationName: "app",
|
||||
componentName: "nodejs",
|
||||
urlName: "example",
|
||||
portNumber: 8080,
|
||||
@@ -286,7 +292,7 @@ func TestCreate(t *testing.T) {
|
||||
tlsSecret: "user-secret",
|
||||
urlKind: envinfo.INGRESS,
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs", "app"),
|
||||
defaultTLSExists: false,
|
||||
userGivenTLSExists: true,
|
||||
want: "https://example.com",
|
||||
@@ -296,6 +302,7 @@ func TestCreate(t *testing.T) {
|
||||
{
|
||||
name: "Case 10: invalid url kind",
|
||||
args: args{
|
||||
applicationName: "app",
|
||||
componentName: "nodejs",
|
||||
urlName: "example",
|
||||
portNumber: 8080,
|
||||
@@ -305,7 +312,7 @@ func TestCreate(t *testing.T) {
|
||||
tlsSecret: "user-secret",
|
||||
urlKind: "blah",
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("example-nodejs", "nodejs", "app"),
|
||||
defaultTLSExists: false,
|
||||
userGivenTLSExists: true,
|
||||
want: "",
|
||||
@@ -320,7 +327,7 @@ func TestCreate(t *testing.T) {
|
||||
isRouteSupported: false,
|
||||
urlKind: envinfo.ROUTE,
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("example", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("example", "nodejs", "app"),
|
||||
defaultTLSExists: false,
|
||||
userGivenTLSExists: true,
|
||||
want: "",
|
||||
@@ -336,7 +343,7 @@ func TestCreate(t *testing.T) {
|
||||
tlsSecret: "secret",
|
||||
urlKind: envinfo.ROUTE,
|
||||
},
|
||||
returnedIngress: fake.GetSingleIngress("example", "nodejs"),
|
||||
returnedIngress: fake.GetSingleIngress("example", "nodejs", "app"),
|
||||
defaultTLSExists: false,
|
||||
userGivenTLSExists: true,
|
||||
want: "",
|
||||
@@ -890,15 +897,17 @@ func TestPush(t *testing.T) {
|
||||
{
|
||||
name: "0 urls on env file and cluster",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{},
|
||||
returnedRoutes: &routev1.RouteList{},
|
||||
returnedIngress: &extensionsv1.IngressList{},
|
||||
},
|
||||
{
|
||||
name: "2 urls on env file and 0 on openshift cluster",
|
||||
componentName: "nodejs",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "2 urls on env file and 0 on openshift cluster",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example",
|
||||
@@ -960,10 +969,11 @@ func TestPush(t *testing.T) {
|
||||
{
|
||||
name: "0 urls on env file and 2 on openshift cluster",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{},
|
||||
returnedRoutes: &routev1.RouteList{},
|
||||
returnedIngress: fake.GetIngressListWithMultiple("nodejs"),
|
||||
returnedIngress: fake.GetIngressListWithMultiple("nodejs", "app"),
|
||||
deletedURLs: []URL{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -978,9 +988,10 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "2 urls on env file and 2 on openshift cluster, but they are different",
|
||||
componentName: "wildfly",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "2 urls on env file and 2 on openshift cluster, but they are different",
|
||||
componentName: "wildfly",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example-local-0",
|
||||
@@ -1013,7 +1024,7 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
returnedRoutes: &routev1.RouteList{},
|
||||
returnedIngress: fake.GetIngressListWithMultiple("wildfly"),
|
||||
returnedIngress: fake.GetIngressListWithMultiple("wildfly", "app"),
|
||||
createdURLs: []URL{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -1052,9 +1063,10 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "2 urls on env file and openshift cluster are in sync",
|
||||
componentName: "wildfly",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "2 urls on env file and openshift cluster are in sync",
|
||||
componentName: "wildfly",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example-0",
|
||||
@@ -1087,14 +1099,15 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
returnedRoutes: &routev1.RouteList{},
|
||||
returnedIngress: fake.GetIngressListWithMultiple("wildfly"),
|
||||
returnedIngress: fake.GetIngressListWithMultiple("wildfly", "app"),
|
||||
createdURLs: []URL{},
|
||||
deletedURLs: []URL{},
|
||||
},
|
||||
{
|
||||
name: "2 (1 ingress,1 route) urls on env file and 2 on openshift cluster (1 ingress,1 route), but they are different",
|
||||
componentName: "nodejs",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "2 (1 ingress,1 route) urls on env file and 2 on openshift cluster (1 ingress,1 route), but they are different",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example-local-0",
|
||||
@@ -1126,7 +1139,7 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
returnedRoutes: &routev1.RouteList{},
|
||||
returnedIngress: fake.GetIngressListWithMultiple("nodejs"),
|
||||
returnedIngress: fake.GetIngressListWithMultiple("nodejs", "app"),
|
||||
createdURLs: []URL{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -1164,9 +1177,10 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create a ingress on a kubernetes cluster",
|
||||
componentName: "nodejs",
|
||||
args: args{isRouteSupported: false},
|
||||
name: "create a ingress on a kubernetes cluster",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: false},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example",
|
||||
@@ -1207,8 +1221,9 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "url with same name exists on env and cluster but with different specs",
|
||||
componentName: "nodejs",
|
||||
name: "url with same name exists on env and cluster but with different specs",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{
|
||||
isRouteSupported: true,
|
||||
},
|
||||
@@ -1235,7 +1250,7 @@ func TestPush(t *testing.T) {
|
||||
returnedRoutes: &routev1.RouteList{},
|
||||
returnedIngress: &extensionsv1.IngressList{
|
||||
Items: []extensionsv1.Ingress{
|
||||
*fake.GetSingleIngress("example-local-0", "nodejs"),
|
||||
*fake.GetSingleIngress("example-local-0", "nodejs", "app"),
|
||||
},
|
||||
},
|
||||
createdURLs: []URL{
|
||||
@@ -1299,9 +1314,10 @@ func TestPush(t *testing.T) {
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "create a secure route url",
|
||||
componentName: "nodejs",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "create a secure route url",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example",
|
||||
@@ -1338,9 +1354,10 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create a secure ingress url with empty user given tls secret",
|
||||
componentName: "nodejs",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "create a secure ingress url with empty user given tls secret",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example",
|
||||
@@ -1379,9 +1396,10 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create a secure ingress url with user given tls secret",
|
||||
componentName: "nodejs",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "create a secure ingress url with user given tls secret",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example",
|
||||
@@ -1570,6 +1588,7 @@ func TestPush(t *testing.T) {
|
||||
{
|
||||
name: "should create route in openshift cluster if endpoint is defined in devfile",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{},
|
||||
containerComponents: []versionsCommon.DevfileComponent{
|
||||
@@ -1604,9 +1623,10 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should create ingress if endpoint is defined in devfile",
|
||||
componentName: "nodejs",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "should create ingress if endpoint is defined in devfile",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example",
|
||||
@@ -1649,6 +1669,7 @@ func TestPush(t *testing.T) {
|
||||
{
|
||||
name: "should create route in openshift cluster with path defined in devfile",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{},
|
||||
containerComponents: []versionsCommon.DevfileComponent{
|
||||
@@ -1684,9 +1705,10 @@ func TestPush(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should create ingress with path defined in devfile",
|
||||
componentName: "nodejs",
|
||||
args: args{isRouteSupported: true},
|
||||
name: "should create ingress with path defined in devfile",
|
||||
componentName: "nodejs",
|
||||
applicationName: "app",
|
||||
args: args{isRouteSupported: true},
|
||||
existingEnvInfoURLs: []envinfo.EnvInfoURL{
|
||||
{
|
||||
Name: "example",
|
||||
@@ -2041,7 +2063,7 @@ func TestListIngressAndRoute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
routeSupported: true,
|
||||
ingressList: fake.GetIngressListWithMultiple(componentName),
|
||||
ingressList: fake.GetIngressListWithMultiple(componentName, "app"),
|
||||
routeList: &routev1.RouteList{
|
||||
Items: []routev1.Route{
|
||||
testingutil.GetSingleRoute(testURL4.Name, int(exampleEndpoint.TargetPort), componentName, ""),
|
||||
@@ -2114,7 +2136,7 @@ func TestListIngressAndRoute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
routeList: &routev1.RouteList{},
|
||||
ingressList: fake.GetIngressListWithMultiple(componentName),
|
||||
ingressList: fake.GetIngressListWithMultiple(componentName, "app"),
|
||||
routeSupported: false,
|
||||
wantURLs: []URL{
|
||||
URL{
|
||||
@@ -2159,7 +2181,7 @@ func TestListIngressAndRoute(t *testing.T) {
|
||||
},
|
||||
routeSupported: true,
|
||||
routeList: &routev1.RouteList{},
|
||||
ingressList: fake.GetIngressListWithMultiple(componentName),
|
||||
ingressList: fake.GetIngressListWithMultiple(componentName, "app"),
|
||||
wantURLs: []URL{
|
||||
URL{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "url", APIVersion: "odo.dev/v1alpha1"},
|
||||
@@ -2366,7 +2388,7 @@ func TestGetIngressOrRoute(t *testing.T) {
|
||||
component: componentName,
|
||||
urlName: testURL1.Name,
|
||||
routeSupported: true,
|
||||
pushedIngress: fake.GetSingleIngress(testURL1.Name, componentName),
|
||||
pushedIngress: fake.GetSingleIngress(testURL1.Name, componentName, "app"),
|
||||
pushedRoute: routev1.Route{},
|
||||
wantURL: URL{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "url", APIVersion: "odo.dev/v1alpha1"},
|
||||
@@ -2383,7 +2405,7 @@ func TestGetIngressOrRoute(t *testing.T) {
|
||||
component: componentName,
|
||||
urlName: testURL2.Name,
|
||||
routeSupported: true,
|
||||
pushedIngress: fake.GetSingleIngress(testURL2.Name, componentName),
|
||||
pushedIngress: fake.GetSingleIngress(testURL2.Name, componentName, "app"),
|
||||
pushedRoute: routev1.Route{},
|
||||
wantURL: URL{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "url", APIVersion: "odo.dev/v1alpha1"},
|
||||
@@ -2504,7 +2526,7 @@ func TestGetIngressOrRoute(t *testing.T) {
|
||||
component: componentName,
|
||||
urlName: testURL2.Name,
|
||||
routeSupported: false,
|
||||
pushedIngress: fake.GetSingleIngress(testURL2.Name, componentName),
|
||||
pushedIngress: fake.GetSingleIngress(testURL2.Name, componentName, "app"),
|
||||
pushedRoute: routev1.Route{},
|
||||
wantURL: URL{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "url", APIVersion: "odo.dev/v1alpha1"},
|
||||
@@ -2521,7 +2543,7 @@ func TestGetIngressOrRoute(t *testing.T) {
|
||||
component: componentName,
|
||||
urlName: testURL1.Name,
|
||||
routeSupported: false,
|
||||
pushedIngress: fake.GetSingleIngress(testURL1.Name, componentName),
|
||||
pushedIngress: fake.GetSingleIngress(testURL1.Name, componentName, "app"),
|
||||
pushedRoute: routev1.Route{},
|
||||
wantURL: URL{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "url", APIVersion: "odo.dev/v1alpha1"},
|
||||
|
||||
@@ -260,6 +260,12 @@ func Suffocate(s string) string {
|
||||
return strings.ReplaceAll(strings.ReplaceAll(s, " ", ""), "\t", "")
|
||||
}
|
||||
|
||||
// IsJson returns true if a string is in json format
|
||||
func IsJSON(s string) bool {
|
||||
var js map[string]interface{}
|
||||
return json.Unmarshal([]byte(s), &js) == nil
|
||||
}
|
||||
|
||||
type CommonVar struct {
|
||||
// Project is new clean project/namespace for each test
|
||||
Project string
|
||||
|
||||
@@ -30,7 +30,7 @@ var _ = Describe("odo app command tests", func() {
|
||||
Context("when running help for app command", func() {
|
||||
It("should display the help", func() {
|
||||
appHelp := helper.CmdShouldPass("odo", "app", "-h")
|
||||
Expect(appHelp).To(ContainSubstring("Performs application operations related to your OpenShift project."))
|
||||
Expect(appHelp).To(ContainSubstring("Performs application operations related to your project."))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -58,19 +58,19 @@ var _ = Describe("odo app command tests", func() {
|
||||
// changing directory to the context directory
|
||||
helper.Chdir(commonVar.Context)
|
||||
|
||||
appListOutput := helper.CmdShouldPass("odo", "app", "list")
|
||||
appListOutput := helper.CmdShouldPass("odo", "app", "list", "--project", commonVar.Project)
|
||||
Expect(appListOutput).To(ContainSubstring(appName))
|
||||
actualCompListJSON := helper.CmdShouldPass("odo", "list", "-o", "json")
|
||||
actualCompListJSON := helper.CmdShouldPass("odo", "list", "-o", "json", "--project", commonVar.Project)
|
||||
|
||||
desiredCompListJSON := fmt.Sprintf(`{"kind":"List","apiVersion":"odo.dev/v1alpha1","metadata":{},"s2iComponents":[{"kind":"Component","apiVersion":"odo.dev/v1alpha1","metadata":{"name":"nodejs","namespace":"%s","creationTimestamp":null},"spec":{"app":"app","type":"nodejs","sourceType":"local","env":[{"name":"DEBUG_PORT","value":"5858"}]},"status":{"state":"Pushed"}}],"devfileComponents":[]}`, commonVar.Project)
|
||||
Expect(desiredCompListJSON).Should(MatchJSON(actualCompListJSON))
|
||||
|
||||
helper.CmdShouldPass("odo", "app", "describe")
|
||||
helper.CmdShouldPass("odo", "app", "describe", "--project", commonVar.Project)
|
||||
desiredDesAppJSON := fmt.Sprintf(`{"kind":"Application","apiVersion":"odo.dev/v1alpha1","metadata":{"name":"app","namespace":"%s","creationTimestamp":null},"spec":{"components": ["nodejs"]}}`, commonVar.Project)
|
||||
actualDesAppJSON := helper.CmdShouldPass("odo", "app", "describe", "app", "-o", "json")
|
||||
actualDesAppJSON := helper.CmdShouldPass("odo", "app", "describe", "app", "-o", "json", "--project", commonVar.Project)
|
||||
Expect(desiredDesAppJSON).Should(MatchJSON(actualDesAppJSON))
|
||||
|
||||
helper.CmdShouldPass("odo", "app", "delete", "-f")
|
||||
helper.CmdShouldPass("odo", "app", "delete", "-f", "--project", commonVar.Project)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -83,8 +83,8 @@ var _ = Describe("odo app command tests", func() {
|
||||
// list should pass as the project exists
|
||||
appListOutput := helper.CmdShouldPass("odo", "app", "list", "--project", commonVar.Project)
|
||||
Expect(appListOutput).To(ContainSubstring(appName))
|
||||
helper.CmdShouldFail("odo", "app", "describe")
|
||||
helper.CmdShouldFail("odo", "app", "delete", "-f")
|
||||
helper.CmdShouldFail("odo", "app", "describe", "--project", commonVar.Project)
|
||||
helper.CmdShouldFail("odo", "app", "delete", "-f", "--project", commonVar.Project)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -187,15 +187,24 @@ func componentTests(args ...string) {
|
||||
helper.CmdShouldPass("odo", append(args, "create", "--s2i", "nodejs", "cmp-git", "--project", commonVar.Project, "--git", "https://github.com/openshift/nodejs-ex", "--context", commonVar.Context, "--app", "testing")...)
|
||||
helper.ValidateLocalCmpExist(commonVar.Context, "Type,nodejs", "Name,cmp-git", "Application,testing")
|
||||
kubeconfigOrig := os.Getenv("KUBECONFIG")
|
||||
os.Setenv("KUBECONFIG", "/no/such/path")
|
||||
cmpList := helper.CmdShouldPass("odo", append(args, "list", "--context", commonVar.Context, "--v", "9")...)
|
||||
helper.MatchAllInOutput(cmpList, []string{"cmp-git", "Unknown"})
|
||||
// KUBECONFIG defaults to ~/.kube/config so it can be empty in some cases.
|
||||
if kubeconfigOrig != "" {
|
||||
os.Setenv("KUBECONFIG", kubeconfigOrig)
|
||||
} else {
|
||||
os.Unsetenv("KUBECONFIG")
|
||||
|
||||
unset := func() {
|
||||
// KUBECONFIG defaults to ~/.kube/config so it can be empty in some cases.
|
||||
if kubeconfigOrig != "" {
|
||||
os.Setenv("KUBECONFIG", kubeconfigOrig)
|
||||
} else {
|
||||
os.Unsetenv("KUBECONFIG")
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("KUBECONFIG", "/no/such/path")
|
||||
|
||||
defer unset()
|
||||
cmpList := helper.CmdShouldPass("odo", append(args, "list", "--context", commonVar.Context, "--v", "9")...)
|
||||
|
||||
helper.MatchAllInOutput(cmpList, []string{"cmp-git", "Unknown"})
|
||||
unset()
|
||||
|
||||
fmt.Printf("kubeconfig before delete %v", os.Getenv("KUBECONFIG"))
|
||||
helper.CmdShouldPass("odo", append(args, "delete", "-f", "--all", "--context", commonVar.Context)...)
|
||||
})
|
||||
|
||||
125
tests/integration/devfile/cmd_devfile_app_test.go
Normal file
125
tests/integration/devfile/cmd_devfile_app_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package devfile
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/openshift/odo/tests/helper"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = Describe("odo devfile app command tests", func() {
|
||||
|
||||
var namespace, context, currentWorkingDirectory, originalKubeconfig string
|
||||
|
||||
// Using program command according to cliRunner in devfile
|
||||
cliRunner := helper.GetCliRunner()
|
||||
|
||||
// This is run before every Spec (It)
|
||||
var _ = BeforeEach(func() {
|
||||
SetDefaultEventuallyTimeout(10 * time.Minute)
|
||||
context = helper.CreateNewContext()
|
||||
os.Setenv("GLOBALODOCONFIG", filepath.Join(context, "config.yaml"))
|
||||
originalKubeconfig = os.Getenv("KUBECONFIG")
|
||||
helper.LocalKubeconfigSet(context)
|
||||
namespace = cliRunner.CreateRandNamespaceProject()
|
||||
currentWorkingDirectory = helper.Getwd()
|
||||
})
|
||||
|
||||
// This is run after every Spec (It)
|
||||
var _ = AfterEach(func() {
|
||||
cliRunner.DeleteNamespaceProject(namespace)
|
||||
helper.Chdir(currentWorkingDirectory)
|
||||
err := os.Setenv("KUBECONFIG", originalKubeconfig)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
helper.DeleteDir(context)
|
||||
os.Unsetenv("GLOBALODOCONFIG")
|
||||
})
|
||||
|
||||
Context("listing apps", func() {
|
||||
It("it should list, describe and delete the apps", func() {
|
||||
runner(namespace, false)
|
||||
})
|
||||
})
|
||||
|
||||
Context("Testing URLs for OpenShift specific scenarios", func() {
|
||||
JustBeforeEach(func() {
|
||||
if os.Getenv("KUBERNETES") == "true" {
|
||||
Skip("This is a OpenShift specific scenario, skipping")
|
||||
}
|
||||
})
|
||||
|
||||
It("it should list, describe and delete the apps", func() {
|
||||
runner(namespace, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func runner(namespace string, s2i bool) {
|
||||
context0 := helper.CreateNewContext()
|
||||
context00 := helper.CreateNewContext()
|
||||
context1 := helper.CreateNewContext()
|
||||
|
||||
defer func() {
|
||||
helper.DeleteDir(context0)
|
||||
helper.DeleteDir(context00)
|
||||
helper.DeleteDir(context1)
|
||||
}()
|
||||
|
||||
app0 := helper.RandString(4)
|
||||
app1 := helper.RandString(4)
|
||||
|
||||
component0 := helper.RandString(4)
|
||||
component00 := helper.RandString(4)
|
||||
component1 := helper.RandString(4)
|
||||
|
||||
storage00 := helper.RandString(4)
|
||||
url00 := helper.RandString(4)
|
||||
|
||||
helper.CmdShouldPass("odo", "create", "nodejs", "--project", namespace, component0, "--context", context0, "--app", app0)
|
||||
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), context0)
|
||||
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(context0, "devfile.yaml"))
|
||||
helper.CmdShouldPass("odo", "push", "--context", context0)
|
||||
|
||||
if s2i {
|
||||
helper.CopyExample(filepath.Join("source", "nodejs"), context00)
|
||||
helper.CmdShouldPass("odo", "component", "create", "--s2i", "nodejs", component00, "--app", app0, "--project", namespace, "--context", context00)
|
||||
helper.CmdShouldPass("odo", "storage", "create", storage00, "--path", "/data", "--size", "1Gi", "--context", context00)
|
||||
helper.CmdShouldPass("odo", "url", "create", url00, "--port", "8080", "--context", context00)
|
||||
} else {
|
||||
helper.CmdShouldPass("odo", "create", "nodejs", "--project", namespace, component00, "--context", context00, "--app", app0)
|
||||
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), context00)
|
||||
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(context00, "devfile.yaml"))
|
||||
helper.CmdShouldPass("odo", "storage", "create", storage00, "--path", "/data", "--size", "1Gi", "--context", context00)
|
||||
helper.CmdShouldPass("odo", "url", "create", url00, "--port", "3000", "--context", context00, "--host", "com", "--ingress")
|
||||
}
|
||||
helper.CmdShouldPass("odo", "push", "--context", context00)
|
||||
|
||||
helper.CmdShouldPass("odo", "create", "nodejs", "--project", namespace, component1, "--context", context1, "--app", app1)
|
||||
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), context1)
|
||||
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(context1, "devfile.yaml"))
|
||||
helper.CmdShouldPass("odo", "push", "--context", context1)
|
||||
|
||||
stdOut := helper.CmdShouldPass("odo", "app", "list", "--project", namespace)
|
||||
helper.MatchAllInOutput(stdOut, []string{app0, app1})
|
||||
|
||||
// test the json output
|
||||
stdOut = helper.CmdShouldPass("odo", "app", "list", "--project", namespace, "-o", "json")
|
||||
helper.MatchAllInOutput(stdOut, []string{app0, app1})
|
||||
Expect(helper.IsJSON(stdOut)).To(BeTrue())
|
||||
|
||||
stdOut = helper.CmdShouldPass("odo", "app", "describe", app0, "--project", namespace)
|
||||
helper.MatchAllInOutput(stdOut, []string{app0, component0, component00, storage00, url00})
|
||||
|
||||
// test the json output
|
||||
stdOut = helper.CmdShouldPass("odo", "app", "describe", app0, "--project", namespace, "-o", "json")
|
||||
helper.MatchAllInOutput(stdOut, []string{app0, component0, component00})
|
||||
Expect(helper.IsJSON(stdOut)).To(BeTrue())
|
||||
|
||||
stdOut = helper.CmdShouldPass("odo", "app", "delete", app0, "--project", namespace, "-f")
|
||||
helper.MatchAllInOutput(stdOut, []string{app0, component0, component00, url00, storage00})
|
||||
|
||||
stdOut = helper.CmdShouldPass("odo", "app", "list", "--project", namespace)
|
||||
helper.MatchAllInOutput(stdOut, []string{app1})
|
||||
}
|
||||
Reference in New Issue
Block a user