github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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  		if _, isContainer := m.ParentId(); isContainer {
    85  			continue
    86  		}
    87  		manual, err := m.IsManual()
    88  		if manual {
    89  			continue
    90  		} else if err != nil {
    91  			return err
    92  		}
    93  		id, err := m.InstanceId()
    94  		if err != nil {
    95  			continue
    96  		}
    97  		ids = append(ids, id)
    98  	}
    99  	if len(ids) == 0 {
   100  		return nil
   101  	}
   102  	envcfg, err := st.EnvironConfig()
   103  	if err != nil {
   104  		return err
   105  	}
   106  	env, err := environs.New(envcfg)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	return env.StopInstances(ids...)
   111  }
   112  
   113  // checkManualMachines checks if any of the machines in the slice were
   114  // manually provisioned, and are non-manager machines. These machines
   115  // must (currently) be manually destroyed via destroy-machine before
   116  // destroy-environment can successfully complete.
   117  func checkManualMachines(machines []*state.Machine) error {
   118  	var ids []string
   119  	for _, m := range machines {
   120  		if m.IsManager() {
   121  			continue
   122  		}
   123  		manual, err := m.IsManual()
   124  		if err != nil {
   125  			return err
   126  		}
   127  		if manual {
   128  			ids = append(ids, m.Id())
   129  		}
   130  	}
   131  	if len(ids) > 0 {
   132  		return fmt.Errorf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", strings.Join(ids, " "))
   133  	}
   134  	return nil
   135  }