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 }