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