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 }