github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/juju/system/kill.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package system
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	"launchpad.net/gnuflag"
    12  
    13  	"github.com/juju/juju/api"
    14  	"github.com/juju/juju/api/systemmanager"
    15  	"github.com/juju/juju/apiserver/common"
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/environs"
    18  	"github.com/juju/juju/environs/configstore"
    19  )
    20  
    21  var (
    22  	apiTimeout      = 10 * time.Second
    23  	ErrConnTimedOut = errors.New("connection to state server timed out")
    24  )
    25  
    26  const killDoc = `
    27  Forcibly destroy the specified system.  If the API server is accessible,
    28  this command will attempt to destroy the state server environment and all
    29  hosted environments and their resources.
    30  
    31  If the API server is unreachable, the machines of the state server environment
    32  will be destroyed through the cloud provisioner.  If there are additional
    33  machines, including machines within hosted environments, these machines
    34  will not be destroyed and will never be reconnected to the Juju system being
    35  destroyed.
    36  `
    37  
    38  // KillCommand kills the specified system.
    39  type KillCommand struct {
    40  	DestroyCommandBase
    41  	// TODO (cherylj) If timeouts for dialing the API are added to new or
    42  	// existing commands later, the dialer should be pulled into a common
    43  	// base and made to be an interface rather than a function.
    44  	apiDialerFunc func(string) (api.Connection, error)
    45  }
    46  
    47  // Info implements Command.Info.
    48  func (c *KillCommand) Info() *cmd.Info {
    49  	return &cmd.Info{
    50  		Name:    "kill",
    51  		Args:    "<system name>",
    52  		Purpose: "forcibly terminate all machines and other associated resources for a system environment",
    53  		Doc:     killDoc,
    54  	}
    55  }
    56  
    57  // SetFlags implements Command.SetFlags.
    58  func (c *KillCommand) SetFlags(f *gnuflag.FlagSet) {
    59  	f.BoolVar(&c.assumeYes, "y", false, "Do not ask for confirmation")
    60  	f.BoolVar(&c.assumeYes, "yes", false, "")
    61  }
    62  
    63  // Init implements Command.Init.
    64  func (c *KillCommand) Init(args []string) error {
    65  	if c.apiDialerFunc == nil {
    66  		// This should never happen, but check here instead of panicking later.
    67  		return errors.New("no api dialer specified")
    68  	}
    69  
    70  	return c.DestroyCommandBase.Init(args)
    71  }
    72  
    73  func (c *KillCommand) getSystemAPI(info configstore.EnvironInfo) (destroySystemAPI, error) {
    74  	if c.api != nil {
    75  		return c.api, c.apierr
    76  	}
    77  
    78  	// Attempt to connect to the API with a short timeout
    79  	apic := make(chan api.Connection)
    80  	errc := make(chan error)
    81  	go func() {
    82  		api, dialErr := c.apiDialerFunc(c.systemName)
    83  		if dialErr != nil {
    84  			errc <- dialErr
    85  			return
    86  		}
    87  		apic <- api
    88  	}()
    89  
    90  	var apiRoot api.Connection
    91  	select {
    92  	case err := <-errc:
    93  		return nil, err
    94  	case apiRoot = <-apic:
    95  	case <-time.After(apiTimeout):
    96  		return nil, ErrConnTimedOut
    97  	}
    98  
    99  	return systemmanager.NewClient(apiRoot), nil
   100  }
   101  
   102  // Run implements Command.Run
   103  func (c *KillCommand) Run(ctx *cmd.Context) error {
   104  	store, err := configstore.Default()
   105  	if err != nil {
   106  		return errors.Annotate(err, "cannot open system info storage")
   107  	}
   108  
   109  	cfgInfo, err := store.ReadInfo(c.systemName)
   110  	if err != nil {
   111  		return errors.Annotate(err, "cannot read system info")
   112  	}
   113  
   114  	// Verify that we're destroying a system
   115  	apiEndpoint := cfgInfo.APIEndpoint()
   116  	if apiEndpoint.ServerUUID != "" && apiEndpoint.EnvironUUID != apiEndpoint.ServerUUID {
   117  		return errors.Errorf("%q is not a system; use juju environment destroy to destroy it", c.systemName)
   118  	}
   119  
   120  	if !c.assumeYes {
   121  		if err = confirmDestruction(ctx, c.systemName); err != nil {
   122  			return err
   123  		}
   124  	}
   125  
   126  	// Attempt to connect to the API.
   127  	api, err := c.getSystemAPI(cfgInfo)
   128  	switch {
   129  	case err == nil:
   130  		defer api.Close()
   131  	case errors.Cause(err) == common.ErrPerm:
   132  		return errors.Annotate(err, "cannot destroy system")
   133  	default:
   134  		if err != ErrConnTimedOut {
   135  			logger.Debugf("unable to open api: %s", err)
   136  		}
   137  		ctx.Infof("Unable to open API: %s\n", err)
   138  		api = nil
   139  	}
   140  
   141  	// Obtain bootstrap / system environ information
   142  	systemEnviron, err := c.getSystemEnviron(cfgInfo, api)
   143  	if err != nil {
   144  		return errors.Annotate(err, "cannot obtain bootstrap information")
   145  	}
   146  
   147  	// If we were unable to connect to the API, just destroy the system through
   148  	// the environs interface.
   149  	if api == nil {
   150  		return environs.Destroy(systemEnviron, store)
   151  	}
   152  
   153  	// Attempt to destroy the system with destroyEnvs and ignoreBlocks = true
   154  	err = api.DestroySystem(true, true)
   155  	if params.IsCodeNotImplemented(err) {
   156  		// Fall back to using the client endpoint to destroy the system,
   157  		// sending the info we were already able to collect.
   158  		return c.killSystemViaClient(ctx, cfgInfo, systemEnviron, store)
   159  	}
   160  
   161  	if err != nil {
   162  		ctx.Infof("Unable to destroy system through the API: %s.  Destroying through provider.", err)
   163  	}
   164  
   165  	return environs.Destroy(systemEnviron, store)
   166  }
   167  
   168  // killSystemViaClient attempts to kill the system using the client
   169  // endpoint for older juju systems which do not implement systemmanager.DestroySystem
   170  func (c *KillCommand) killSystemViaClient(ctx *cmd.Context, info configstore.EnvironInfo, systemEnviron environs.Environ, store configstore.Storage) error {
   171  	api, err := c.getClientAPI()
   172  	if err != nil {
   173  		defer api.Close()
   174  	}
   175  
   176  	if api != nil {
   177  		err = api.DestroyEnvironment()
   178  		if err != nil {
   179  			ctx.Infof("Unable to destroy system through the API: %s.  Destroying through provider.", err)
   180  		}
   181  	}
   182  
   183  	return environs.Destroy(systemEnviron, store)
   184  }