github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/application/removeapplication.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package application
     5  
     6  import (
     7  	"github.com/juju/cmd"
     8  	"github.com/juju/collections/set"
     9  	"github.com/juju/errors"
    10  	"github.com/juju/gnuflag"
    11  	"gopkg.in/juju/names.v2"
    12  
    13  	"github.com/juju/juju/api/application"
    14  	"github.com/juju/juju/api/storage"
    15  	"github.com/juju/juju/apiserver/params"
    16  	jujucmd "github.com/juju/juju/cmd"
    17  	"github.com/juju/juju/cmd/juju/block"
    18  	"github.com/juju/juju/cmd/modelcmd"
    19  )
    20  
    21  // NewRemoveApplicationCommand returns a command which removes an application.
    22  func NewRemoveApplicationCommand() cmd.Command {
    23  	return modelcmd.Wrap(&removeApplicationCommand{})
    24  }
    25  
    26  // removeApplicationCommand causes an existing application to be destroyed.
    27  type removeApplicationCommand struct {
    28  	modelcmd.ModelCommandBase
    29  	DestroyStorage   bool
    30  	ApplicationNames []string
    31  }
    32  
    33  var helpSummaryRmApp = `
    34  Remove applications from the model.`[1:]
    35  
    36  var helpDetailsRmApp = `
    37  Removing an application will terminate any relations that application has, remove
    38  all units of the application, and in the case that this leaves machines with
    39  no running applications, Juju will also remove the machine. For this reason,
    40  you should retrieve any logs or data required from applications and units 
    41  before removing them. Removing units which are co-located with units of
    42  other charms or a Juju controller will not result in the removal of the
    43  machine.
    44  
    45  Examples:
    46      juju remove-application hadoop
    47      juju remove-application -m test-model mariadb`[1:]
    48  
    49  func (c *removeApplicationCommand) Info() *cmd.Info {
    50  	return jujucmd.Info(&cmd.Info{
    51  		Name:    "remove-application",
    52  		Args:    "<application> [<application>...]",
    53  		Purpose: helpSummaryRmApp,
    54  		Doc:     helpDetailsRmApp,
    55  	})
    56  }
    57  
    58  func (c *removeApplicationCommand) SetFlags(f *gnuflag.FlagSet) {
    59  	c.ModelCommandBase.SetFlags(f)
    60  	f.BoolVar(&c.DestroyStorage, "destroy-storage", false, "Destroy storage attached to application units")
    61  }
    62  
    63  func (c *removeApplicationCommand) Init(args []string) error {
    64  	if len(args) == 0 {
    65  		return errors.Errorf("no application specified")
    66  	}
    67  	for _, arg := range args {
    68  		if !names.IsValidApplication(arg) {
    69  			return errors.Errorf("invalid application name %q", arg)
    70  		}
    71  	}
    72  	c.ApplicationNames = args
    73  	return nil
    74  }
    75  
    76  type removeApplicationAPI interface {
    77  	Close() error
    78  	ScaleApplication(application.ScaleApplicationParams) (params.ScaleApplicationResult, error)
    79  	DestroyApplications(application.DestroyApplicationsParams) ([]params.DestroyApplicationResult, error)
    80  	DestroyDeprecated(appName string) error
    81  	DestroyUnits(application.DestroyUnitsParams) ([]params.DestroyUnitResult, error)
    82  	DestroyUnitsDeprecated(unitNames ...string) error
    83  	ModelUUID() string
    84  	BestAPIVersion() int
    85  }
    86  
    87  type storageAPI interface {
    88  	Close() error
    89  	ListStorageDetails() ([]params.StorageDetails, error)
    90  }
    91  
    92  func (c *removeApplicationCommand) getAPI() (removeApplicationAPI, int, error) {
    93  	root, err := c.NewAPIRoot()
    94  	if err != nil {
    95  		return nil, -1, errors.Trace(err)
    96  	}
    97  	version := root.BestFacadeVersion("Application")
    98  	return application.NewClient(root), version, nil
    99  }
   100  
   101  func (c *removeApplicationCommand) getStorageAPI() (storageAPI, error) {
   102  	root, err := c.NewAPIRoot()
   103  	if err != nil {
   104  		return nil, errors.Trace(err)
   105  	}
   106  	return storage.NewClient(root), nil
   107  }
   108  
   109  func (c *removeApplicationCommand) applicationsHaveStorage(appNames []string) (bool, error) {
   110  	client, err := c.getStorageAPI()
   111  	if err != nil {
   112  		return false, errors.Trace(err)
   113  	}
   114  	defer client.Close()
   115  
   116  	storage, err := client.ListStorageDetails()
   117  	if err != nil {
   118  		return false, errors.Trace(err)
   119  	}
   120  	namesSet := set.NewStrings(appNames...)
   121  	for _, s := range storage {
   122  		if s.OwnerTag == "" {
   123  			continue
   124  		}
   125  		owner, err := names.ParseTag(s.OwnerTag)
   126  		if err != nil {
   127  			return false, errors.Trace(err)
   128  		}
   129  		if owner.Kind() != names.UnitTagKind {
   130  			continue
   131  		}
   132  		appName, err := names.UnitApplication(owner.Id())
   133  		if err != nil {
   134  			return false, errors.Trace(err)
   135  		}
   136  		if namesSet.Contains(appName) {
   137  			return true, nil
   138  		}
   139  	}
   140  	return false, nil
   141  }
   142  
   143  func (c *removeApplicationCommand) Run(ctx *cmd.Context) error {
   144  	client, apiVersion, err := c.getAPI()
   145  	if err != nil {
   146  		return err
   147  	}
   148  	defer client.Close()
   149  
   150  	if apiVersion < 4 {
   151  		return c.removeApplicationsDeprecated(ctx, client)
   152  	}
   153  	if c.DestroyStorage && apiVersion < 5 {
   154  		return errors.New("--destroy-storage is not supported by this controller")
   155  	}
   156  	return c.removeApplications(ctx, client)
   157  }
   158  
   159  // TODO(axw) 2017-03-16 #1673323
   160  // Drop this in Juju 3.0.
   161  func (c *removeApplicationCommand) removeApplicationsDeprecated(
   162  	ctx *cmd.Context,
   163  	client removeApplicationAPI,
   164  ) error {
   165  	for _, name := range c.ApplicationNames {
   166  		err := client.DestroyDeprecated(name)
   167  		if err := block.ProcessBlockedError(err, block.BlockRemove); err != nil {
   168  			return errors.Trace(err)
   169  		}
   170  	}
   171  	return nil
   172  }
   173  
   174  func (c *removeApplicationCommand) removeApplications(
   175  	ctx *cmd.Context,
   176  	client removeApplicationAPI,
   177  ) error {
   178  	results, err := client.DestroyApplications(application.DestroyApplicationsParams{
   179  		Applications:   c.ApplicationNames,
   180  		DestroyStorage: c.DestroyStorage,
   181  	})
   182  	if err := block.ProcessBlockedError(err, block.BlockRemove); err != nil {
   183  		return errors.Trace(err)
   184  	}
   185  	anyFailed := false
   186  	for i, name := range c.ApplicationNames {
   187  		result := results[i]
   188  		if result.Error != nil {
   189  			ctx.Infof("removing application %s failed: %s", name, result.Error)
   190  			anyFailed = true
   191  			continue
   192  		}
   193  		ctx.Infof("removing application %s", name)
   194  		for _, entity := range result.Info.DestroyedUnits {
   195  			unitTag, err := names.ParseUnitTag(entity.Tag)
   196  			if err != nil {
   197  				logger.Warningf("%s", err)
   198  				continue
   199  			}
   200  			ctx.Verbosef("- will remove %s", names.ReadableString(unitTag))
   201  		}
   202  		for _, entity := range result.Info.DestroyedStorage {
   203  			storageTag, err := names.ParseStorageTag(entity.Tag)
   204  			if err != nil {
   205  				logger.Warningf("%s", err)
   206  				continue
   207  			}
   208  			ctx.Infof("- will remove %s", names.ReadableString(storageTag))
   209  		}
   210  		for _, entity := range result.Info.DetachedStorage {
   211  			storageTag, err := names.ParseStorageTag(entity.Tag)
   212  			if err != nil {
   213  				logger.Warningf("%s", err)
   214  				continue
   215  			}
   216  			ctx.Infof("- will detach %s", names.ReadableString(storageTag))
   217  		}
   218  	}
   219  	if anyFailed {
   220  		return cmd.ErrSilent
   221  	}
   222  	return nil
   223  }