github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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 "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 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 // TODO(axw) 2013-12-12 #1260171 108 // Modify InstanceBroker.StopInstances to take 109 // a slice of IDs rather than Instances. 110 instances, err := env.Instances(ids) 111 switch err { 112 case nil: 113 default: 114 return err 115 case environs.ErrNoInstances: 116 return nil 117 case environs.ErrPartialInstances: 118 var nonNilInstances []instance.Instance 119 for i, inst := range instances { 120 if inst == nil { 121 logger.Warningf("unknown instance ID: %v", ids[i]) 122 continue 123 } 124 nonNilInstances = append(nonNilInstances, inst) 125 } 126 instances = nonNilInstances 127 } 128 return env.StopInstances(instances) 129 } 130 131 // checkManualMachines checks if any of the machines in the slice were 132 // manually provisioned, and are non-manager machines. These machines 133 // must (currently) be manually destroyed via destroy-machine before 134 // destroy-environment can successfully complete. 135 func checkManualMachines(machines []*state.Machine) error { 136 var ids []string 137 for _, m := range machines { 138 if m.IsManager() { 139 continue 140 } 141 manual, err := m.IsManual() 142 if err != nil { 143 return err 144 } 145 if manual { 146 ids = append(ids, m.Id()) 147 } 148 } 149 if len(ids) > 0 { 150 return fmt.Errorf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", strings.Join(ids, " ")) 151 } 152 return nil 153 }