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 }