~ubuntu-branches/ubuntu/trusty/juju-core/trusty

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/state/state.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-03-24 16:05:44 UTC
  • mfrom: (1.1.20)
  • Revision ID: package-import@ubuntu.com-20140324160544-g8lsfufby18d5fj4
Tags: 1.17.6-0ubuntu1
* New upstream point release, including fixes for:
  - br0 not bought up by cloud-init with MAAS provider (LP: #1271144).
  - ppc64el enablement for juju/lxc (LP: #1273769).
  - juju userdata should not restart networking (LP: #1248283).
  - error detecting hardware characteristics (LP: #1276909).
  - juju instances not including the default security group (LP: #1129720).
  - juju bootstrap does not honor https_proxy (LP: #1240260).
* d/control,rules: Drop BD on bash-completion, install bash-completion
  direct from upstream source code.
* d/rules: Set HOME prior to generating man pages.
* d/control: Drop alternative dependency on mongodb-server; juju now only
  works on trusty with juju-mongodb.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
        "strings"
16
16
        "sync"
17
17
 
18
 
        "github.com/loggo/loggo"
 
18
        "github.com/juju/loggo"
19
19
        "labix.org/v2/mgo"
20
20
        "labix.org/v2/mgo/bson"
21
21
        "labix.org/v2/mgo/txn"
34
34
 
35
35
var logger = loggo.GetLogger("juju.state")
36
36
 
37
 
// TODO(niemeyer): This must not be exported.
38
 
type D []bson.DocElem
39
 
 
40
37
// BootstrapNonce is used as a nonce for the state server machine.
41
 
const BootstrapNonce = "user-admin:bootstrap"
 
38
const (
 
39
        BootstrapNonce = "user-admin:bootstrap"
 
40
        AdminUser      = "admin"
 
41
)
42
42
 
43
43
// State represents the state of an environment
44
44
// managed by juju.
184
184
        matchCurrent := "^" + regexp.QuoteMeta(currentVersion) + "-"
185
185
        matchNew := "^" + regexp.QuoteMeta(newVersion) + "-"
186
186
        // Get all machines and units with a different or empty version.
187
 
        sel := D{{"$or", []D{
188
 
                {{"tools", D{{"$exists", false}}}},
189
 
                {{"$and", []D{
190
 
                        {{"tools.version", D{{"$not", bson.RegEx{matchCurrent, ""}}}}},
191
 
                        {{"tools.version", D{{"$not", bson.RegEx{matchNew, ""}}}}},
 
187
        sel := bson.D{{"$or", []bson.D{
 
188
                {{"tools", bson.D{{"$exists", false}}}},
 
189
                {{"$and", []bson.D{
 
190
                        {{"tools.version", bson.D{{"$not", bson.RegEx{matchCurrent, ""}}}}},
 
191
                        {{"tools.version", bson.D{{"$not", bson.RegEx{matchNew, ""}}}}},
192
192
                }}},
193
193
        }}}
194
194
        var agentTags []string
196
196
                var doc struct {
197
197
                        Id string `bson:"_id"`
198
198
                }
199
 
                iter := collection.Find(sel).Select(D{{"_id", 1}}).Iter()
 
199
                iter := collection.Find(sel).Select(bson.D{{"_id", 1}}).Iter()
200
200
                for iter.Next(&doc) {
201
201
                        switch collection.Name {
202
202
                        case "machines":
244
244
                ops := []txn.Op{{
245
245
                        C:      st.settings.Name,
246
246
                        Id:     environGlobalKey,
247
 
                        Assert: D{{"txn-revno", settings.txnRevno}},
248
 
                        Update: D{{"$set", D{{"agent-version", newVersion.String()}}}},
 
247
                        Assert: bson.D{{"txn-revno", settings.txnRevno}},
 
248
                        Update: bson.D{{"$set", bson.D{{"agent-version", newVersion.String()}}}},
249
249
                }}
250
250
                if err := st.runTransaction(ops); err == nil {
251
251
                        return nil
256
256
        return ErrExcessiveContention
257
257
}
258
258
 
259
 
// SetEnvironConfig replaces the current configuration of the
260
 
// environment with the provided configuration.
261
 
func (st *State) SetEnvironConfig(cfg, old *config.Config) error {
262
 
        if err := checkEnvironConfig(cfg); err != nil {
263
 
                return err
264
 
        }
 
259
func (st *State) buildAndValidateEnvironConfig(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) (validCfg *config.Config, err error) {
 
260
        newConfig, err := oldConfig.Apply(updateAttrs)
 
261
        if err != nil {
 
262
                return nil, err
 
263
        }
 
264
        if len(removeAttrs) != 0 {
 
265
                newConfig, err = newConfig.Remove(removeAttrs)
 
266
                if err != nil {
 
267
                        return nil, err
 
268
                }
 
269
        }
 
270
        if err := checkEnvironConfig(newConfig); err != nil {
 
271
                return nil, err
 
272
        }
 
273
        return st.validate(newConfig, oldConfig)
 
274
}
 
275
 
 
276
type ValidateConfigFunc func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error
 
277
 
 
278
// UpateEnvironConfig adds, updates or removes attributes in the current
 
279
// configuration of the environment with the provided updateAttrs and
 
280
// removeAttrs.
 
281
func (st *State) UpdateEnvironConfig(updateAttrs map[string]interface{}, removeAttrs []string, additionalValidation ValidateConfigFunc) error {
 
282
        if len(updateAttrs)+len(removeAttrs) == 0 {
 
283
                return nil
 
284
        }
 
285
 
265
286
        // TODO(axw) 2013-12-6 #1167616
266
287
        // Ensure that the settings on disk have not changed
267
288
        // underneath us. The settings changes are actually
272
293
        if err != nil {
273
294
                return err
274
295
        }
275
 
        newattrs := cfg.AllAttrs()
276
 
        for k, _ := range old.AllAttrs() {
277
 
                if _, ok := newattrs[k]; !ok {
 
296
 
 
297
        // Get the existing environment config from state.
 
298
        oldConfig, err := config.New(config.NoDefaults, settings.Map())
 
299
        if err != nil {
 
300
                return err
 
301
        }
 
302
        if additionalValidation != nil {
 
303
                err = additionalValidation(updateAttrs, removeAttrs, oldConfig)
 
304
                if err != nil {
 
305
                        return err
 
306
                }
 
307
        }
 
308
        validCfg, err := st.buildAndValidateEnvironConfig(updateAttrs, removeAttrs, oldConfig)
 
309
        if err != nil {
 
310
                return err
 
311
        }
 
312
 
 
313
        validAttrs := validCfg.AllAttrs()
 
314
        for k, _ := range oldConfig.AllAttrs() {
 
315
                if _, ok := validAttrs[k]; !ok {
278
316
                        settings.Delete(k)
279
317
                }
280
318
        }
281
 
        settings.Update(newattrs)
 
319
        settings.Update(validAttrs)
282
320
        _, err = settings.Write()
283
321
        return err
284
322
}
371
409
// Machine returns the machine with the given id.
372
410
func (st *State) Machine(id string) (*Machine, error) {
373
411
        mdoc := &machineDoc{}
374
 
        sel := D{{"_id", id}}
 
412
        sel := bson.D{{"_id", id}}
375
413
        err := st.machines.Find(sel).One(mdoc)
376
414
        if err == mgo.ErrNotFound {
377
415
                return nil, errors.NotFoundf("machine %s", id)
462
500
        // check for that situation and update the existing charm record
463
501
        // if necessary, otherwise add a new record.
464
502
        var existing charmDoc
465
 
        err = st.charms.Find(D{{"_id", curl.String()}, {"placeholder", true}}).One(&existing)
 
503
        err = st.charms.Find(bson.D{{"_id", curl.String()}, {"placeholder", true}}).One(&existing)
466
504
        if err == mgo.ErrNotFound {
467
505
                cdoc := &charmDoc{
468
506
                        URL:          curl,
486
524
// to storage and placeholders are never returned.
487
525
func (st *State) Charm(curl *charm.URL) (*Charm, error) {
488
526
        cdoc := &charmDoc{}
489
 
        what := D{
 
527
        what := bson.D{
490
528
                {"_id", curl},
491
 
                {"placeholder", D{{"$ne", true}}},
492
 
                {"pendingupload", D{{"$ne", true}}},
 
529
                {"placeholder", bson.D{{"$ne", true}}},
 
530
                {"pendingupload", bson.D{{"$ne", true}}},
493
531
        }
494
532
        err := st.charms.Find(what).One(&cdoc)
495
533
        if err == mgo.ErrNotFound {
510
548
        noRevURL := curl.WithRevision(-1)
511
549
        curlRegex := "^" + regexp.QuoteMeta(noRevURL.String())
512
550
        var docs []charmDoc
513
 
        err := st.charms.Find(D{{"_id", D{{"$regex", curlRegex}}}, {"placeholder", true}}).All(&docs)
 
551
        err := st.charms.Find(bson.D{{"_id", bson.D{{"$regex", curlRegex}}}, {"placeholder", true}}).All(&docs)
514
552
        if err != nil {
515
553
                return nil, fmt.Errorf("cannot get charm %q: %v", curl, err)
516
554
        }
548
586
        for attempt := 0; attempt < 3; attempt++ {
549
587
                // Find the highest revision of that charm in state.
550
588
                var docs []charmDoc
551
 
                err = st.charms.Find(D{{"_id", D{{"$regex", curlRegex}}}}).Select(D{{"_id", 1}}).All(&docs)
 
589
                err = st.charms.Find(bson.D{{"_id", bson.D{{"$regex", curlRegex}}}}).Select(bson.D{{"_id", 1}}).All(&docs)
552
590
                if err != nil {
553
591
                        return nil, err
554
592
                }
641
679
                        ops = []txn.Op{{
642
680
                                C:  st.charms.Name,
643
681
                                Id: curl,
644
 
                                Assert: D{
 
682
                                Assert: bson.D{
645
683
                                        {"bundlesha256", ""},
646
684
                                        {"pendingupload", false},
647
685
                                        {"placeholder", true},
648
686
                                },
649
 
                                Update: D{{"$set", D{
 
687
                                Update: bson.D{{"$set", bson.D{
650
688
                                        {"pendingupload", true},
651
689
                                        {"placeholder", false},
652
690
                                }}},
678
716
}
679
717
 
680
718
var (
681
 
        stillPending     = D{{"pendingupload", true}}
682
 
        stillPlaceholder = D{{"placeholder", true}}
 
719
        stillPending     = bson.D{{"pendingupload", true}}
 
720
        stillPlaceholder = bson.D{{"placeholder", true}}
683
721
)
684
722
 
685
723
// AddStoreCharmPlaceholder creates a charm document in state for the given charm URL which
697
735
        for attempt := 0; attempt < 3; attempt++ {
698
736
                // See if the charm already exists in state and exit early if that's the case.
699
737
                var doc charmDoc
700
 
                err = st.charms.Find(D{{"_id", curl.String()}}).Select(D{{"_id", 1}}).One(&doc)
 
738
                err = st.charms.Find(bson.D{{"_id", curl.String()}}).Select(bson.D{{"_id", 1}}).One(&doc)
701
739
                if err != nil && err != mgo.ErrNotFound {
702
740
                        return err
703
741
                }
745
783
        curlRegex := "^" + regexp.QuoteMeta(noRevURL.String())
746
784
        var docs []charmDoc
747
785
        err := st.charms.Find(
748
 
                D{{"_id", D{{"$regex", curlRegex}}}, {"placeholder", true}}).Select(D{{"_id", 1}}).All(&docs)
 
786
                bson.D{{"_id", bson.D{{"$regex", curlRegex}}}, {"placeholder", true}}).Select(bson.D{{"_id", 1}}).All(&docs)
749
787
        if err != nil {
750
788
                return nil, err
751
789
        }
815
853
func (st *State) updateCharmDoc(
816
854
        ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bundleSha256 string, preReq interface{}) (*Charm, error) {
817
855
 
818
 
        updateFields := D{{"$set", D{
 
856
        updateFields := bson.D{{"$set", bson.D{
819
857
                {"meta", ch.Meta()},
820
858
                {"config", ch.Config()},
821
859
                {"bundleurl", bundleURL},
968
1006
                return nil, fmt.Errorf("%q is not a valid service name", name)
969
1007
        }
970
1008
        sdoc := &serviceDoc{}
971
 
        sel := D{{"_id", name}}
 
1009
        sel := bson.D{{"_id", name}}
972
1010
        err = st.services.Find(sel).One(sdoc)
973
1011
        if err == mgo.ErrNotFound {
974
1012
                return nil, errors.NotFoundf("service %q", name)
982
1020
// AllServices returns all deployed services in the environment.
983
1021
func (st *State) AllServices() (services []*Service, err error) {
984
1022
        sdocs := []serviceDoc{}
985
 
        err = st.services.Find(D{}).All(&sdocs)
 
1023
        err = st.services.Find(bson.D{}).All(&sdocs)
986
1024
        if err != nil {
987
1025
                return nil, fmt.Errorf("cannot get all services")
988
1026
        }
1164
1202
                        ops = append(ops, txn.Op{
1165
1203
                                C:      st.services.Name,
1166
1204
                                Id:     ep.ServiceName,
1167
 
                                Assert: D{{"life", Alive}, {"charmurl", ch.URL()}},
1168
 
                                Update: D{{"$inc", D{{"relationcount", 1}}}},
 
1205
                                Assert: bson.D{{"life", Alive}, {"charmurl", ch.URL()}},
 
1206
                                Update: bson.D{{"$inc", bson.D{{"relationcount", 1}}}},
1169
1207
                        })
1170
1208
                }
1171
1209
                if matchSeries && len(series) != 1 {
1211
1249
// be derived unambiguously from the relation's endpoints).
1212
1250
func (st *State) KeyRelation(key string) (*Relation, error) {
1213
1251
        doc := relationDoc{}
1214
 
        err := st.relations.Find(D{{"_id", key}}).One(&doc)
 
1252
        err := st.relations.Find(bson.D{{"_id", key}}).One(&doc)
1215
1253
        if err == mgo.ErrNotFound {
1216
1254
                return nil, errors.NotFoundf("relation %q", key)
1217
1255
        }
1224
1262
// Relation returns the existing relation with the given id.
1225
1263
func (st *State) Relation(id int) (*Relation, error) {
1226
1264
        doc := relationDoc{}
1227
 
        err := st.relations.Find(D{{"id", id}}).One(&doc)
 
1265
        err := st.relations.Find(bson.D{{"id", id}}).One(&doc)
1228
1266
        if err == mgo.ErrNotFound {
1229
1267
                return nil, errors.NotFoundf("relation %d", id)
1230
1268
        }
1294
1332
// all subsequent attempts to access the state must
1295
1333
// be authorized; otherwise no authorization is required.
1296
1334
func (st *State) SetAdminMongoPassword(password string) error {
1297
 
        admin := st.db.Session.DB("admin")
 
1335
        admin := st.db.Session.DB(AdminUser)
1298
1336
        if password != "" {
1299
1337
                // On 2.2+, we get a "need to login" error without a code when
1300
1338
                // adding the first user because we go from no-auth+no-login to
1301
1339
                // auth+no-login. Not great. Hopefully being fixed in 2.4.
1302
 
                if err := admin.AddUser("admin", password, false); err != nil && err.Error() != "need to login" {
 
1340
                if err := admin.AddUser(AdminUser, password, false); err != nil && err.Error() != "need to login" {
1303
1341
                        return fmt.Errorf("cannot set admin password: %v", err)
1304
1342
                }
1305
 
                if err := admin.Login("admin", password); err != nil {
 
1343
                if err := admin.Login(AdminUser, password); err != nil {
1306
1344
                        return fmt.Errorf("cannot login after setting password: %v", err)
1307
1345
                }
1308
1346
        } else {
1309
 
                if err := admin.RemoveUser("admin"); err != nil && err != mgo.ErrNotFound {
 
1347
                if err := admin.RemoveUser(AdminUser); err != nil && err != mgo.ErrNotFound {
1310
1348
                        return fmt.Errorf("cannot disable admin password: %v", err)
1311
1349
                }
1312
1350
        }
1347
1385
// the currently configured state server machines.
1348
1386
func (st *State) StateServerInfo() (*StateServerInfo, error) {
1349
1387
        var doc stateServersDoc
1350
 
        err := st.stateServers.Find(D{{"_id", environGlobalKey}}).One(&doc)
 
1388
        err := st.stateServers.Find(bson.D{{"_id", environGlobalKey}}).One(&doc)
1351
1389
        if err != nil {
1352
1390
                return nil, fmt.Errorf("cannot get state servers document: %v", err)
1353
1391
        }