launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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 "strings" 8 9 "launchpad.net/errgo/errors" 10 "launchpad.net/juju-core/environs" 11 "launchpad.net/juju-core/instance" 12 "launchpad.net/juju-core/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 mask(err) 32 } 33 if err := checkManualMachines(machines); err != nil { 34 return mask(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 mask(err) 44 } 45 if err = env.Destroy(); err != nil { 46 return mask(err) 47 } 48 machines, err = c.api.state.AllMachines() 49 if err != nil { 50 return mask(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 mask(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 mask(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 mask(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 mask(err) 102 } 103 env, err := environs.New(envcfg) 104 if err != nil { 105 return mask(err) 106 } 107 108 // TODO(axw) 2013-12-12 #1260171 109 // Modify InstanceBroker.StopInstances to take 110 // a slice of IDs rather than Instances. 111 instances, err := env.Instances(ids) 112 switch err { 113 case nil: 114 default: 115 return err 116 case environs.ErrNoInstances: 117 return nil 118 case environs.ErrPartialInstances: 119 var nonNilInstances []instance.Instance 120 for i, inst := range instances { 121 if inst == nil { 122 logger.Warningf("unknown instance ID: %v", ids[i]) 123 continue 124 } 125 nonNilInstances = append(nonNilInstances, inst) 126 } 127 instances = nonNilInstances 128 } 129 return env.StopInstances(instances) 130 } 131 132 // checkManualMachines checks if any of the machines in the slice were 133 // manually provisioned, and are non-manager machines. These machines 134 // must (currently) be manually destroyed via destroy-machine before 135 // destroy-environment can successfully complete. 136 func checkManualMachines(machines []*state.Machine) error { 137 var ids []string 138 for _, m := range machines { 139 if m.IsManager() { 140 continue 141 } 142 manual, err := m.IsManual() 143 if err != nil { 144 return mask(err) 145 } 146 if manual { 147 ids = append(ids, m.Id()) 148 } 149 } 150 if len(ids) > 0 { 151 return errors.Newf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", strings.Join(ids, " ")) 152 } 153 return nil 154 }