github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/client/destroy.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package client
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/errors"
    11  
    12  	"github.com/juju/juju/environs"
    13  	"github.com/juju/juju/instance"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  // DestroyEnvironment destroys all services and non-manager machine
    18  // instances in the environment.
    19  func (c *Client) DestroyEnvironment() error {
    20  	if err := c.check.DestroyAllowed(); err != nil {
    21  		return errors.Trace(err)
    22  	}
    23  	// TODO(axw) 2013-08-30 bug 1218688
    24  	//
    25  	// There's a race here: a client might add a manual machine
    26  	// after another client checks. Destroy-environment will
    27  	// still fail, but the environment will be in a state where
    28  	// entities can only be destroyed. In the future we should
    29  	// introduce a method of preventing Environment.Destroy()
    30  	// from succeeding if machines have been added.
    31  
    32  	// First, check for manual machines. We bail out if there are any,
    33  	// to stop the user from prematurely hobbling the environment.
    34  	machines, err := c.api.state.AllMachines()
    35  	if err != nil {
    36  		return err
    37  	}
    38  	if err := checkManualMachines(machines); err != nil {
    39  		return err
    40  	}
    41  
    42  	// Set the environment to Dying, to lock out new machines and services.
    43  	// Environment.Destroy() also schedules a cleanup for existing services.
    44  	// Afterwards, refresh the machines in case any were added between the
    45  	// first check and the Environment.Destroy().
    46  	env, err := c.api.state.Environment()
    47  	if err != nil {
    48  		return err
    49  	}
    50  	if err = env.Destroy(); err != nil {
    51  		return err
    52  	}
    53  	machines, err = c.api.state.AllMachines()
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	// We must destroy instances server-side to support hosted Juju,
    59  	// as there's no CLI to fall back on. In that case, we only ever
    60  	// destroy non-state machines; we leave destroying state servers
    61  	// in non-hosted environments to the CLI, as otherwise the API
    62  	// server may get cut off.
    63  	if err := destroyInstances(c.api.state, machines); err != nil {
    64  		return err
    65  	}
    66  
    67  	// Make sure once again that there are no manually provisioned
    68  	// non-manager machines. This caters for the race between the
    69  	// first check and the Environment.Destroy().
    70  	if err := checkManualMachines(machines); err != nil {
    71  		return err
    72  	}
    73  
    74  	// Return to the caller. If it's the CLI, it will finish up
    75  	// by calling the provider's Destroy method, which will
    76  	// destroy the state servers, any straggler instances, and
    77  	// other provider-specific resources.
    78  	return nil
    79  }
    80  
    81  // destroyInstances directly destroys all non-manager,
    82  // non-manual machine instances.
    83  func destroyInstances(st *state.State, machines []*state.Machine) error {
    84  	var ids []instance.Id
    85  	for _, m := range machines {
    86  		if m.IsManager() {
    87  			continue
    88  		}
    89  		if _, isContainer := m.ParentId(); isContainer {
    90  			continue
    91  		}
    92  		manual, err := m.IsManual()
    93  		if manual {
    94  			continue
    95  		} else if err != nil {
    96  			return err
    97  		}
    98  		id, err := m.InstanceId()
    99  		if err != nil {
   100  			continue
   101  		}
   102  		ids = append(ids, id)
   103  	}
   104  	if len(ids) == 0 {
   105  		return nil
   106  	}
   107  	envcfg, err := st.EnvironConfig()
   108  	if err != nil {
   109  		return err
   110  	}
   111  	env, err := environs.New(envcfg)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	return env.StopInstances(ids...)
   116  }
   117  
   118  // checkManualMachines checks if any of the machines in the slice were
   119  // manually provisioned, and are non-manager machines. These machines
   120  // must (currently) be manually destroyed via destroy-machine before
   121  // destroy-environment can successfully complete.
   122  func checkManualMachines(machines []*state.Machine) error {
   123  	var ids []string
   124  	for _, m := range machines {
   125  		if m.IsManager() {
   126  			continue
   127  		}
   128  		manual, err := m.IsManual()
   129  		if err != nil {
   130  			return err
   131  		}
   132  		if manual {
   133  			ids = append(ids, m.Id())
   134  		}
   135  	}
   136  	if len(ids) > 0 {
   137  		return fmt.Errorf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", strings.Join(ids, " "))
   138  	}
   139  	return nil
   140  }