github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/upgrades.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/juju/status" 11 "github.com/juju/loggo" 12 "github.com/juju/names" 13 "gopkg.in/mgo.v2/bson" 14 "gopkg.in/mgo.v2/txn" 15 ) 16 17 var upgradesLogger = loggo.GetLogger("juju.state.upgrade") 18 19 func AddPreferredAddressesToMachines(st *State) error { 20 machines, err := st.AllMachines() 21 if err != nil { 22 return errors.Trace(err) 23 } 24 25 for _, machine := range machines { 26 if machine.Life() == Dead { 27 continue 28 } 29 // Setting the addresses is enough to trigger setting the preferred 30 // addresses. 31 err = machine.SetMachineAddresses(machine.MachineAddresses()...) 32 if err != nil { 33 return errors.Trace(err) 34 } 35 err := machine.SetProviderAddresses(machine.ProviderAddresses()...) 36 if err != nil { 37 return errors.Trace(err) 38 } 39 } 40 return nil 41 } 42 43 // runForAllEnvStates will run runner function for every env passing a state 44 // for that env. 45 func runForAllEnvStates(st *State, runner func(st *State) error) error { 46 environments, closer := st.getCollection(modelsC) 47 defer closer() 48 49 var envDocs []bson.M 50 err := environments.Find(nil).Select(bson.M{"_id": 1}).All(&envDocs) 51 if err != nil { 52 return errors.Annotate(err, "failed to read models") 53 } 54 55 for _, envDoc := range envDocs { 56 modelUUID := envDoc["_id"].(string) 57 envSt, err := st.ForModel(names.NewModelTag(modelUUID)) 58 if err != nil { 59 return errors.Annotatef(err, "failed to open model %q", modelUUID) 60 } 61 defer envSt.Close() 62 if err := runner(envSt); err != nil { 63 return errors.Annotatef(err, "model UUID %q", modelUUID) 64 } 65 } 66 return nil 67 } 68 69 // AddFilesystemStatus ensures each filesystem has a status doc. 70 func AddFilesystemStatus(st *State) error { 71 return runForAllEnvStates(st, func(st *State) error { 72 filesystems, err := st.AllFilesystems() 73 if err != nil { 74 return errors.Trace(err) 75 } 76 var ops []txn.Op 77 for _, filesystem := range filesystems { 78 _, err := filesystem.Status() 79 if err == nil { 80 continue 81 } 82 if !errors.IsNotFound(err) { 83 return errors.Annotate(err, "getting status") 84 } 85 status, err := upgradingFilesystemStatus(st, filesystem) 86 if err != nil { 87 return errors.Annotate(err, "deciding filesystem status") 88 } 89 ops = append(ops, createStatusOp(st, filesystem.globalKey(), statusDoc{ 90 Status: status, 91 Updated: time.Now().UnixNano(), 92 })) 93 } 94 if len(ops) > 0 { 95 return errors.Trace(st.runTransaction(ops)) 96 } 97 return nil 98 }) 99 } 100 101 // If the filesystem has not been provisioned, then it should be Pending; 102 // if it has been provisioned, but there is an unprovisioned attachment, then 103 // it should be Attaching; otherwise it is Attached. 104 func upgradingFilesystemStatus(st *State, filesystem Filesystem) (status.Status, error) { 105 if _, err := filesystem.Info(); errors.IsNotProvisioned(err) { 106 return status.StatusPending, nil 107 } 108 attachments, err := st.FilesystemAttachments(filesystem.FilesystemTag()) 109 if err != nil { 110 return "", errors.Trace(err) 111 } 112 for _, attachment := range attachments { 113 _, err := attachment.Info() 114 if errors.IsNotProvisioned(err) { 115 return status.StatusAttaching, nil 116 } 117 } 118 return status.StatusAttached, nil 119 } 120 121 // MigrateSettingsSchema migrates the schema of the settings collection, 122 // moving non-reserved keys at the top-level into a subdoc, and introducing 123 // a top-level "version" field with the initial value matching txn-revno. 124 // 125 // This migration takes place both before and after model-uuid migration, 126 // to get the correct txn-revno value. 127 func MigrateSettingsSchema(st *State) error { 128 coll, closer := st.getRawCollection(settingsC) 129 defer closer() 130 131 upgradesLogger.Debugf("migrating schema of the %s collection", settingsC) 132 iter := coll.Find(nil).Iter() 133 defer iter.Close() 134 135 var ops []txn.Op 136 var doc bson.M 137 for iter.Next(&doc) { 138 if !settingsDocNeedsMigration(doc) { 139 continue 140 } 141 142 id := doc["_id"] 143 txnRevno := doc["txn-revno"].(int64) 144 145 // Remove reserved attributes; we'll move the remaining 146 // ones to the "settings" subdoc. 147 delete(doc, "model-uuid") 148 delete(doc, "_id") 149 delete(doc, "txn-revno") 150 delete(doc, "txn-queue") 151 152 // If there exists a setting by the name "settings", 153 // we must remove it first, or it will collide with 154 // the dotted-notation $sets. 155 if _, ok := doc["settings"]; ok { 156 ops = append(ops, txn.Op{ 157 C: settingsC, 158 Id: id, 159 Assert: txn.DocExists, 160 Update: bson.D{{"$unset", bson.D{{"settings", 1}}}}, 161 }) 162 } 163 164 var update bson.D 165 for key, value := range doc { 166 if key != "settings" && key != "version" { 167 // Don't try to unset these fields, 168 // as we've unset "settings" above 169 // already, and we'll overwrite 170 // "version" below. 171 update = append(update, bson.DocElem{ 172 "$unset", bson.D{{key, 1}}, 173 }) 174 } 175 update = append(update, bson.DocElem{ 176 "$set", bson.D{{"settings." + key, value}}, 177 }) 178 } 179 if len(update) == 0 { 180 // If there are no settings, then we need 181 // to add an empty "settings" map so we 182 // can tell for next time that migration 183 // is complete, and don't move the "version" 184 // field we add. 185 update = bson.D{{ 186 "$set", bson.D{{"settings", bson.M{}}}, 187 }} 188 } 189 update = append(update, bson.DocElem{ 190 "$set", bson.D{{"version", txnRevno}}, 191 }) 192 193 ops = append(ops, txn.Op{ 194 C: settingsC, 195 Id: id, 196 Assert: txn.DocExists, 197 Update: update, 198 }) 199 } 200 if err := iter.Err(); err != nil { 201 return errors.Trace(err) 202 } 203 return st.runRawTransaction(ops) 204 } 205 206 func settingsDocNeedsMigration(doc bson.M) bool { 207 // It is not possible for there to exist a settings value 208 // with type bson.M, so we know that it is the new settings 209 // field and not just a setting with the name "settings". 210 if _, ok := doc["settings"].(bson.M); ok { 211 return false 212 } 213 return true 214 } 215 216 func addDefaultBindingsToServices(st *State) error { 217 services, err := st.AllServices() 218 if err != nil { 219 return errors.Trace(err) 220 } 221 222 upgradesLogger.Debugf("adding default endpoint bindings to services (where missing)") 223 ops := make([]txn.Op, 0, len(services)) 224 for _, service := range services { 225 ch, _, err := service.Charm() 226 if err != nil { 227 return errors.Annotatef(err, "cannot get charm for service %q", service.Name()) 228 } 229 if _, err := service.EndpointBindings(); err == nil { 230 upgradesLogger.Debugf("service %q already has bindings (skipping)", service.Name()) 231 continue 232 } else if !errors.IsNotFound(err) { 233 return errors.Annotatef(err, "checking service %q for existing bindings", service.Name()) 234 } 235 // Passing nil for the bindings map will use the defaults. 236 createOp, err := createEndpointBindingsOp(st, service.globalKey(), nil, ch.Meta()) 237 if err != nil { 238 return errors.Annotatef(err, "setting default endpoint bindings for service %q", service.Name()) 239 } 240 ops = append(ops, createOp) 241 } 242 return st.runTransaction(ops) 243 } 244 245 // AddDefaultEndpointBindingsToServices adds default endpoint bindings for each 246 // service. As long as the service has a charm URL set, each charm endpoint will 247 // be bound to the default space. 248 func AddDefaultEndpointBindingsToServices(st *State) error { 249 return runForAllEnvStates(st, addDefaultBindingsToServices) 250 }