github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/cmd/juju/service/removeservice.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package service
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names"
    12  	"github.com/juju/romulus/api/budget"
    13  	wireformat "github.com/juju/romulus/wireformat/budget"
    14  	"gopkg.in/juju/charm.v6-unstable"
    15  	"gopkg.in/macaroon-bakery.v1/httpbakery"
    16  
    17  	"github.com/juju/juju/api/charms"
    18  	apiservice "github.com/juju/juju/api/service"
    19  	"github.com/juju/juju/cmd/juju/block"
    20  	"github.com/juju/juju/cmd/modelcmd"
    21  )
    22  
    23  // NewRemoveServiceCommand returns a command which removes a service.
    24  func NewRemoveServiceCommand() cmd.Command {
    25  	return modelcmd.Wrap(&removeServiceCommand{})
    26  }
    27  
    28  // removeServiceCommand causes an existing service to be destroyed.
    29  type removeServiceCommand struct {
    30  	modelcmd.ModelCommandBase
    31  	ServiceName string
    32  }
    33  
    34  var helpSummaryRmSvc = `
    35  Remove a service from the model.`[1:]
    36  
    37  var helpDetailsRmSvc = `
    38  Removing a service will terminate any relations that service has, remove
    39  all units of the service, and in the case that this leaves machines with
    40  no running services, Juju will also remove the machine. For this reason,
    41  you should retrieve any logs or data required from services and units 
    42  before removing them. Removing units which are co-located with units of
    43  other charms or a Juju controller will not result in the removal of the
    44  machine.
    45  
    46  Examples:
    47      juju remove-service hadoop
    48      juju remove-service -m test-model mariadb`[1:]
    49  
    50  func (c *removeServiceCommand) Info() *cmd.Info {
    51  	return &cmd.Info{
    52  		Name:    "remove-service",
    53  		Args:    "<service>",
    54  		Purpose: helpSummaryRmSvc,
    55  		Doc:     helpDetailsRmSvc,
    56  		Aliases: []string{"destroy-service"},
    57  	}
    58  }
    59  
    60  func (c *removeServiceCommand) Init(args []string) error {
    61  	if len(args) == 0 {
    62  		return fmt.Errorf("no service specified")
    63  	}
    64  	if !names.IsValidService(args[0]) {
    65  		return fmt.Errorf("invalid service name %q", args[0])
    66  	}
    67  	c.ServiceName, args = args[0], args[1:]
    68  	return cmd.CheckEmpty(args)
    69  }
    70  
    71  type ServiceAPI interface {
    72  	Close() error
    73  	Destroy(serviceName string) error
    74  	DestroyUnits(unitNames ...string) error
    75  	GetCharmURL(serviceName string) (*charm.URL, error)
    76  	ModelUUID() string
    77  }
    78  
    79  func (c *removeServiceCommand) getAPI() (ServiceAPI, error) {
    80  	root, err := c.NewAPIRoot()
    81  	if err != nil {
    82  		return nil, errors.Trace(err)
    83  	}
    84  	return apiservice.NewClient(root), nil
    85  }
    86  
    87  func (c *removeServiceCommand) Run(ctx *cmd.Context) error {
    88  	client, err := c.getAPI()
    89  	if err != nil {
    90  		return err
    91  	}
    92  	defer client.Close()
    93  	err = block.ProcessBlockedError(client.Destroy(c.ServiceName), block.BlockRemove)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	return c.removeAllocation(ctx)
    98  }
    99  
   100  func (c *removeServiceCommand) removeAllocation(ctx *cmd.Context) error {
   101  	client, err := c.getAPI()
   102  	if err != nil {
   103  		return err
   104  	}
   105  	charmURL, err := client.GetCharmURL(c.ServiceName)
   106  	if err != nil {
   107  		return errors.Trace(err)
   108  	}
   109  	if charmURL.Schema == "local" {
   110  		return nil
   111  	}
   112  
   113  	root, err := c.NewAPIRoot()
   114  	if err != nil {
   115  		return errors.Trace(err)
   116  	}
   117  	charmsClient := charms.NewClient(root)
   118  	metered, err := charmsClient.IsMetered(charmURL.String())
   119  	if err != nil {
   120  		return errors.Trace(err)
   121  	}
   122  	if !metered {
   123  		return nil
   124  	}
   125  
   126  	modelUUID := client.ModelUUID()
   127  	bakeryClient, err := c.BakeryClient()
   128  	if err != nil {
   129  		return errors.Trace(err)
   130  	}
   131  	budgetClient := getBudgetAPIClient(bakeryClient)
   132  
   133  	resp, err := budgetClient.DeleteAllocation(modelUUID, c.ServiceName)
   134  	if wireformat.IsNotAvail(err) {
   135  		fmt.Fprintf(ctx.Stdout, "WARNING: Allocation not removed - %s.\n", err.Error())
   136  	} else if err != nil {
   137  		return err
   138  	}
   139  	if resp != "" {
   140  		fmt.Fprintf(ctx.Stdout, "%s\n", resp)
   141  	}
   142  	return nil
   143  }
   144  
   145  var getBudgetAPIClient = getBudgetAPIClientImpl
   146  
   147  func getBudgetAPIClientImpl(bakeryClient *httpbakery.Client) budgetAPIClient {
   148  	return budget.NewClient(bakeryClient)
   149  }
   150  
   151  type budgetAPIClient interface {
   152  	DeleteAllocation(string, string) (string, error)
   153  }