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