diff --git a/pkg/application/application.go b/pkg/application/application.go index 1230cf4f9..5a42cec12 100644 --- a/pkg/application/application.go +++ b/pkg/application/application.go @@ -7,7 +7,6 @@ import ( "github.com/golang/glog" "github.com/pkg/errors" - applabels "github.com/redhat-developer/odo/pkg/application/labels" "github.com/redhat-developer/odo/pkg/component" "github.com/redhat-developer/odo/pkg/occlient" @@ -82,14 +81,18 @@ func Create(client *occlient.Client, appName string) error { // List all applications in current project func List(client *occlient.Client) ([]preference.ApplicationInfo, error) { - return ListInProject(client) + return ListInProject(client, client.Namespace) } // ListInProject lists all applications in given project // Queries cluster and config file. // Shows also empty applications (empty applications are those that are just // mentioned in config file but don't have any object associated with it on cluster). -func ListInProject(client *occlient.Client) ([]preference.ApplicationInfo, error) { +// ListInProject lists all applications in given project +// Queries cluster and config file. +// Shows also empty applications (empty applications are those that are just +// mentioned in config file but don't have any object associated with it on cluster). +func ListInProject(client *occlient.Client, projectName string) ([]preference.ApplicationInfo, error) { var applications []preference.ApplicationInfo cfg, err := preference.New() @@ -99,7 +102,7 @@ func ListInProject(client *occlient.Client) ([]preference.ApplicationInfo, error // All applications of the current project from config file for i := range cfg.ActiveApplications { - if cfg.ActiveApplications[i].Project == client.Namespace { + if cfg.ActiveApplications[i].Project == projectName { applications = append(applications, cfg.ActiveApplications[i]) } } @@ -114,7 +117,7 @@ func ListInProject(client *occlient.Client) ([]preference.ApplicationInfo, error // skip applications that are already in the list (they were mentioned in config file) found := false for _, app := range applications { - if app.Project == client.Namespace && app.Name == name { + if app.Project == projectName && app.Name == name { found = true } } @@ -123,7 +126,7 @@ func ListInProject(client *occlient.Client) ([]preference.ApplicationInfo, error Name: name, // if this application is not in config file, it can't be active Active: false, - Project: client.Namespace, + Project: projectName, }) } } @@ -252,7 +255,7 @@ func SetCurrent(client *occlient.Client, appName string) error { // Exists returns true if given application (appName) exists func Exists(client *occlient.Client, appName string) (bool, error) { - apps, err := ListInProject(client) + apps, err := List(client) if err != nil { return false, errors.Wrap(err, "unable to list applications") } diff --git a/pkg/odo/cli/application/application.go b/pkg/odo/cli/application/application.go index 87419b10c..db60a63ad 100644 --- a/pkg/odo/cli/application/application.go +++ b/pkg/odo/cli/application/application.go @@ -3,19 +3,22 @@ package application import ( "fmt" + "github.com/golang/glog" + "github.com/pkg/errors" "github.com/redhat-developer/odo/pkg/application" "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/occlient" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" "github.com/redhat-developer/odo/pkg/storage" + "github.com/redhat-developer/odo/pkg/url" "github.com/redhat-developer/odo/pkg/component" odoutil "github.com/redhat-developer/odo/pkg/odo/util" "github.com/redhat-developer/odo/pkg/odo/util/completion" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - + "github.com/redhat-developer/odo/pkg/service" "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // RecommendedCommandName is the recommended app command name @@ -78,21 +81,47 @@ func printDeleteAppInfo(client *occlient.Client, appName string, projectName str return errors.Wrap(err, "failed to get Component list") } - 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", currentComponent.Name, "will be deleted.") + 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 { - fmt.Println(" Externally exposed URLs will be removed") + if len(componentDesc.Spec.URL) != 0 { + ul, err := url.List(client, componentDesc.Name, appName) + if err != nil { + return errors.Wrap(err, "Could not get url list") + } + log.Info(" This component has following urls that will be deleted with component") + for _, u := range ul.Items { + log.Info(" URL named ", u.GetName(), " with host ", u.Spec.Host, " having protocol ", u.Spec.Protocol, " at port ", u.Spec.Port) + } + } + + storages, err := storage.List(client, currentComponent.Name, appName) + odoutil.LogErrorAndExit(err, "") + if len(storages.Items) != 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) + log.Info(" Storage named ", store.GetName(), " of size ", store.Spec.Size) + } + } } - storages, err := storage.List(client, currentComponent.Name, appName) - odoutil.LogErrorAndExit(err, "") - for _, storageName := range componentDesc.Spec.Storage { - store := storages.Get(storageName) - fmt.Println(" Storage", store.Name, "of size", store.Spec.Size, "will be removed") + // List services that will be removed + serviceList, err := service.List(client, appName) + if err != nil { + log.Info("No services / could not get services") + glog.V(4).Info(err.Error()) + } + if len(serviceList) != 0 { + log.Info("This application has following service that will be deleted") + for _, ser := range serviceList { + log.Info(" service named ", ser.Name, " of type ", ser.Type) + } } } diff --git a/pkg/odo/cli/application/delete.go b/pkg/odo/cli/application/delete.go index 55d262810..880f01447 100644 --- a/pkg/odo/cli/application/delete.go +++ b/pkg/odo/cli/application/delete.go @@ -2,6 +2,7 @@ package application import ( "fmt" + "github.com/redhat-developer/odo/pkg/application" "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/odo/cli/project" diff --git a/pkg/odo/cli/application/list.go b/pkg/odo/cli/application/list.go index f41179098..e2b59bb30 100644 --- a/pkg/odo/cli/application/list.go +++ b/pkg/odo/cli/application/list.go @@ -49,7 +49,7 @@ func (o *ListOptions) Validate() (err error) { // Run contains the logic for the odo command func (o *ListOptions) Run() (err error) { - apps, err := application.ListInProject(o.Client) + apps, err := application.List(o.Client) if err != nil { return fmt.Errorf("unable to get list of applications: %v", err) } diff --git a/pkg/odo/cli/component/component.go b/pkg/odo/cli/component/component.go index fb2ffb2a8..c97a7b4ad 100644 --- a/pkg/odo/cli/component/component.go +++ b/pkg/odo/cli/component/component.go @@ -3,7 +3,13 @@ package component import ( "fmt" + "github.com/pkg/errors" + "github.com/redhat-developer/odo/pkg/component" + "github.com/redhat-developer/odo/pkg/log" + "github.com/redhat-developer/odo/pkg/occlient" "github.com/redhat-developer/odo/pkg/odo/util/completion" + "github.com/redhat-developer/odo/pkg/storage" + "github.com/redhat-developer/odo/pkg/url" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" odoutil "github.com/redhat-developer/odo/pkg/odo/util" @@ -86,3 +92,33 @@ func AddComponentFlag(cmd *cobra.Command) { cmd.Flags().String(genericclioptions.ComponentFlagName, "", "Component, defaults to active component.") completion.RegisterCommandFlagHandler(cmd, "component", completion.ComponentNameCompletionHandler) } + +// printDeleteComponentInfo will print things which will be deleted +func printDeleteComponentInfo(client *occlient.Client, componentName string, appName string, projectName string) error { + componentDesc, err := component.GetComponent(client, componentName, appName, projectName) + if err != nil { + return errors.Wrap(err, "unable to get component description") + } + + if len(componentDesc.Spec.URL) != 0 { + log.Info("This component has following urls that will be deleted with component") + ul, err := url.List(client, componentDesc.Name, appName) + if err != nil { + return errors.Wrap(err, "Could not get url list") + } + for _, u := range ul.Items { + log.Info(" URL named ", u.GetName(), " with host ", u.Spec.Host, " having protocol ", u.Spec.Protocol, " at port ", u.Spec.Port) + } + } + + storages, err := storage.List(client, componentDesc.Name, appName) + odoutil.LogErrorAndExit(err, "") + if len(storages.Items) != 0 { + log.Info("This component has following storages which will be deleted with the component") + for _, storageName := range componentDesc.Spec.Storage { + store := storages.Get(storageName) + log.Info(" Storage", store.GetName(), "of size", store.Spec.Size) + } + } + return nil +} diff --git a/pkg/odo/cli/component/delete.go b/pkg/odo/cli/component/delete.go index dac2dd73d..c196941dc 100644 --- a/pkg/odo/cli/component/delete.go +++ b/pkg/odo/cli/component/delete.go @@ -2,15 +2,17 @@ package component import ( "fmt" + + "github.com/redhat-developer/odo/pkg/odo/genericclioptions" + "github.com/pkg/errors" + "github.com/redhat-developer/odo/pkg/log" appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application" projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project" "github.com/redhat-developer/odo/pkg/odo/cli/ui" - "github.com/redhat-developer/odo/pkg/odo/genericclioptions" "github.com/redhat-developer/odo/pkg/odo/util/completion" ktemplates "k8s.io/kubernetes/pkg/kubectl/cmd/templates" - "github.com/redhat-developer/odo/pkg/log" odoutil "github.com/redhat-developer/odo/pkg/odo/util" "github.com/golang/glog" @@ -59,6 +61,11 @@ func (do *DeleteOptions) Run() (err error) { glog.V(4).Infof("component delete called") glog.V(4).Infof("args: %#v", do) + err = printDeleteComponentInfo(do.Client, do.componentName, do.Context.Application, do.Context.Project) + if err != nil { + return err + } + if do.componentForceDeleteFlag || ui.Proceed(fmt.Sprintf("Are you sure you want to delete %v from %v?", do.componentName, do.Application)) { err := component.Delete(do.Client, do.componentName, do.Application) if err != nil { @@ -78,7 +85,7 @@ func (do *DeleteOptions) Run() (err error) { } } else { - log.Infof("Aborting deletion of component: %v", do.componentName) + return fmt.Errorf("Aborting deletion of component: %v", do.componentName) } return diff --git a/pkg/odo/cli/project/delete.go b/pkg/odo/cli/project/delete.go index 62aaab704..cf124095c 100644 --- a/pkg/odo/cli/project/delete.go +++ b/pkg/odo/cli/project/delete.go @@ -2,6 +2,7 @@ package project import ( "fmt" + "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/odo/cli/ui" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" @@ -60,6 +61,10 @@ func (pdo *ProjectDeleteOptions) Validate() (err error) { // Run runs the project delete command func (pdo *ProjectDeleteOptions) Run() (err error) { + err = printDeleteProjectInfo(pdo.Context.Client, pdo.projectName) + if err != nil { + return err + } if pdo.projectForceDeleteFlag || ui.Proceed(fmt.Sprintf("Are you sure you want to delete project %v", pdo.projectName)) { currentProject, err := project.Delete(pdo.Context.Client, pdo.projectName) if err != nil { diff --git a/pkg/odo/cli/project/project.go b/pkg/odo/cli/project/project.go index f68fe8594..9e5c81add 100644 --- a/pkg/odo/cli/project/project.go +++ b/pkg/odo/cli/project/project.go @@ -3,9 +3,18 @@ package project import ( "fmt" + "github.com/golang/glog" + "github.com/pkg/errors" + "github.com/redhat-developer/odo/pkg/application" + "github.com/redhat-developer/odo/pkg/component" + "github.com/redhat-developer/odo/pkg/log" + "github.com/redhat-developer/odo/pkg/occlient" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" odoutil "github.com/redhat-developer/odo/pkg/odo/util" "github.com/redhat-developer/odo/pkg/odo/util/completion" + "github.com/redhat-developer/odo/pkg/service" + "github.com/redhat-developer/odo/pkg/storage" + "github.com/redhat-developer/odo/pkg/url" "github.com/spf13/cobra" ) @@ -66,3 +75,71 @@ func AddProjectFlag(cmd *cobra.Command) { cmd.Flags().String(genericclioptions.ProjectFlagName, "", "Project, defaults to active project") completion.RegisterCommandFlagHandler(cmd, "project", completion.ProjectNameCompletionHandler) } + +// printDeleteProjectInfo prints objects affected by project deletion +func printDeleteProjectInfo(client *occlient.Client, projectName string) error { + // Fetch and List the applications + applicationList, err := application.ListInProject(client, projectName) + if err != nil { + return errors.Wrap(err, "failed to get application list") + } + if len(applicationList) != 0 { + log.Info("This project contains the following applications, which will be deleted") + for _, app := range applicationList { + log.Info(" Application ", app.Name) + + // List the components + componentList, err := component.List(client, app.Name) + if err != nil { + return errors.Wrap(err, "failed to get Component list") + } + if len(componentList.Items) != 0 { + log.Info(" This application has following components that will be deleted") + + for _, currentComponent := range componentList.Items { + componentDesc, err := component.GetComponent(client, currentComponent.Name, app.Name, app.Project) + if err != nil { + return errors.Wrap(err, "unable to get component description") + } + log.Info(" component named ", componentDesc.Name) + + if len(componentDesc.Spec.URL) != 0 { + ul, err := url.List(client, componentDesc.Name, app.Name) + if err != nil { + return errors.Wrap(err, "Could not get url list") + } + log.Info(" This component has following urls that will be deleted with component") + for _, u := range ul.Items { + log.Info(" URL named ", u.GetName(), " with host ", u.Spec.Host, " having protocol ", u.Spec.Protocol, " at port ", u.Spec.Port) + } + } + + storages, err := storage.List(client, currentComponent.Name, app.Name) + odoutil.LogErrorAndExit(err, "") + if len(storages.Items) != 0 { + log.Info(" This component has following storages which will be deleted with the component") + for _, storageName := range componentDesc.Spec.Storage { + store := storages.Get(storageName) + log.Info(" Storage named ", store.GetName(), " of size ", store.Spec.Size) + } + } + } + } + + // List services that will be removed + serviceList, err := service.List(client, app.Name) + if err != nil { + log.Info("No services / could not get services") + glog.V(4).Info(err.Error()) + } + + if len(serviceList) != 0 { + log.Info(" This application has following service that will be deleted") + for _, ser := range serviceList { + log.Info(" service named ", ser.Name, " of type ", ser.Type) + } + } + } + } + return nil +} diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index e990ecdc5..36ac159e9 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -3,16 +3,17 @@ package e2e import ( + "os" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "fmt" "io/ioutil" - "os" "regexp" "strings" "testing" "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" ) // TODO: A neater way to provide odo path. Currently we assume \ @@ -725,7 +726,7 @@ var _ = Describe("odoe2e", func() { }) It("should delete the deployed image-specific component", func() { - runCmdShouldPass("odo delete testversioncmp") + runCmdShouldPass("odo delete testversioncmp -f") }) }) @@ -1152,6 +1153,54 @@ var _ = Describe("updateE2e", func() { Expect(token).To(ContainSubstring(userToken)) }) }) + + Context("Deleting application, with component, should list affected children", func() { + projectName := generateTimeBasedName("project") + appName := generateTimeBasedName("app") + componentName := generateTimeBasedName("component") + urlName := generateTimeBasedName("url") + + It("Should setup for the tests ,by creating dummy projects to test against", func() { + odoCreateProject(projectName) + runCmdShouldPass("odo app create " + appName) + runCmdShouldPass("odo component create nodejs " + componentName) + runCmdShouldPass("odo url create " + urlName) + }) + + It("Should list affected child objects", func() { + session := runCmdShouldPass("odo app delete -f " + appName) + Expect(session).To(ContainSubstring("This application has following components that will be deleted")) + Expect(session).To(ContainSubstring(componentName)) + Expect(session).To(ContainSubstring(urlName)) + }) + + It("Should delete project", func() { + odoDeleteProject(projectName) + }) + }) + + Context("Deleting project, with application should list affected children", func() { + projectName := generateTimeBasedName("project") + appName := generateTimeBasedName("app") + componentName := generateTimeBasedName("component") + urlName := generateTimeBasedName("url") + + It("Should setup for the tests ,by creating dummy projects to test against", func() { + odoCreateProject(projectName) + runCmdShouldPass("odo app create " + appName) + runCmdShouldPass("odo component create nodejs " + componentName) + runCmdShouldPass("odo url create " + urlName) + }) + + It("Should list affected child objects", func() { + session := runCmdShouldPass("odo project delete -f " + projectName) + Expect(session).To(ContainSubstring("This project contains the following applications, which will be deleted")) + Expect(session).To(ContainSubstring(appName)) + Expect(session).To(ContainSubstring(componentName)) + Expect(session).To(ContainSubstring(urlName)) + }) + }) + Context("logout of the cluster", func() { // test for odo logout It("should logout the user from the cluster", func() {