Beispiel #1
0
// SetAPIHostPorts sets the addresses of the API server instances.
// Each server is represented by one element in the top level slice.
func (st *State) SetAPIHostPorts(netHostsPorts [][]network.HostPort) error {
	doc := apiHostPortsDoc{
		APIHostPorts: fromNetworkHostsPorts(netHostsPorts),
	}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		existing, err := st.APIHostPorts()
		if err != nil {
			return nil, err
		}
		op := txn.Op{
			C:  stateServersC,
			Id: apiHostPortsKey,
			Assert: bson.D{{
				"apihostports", fromNetworkHostsPorts(existing),
			}},
		}
		if !hostsPortsEqual(netHostsPorts, existing) {
			op.Update = bson.D{{
				"$set", bson.D{{"apihostports", doc.APIHostPorts}},
			}}
		}
		return []txn.Op{op}, nil
	}
	if err := st.run(buildTxn); err != nil {
		return errors.Annotate(err, "cannot set API addresses")
	}
	logger.Debugf("setting API hostPorts: %v", netHostsPorts)
	return nil
}
Beispiel #2
0
// SetHostedEnvironCount is an upgrade step that sets hostedEnvCountDoc.Count
// to the number of hosted environments.
func SetHostedEnvironCount(st *State) error {
	environments, closer := st.getCollection(environmentsC)
	defer closer()

	envCount, err := environments.Find(nil).Count()
	if err != nil {
		return errors.Annotate(err, "failed to read environments")
	}

	stateServers, closer := st.getCollection(stateServersC)
	defer closer()

	count, err := stateServers.FindId(hostedEnvCountKey).Count()
	if err != nil {
		return errors.Annotate(err, "failed to read state server")
	}

	hostedCount := envCount - 1 // -1 as we don't count the system environment
	op := txn.Op{
		C:  stateServersC,
		Id: hostedEnvCountKey,
	}
	if count == 0 {
		op.Assert = txn.DocMissing
		op.Insert = &hostedEnvCountDoc{hostedCount}
	} else {
		op.Update = bson.D{{"$set", bson.D{{"refcount", hostedCount}}}}
	}

	return st.runTransaction([]txn.Op{op})
}
Beispiel #3
0
// SetAPIHostPorts sets the addresses of the API server instances.
// Each server is represented by one element in the top level slice.
func (st *State) SetAPIHostPorts(netHostsPorts [][]network.HostPort) error {
	controllers, closer := st.getCollection(controllersC)
	defer closer()
	doc := apiHostPortsDoc{
		APIHostPorts: fromNetworkHostsPorts(netHostsPorts),
	}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		var existingDoc apiHostPortsDoc
		err := controllers.Find(bson.D{{"_id", apiHostPortsKey}}).One(&existingDoc)
		if err != nil {
			return nil, err
		}
		op := txn.Op{
			C:  controllersC,
			Id: apiHostPortsKey,
			Assert: bson.D{{
				"txn-revno", existingDoc.TxnRevno,
			}},
		}
		hostPorts := networkHostsPorts(existingDoc.APIHostPorts)
		if !hostsPortsEqual(netHostsPorts, hostPorts) {
			op.Update = bson.D{{
				"$set", bson.D{{"apihostports", doc.APIHostPorts}},
			}}
		} else {
			return nil, statetxn.ErrNoOperations
		}
		return []txn.Op{op}, nil
	}
	if err := st.run(buildTxn); err != nil {
		return errors.Annotate(err, "cannot set API addresses")
	}
	logger.Debugf("setting API hostPorts: %v", netHostsPorts)
	return nil
}
Beispiel #4
0
// SetAPIHostPorts sets the addresses of the API server instances.
// Each server is represented by one element in the top level slice.
func (st *State) SetAPIHostPorts(hps [][]network.HostPort) error {
	doc := apiHostPortsDoc{
		APIHostPorts: instanceHostPortsToHostPorts(hps),
	}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		existing, err := st.APIHostPorts()
		if err != nil {
			return nil, err
		}
		op := txn.Op{
			C:  stateServersC,
			Id: apiHostPortsKey,
			Assert: bson.D{{
				"apihostports", instanceHostPortsToHostPorts(existing),
			}},
		}
		if !hostPortsEqual(hps, existing) {
			op.Update = bson.D{{
				"$set", bson.D{{"apihostports", doc.APIHostPorts}},
			}}
		}
		return []txn.Op{op}, nil
	}
	if err := st.run(buildTxn); err != nil {
		return errors.Annotate(err, "cannot set API addresses")
	}
	return nil
}
Beispiel #5
0
// SaveMetadata implements Storage.SaveMetadata and behaves as save-or-update.
func (s *storage) SaveMetadata(metadata []Metadata) error {
	if len(metadata) == 0 {
		return nil
	}

	newDocs := make([]imagesMetadataDoc, len(metadata))
	for i, m := range metadata {
		newDoc := s.mongoDoc(m)
		if err := validateMetadata(&newDoc); err != nil {
			return err
		}
		newDocs[i] = newDoc
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		var ops []txn.Op
		for _, newDoc := range newDocs {
			newDocCopy := newDoc
			op := txn.Op{
				C:  s.collection,
				Id: newDocCopy.Id,
			}

			// Check if this image metadata is already known.
			existing, err := s.getMetadata(newDocCopy.Id)
			if errors.IsNotFound(err) {
				op.Assert = txn.DocMissing
				op.Insert = &newDocCopy
				ops = append(ops, op)
				logger.Debugf("inserting cloud image metadata for %v", newDocCopy.Id)
			} else if err != nil {
				return nil, errors.Trace(err)
			} else if existing.ImageId != newDocCopy.ImageId {
				// need to update imageId
				op.Assert = txn.DocExists
				op.Update = bson.D{{"$set", bson.D{{"image_id", newDocCopy.ImageId}}}}
				ops = append(ops, op)
				logger.Debugf("updating cloud image id for metadata %v", newDocCopy.Id)
			}
		}
		if len(ops) == 0 {
			return nil, jujutxn.ErrNoOperations
		}
		return ops, nil
	}

	err := s.store.RunTransaction(buildTxn)
	if err != nil {
		return errors.Annotate(err, "cannot save cloud image metadata")
	}
	return nil
}
Beispiel #6
0
// trackOp returns a txn.Op that will either insert or update the
// supplied payload, and fail if the observed precondition changes.
func (nsPayloads_) trackOp(payloads mongo.Collection, doc payloadDoc) (txn.Op, error) {
	docID := nsPayloads.docID(doc.UnitID, doc.Name)
	payloadOp := txn.Op{
		C:  payloads.Name(),
		Id: docID,
	}
	count, err := payloads.FindId(docID).Count()
	if err != nil {
		return txn.Op{}, errors.Trace(err)
	} else if count == 0 {
		payloadOp.Assert = txn.DocMissing
		payloadOp.Insert = doc
	} else {
		payloadOp.Assert = txn.DocExists
		payloadOp.Update = bson.D{{"$set", doc}}
	}
	return payloadOp, nil
}
Beispiel #7
0
// setAddresses updates the machine's addresses (either Addresses or
// MachineAddresses, depending on the field argument).
func (m *Machine) setAddresses(addresses []network.Address, field *[]address, fieldName string) error {
	var changed bool
	envConfig, err := m.st.EnvironConfig()
	if err != nil {
		return err
	}
	network.SortAddresses(addresses, envConfig.PreferIPv6())
	stateAddresses := instanceAddressesToAddresses(addresses)
	buildTxn := func(attempt int) ([]txn.Op, error) {
		changed = false
		if attempt > 0 {
			if err := m.Refresh(); err != nil {
				return nil, err
			}
		}
		if m.doc.Life == Dead {
			return nil, ErrDead
		}
		op := txn.Op{
			C:      machinesC,
			Id:     m.doc.Id,
			Assert: append(bson.D{{fieldName, *field}}, notDeadDoc...),
		}
		if !addressesEqual(addresses, addressesToInstanceAddresses(*field)) {
			op.Update = bson.D{{"$set", bson.D{{fieldName, stateAddresses}}}}
			changed = true
		}
		return []txn.Op{op}, nil
	}
	switch err := m.st.run(buildTxn); err {
	case nil:
	case jujutxn.ErrExcessiveContention:
		return errors.Annotatef(err, "cannot set %s for machine %s", fieldName, m)
	default:
		return err
	}
	if !changed {
		return nil
	}
	*field = stateAddresses
	return nil
}
Beispiel #8
0
// SetAPIHostPorts sets the addresses of the API server instances.
// Each server is represented by one element in the top level slice.
func (st *State) SetAPIHostPorts(netHostsPorts [][]network.HostPort) error {
	// Filter any addresses not on the default space, if possible.
	// All API servers need to be accessible there.
	var hpsToSet [][]network.HostPort
	for _, hps := range netHostsPorts {
		defaultSpaceHP, ok := network.SelectHostPortBySpace(hps, network.DefaultSpace)
		if !ok {
			logger.Warningf("cannot determine API addresses in space %q to use as API endpoints; using all addresses", network.DefaultSpace)
			hpsToSet = netHostsPorts
			break
		}
		hpsToSet = append(hpsToSet, []network.HostPort{defaultSpaceHP})
	}

	doc := apiHostPortsDoc{
		APIHostPorts: fromNetworkHostsPorts(hpsToSet),
	}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		existing, err := st.APIHostPorts()
		if err != nil {
			return nil, err
		}
		op := txn.Op{
			C:  controllersC,
			Id: apiHostPortsKey,
			Assert: bson.D{{
				"apihostports", fromNetworkHostsPorts(existing),
			}},
		}
		if !hostsPortsEqual(netHostsPorts, existing) {
			op.Update = bson.D{{
				"$set", bson.D{{"apihostports", doc.APIHostPorts}},
			}}
		}
		return []txn.Op{op}, nil
	}
	if err := st.run(buildTxn); err != nil {
		return errors.Annotate(err, "cannot set API addresses")
	}
	logger.Debugf("setting API hostPorts: %v", hpsToSet)
	return nil
}
Beispiel #9
0
// SaveMetadata implements Storage.SaveMetadata and behaves as save-or-update.
func (s *storage) SaveMetadata(metadata Metadata) error {
	newDoc := s.mongoDoc(metadata)

	buildTxn := func(attempt int) ([]txn.Op, error) {
		op := txn.Op{
			C:  s.collection,
			Id: newDoc.Id,
		}

		// Check if this image metadata is already known.
		existing, err := s.getMetadata(newDoc.Id)
		if err != nil {
			return nil, errors.Trace(err)
		}
		if existing.MetadataAttributes == metadata.MetadataAttributes {
			// may need to updated imageId
			if existing.ImageId != metadata.ImageId {
				op.Assert = txn.DocExists
				op.Update = bson.D{{"$set", bson.D{{"image_id", metadata.ImageId}}}}
				logger.Debugf("updating cloud image id for metadata %v", newDoc.Id)
			} else {
				return nil, jujutxn.ErrNoOperations
			}
		} else {
			op.Assert = txn.DocMissing
			op.Insert = &newDoc
			logger.Debugf("inserting cloud image metadata for %v", newDoc.Id)
		}
		return []txn.Op{op}, nil
	}

	err := s.store.RunTransaction(buildTxn)
	if err != nil {
		return errors.Annotatef(err, "cannot save metadata for cloud image %v", newDoc.ImageId)
	}
	return nil
}
Beispiel #10
0
// Add implements Storage.Add.
func (s *binaryStorage) Add(r io.Reader, metadata Metadata) (resultErr error) {
	// Add the binary file to storage.
	path := fmt.Sprintf("tools/%s-%s", metadata.Version, metadata.SHA256)
	if err := s.managedStorage.PutForBucket(s.modelUUID, path, r, metadata.Size); err != nil {
		return errors.Annotate(err, "cannot store binary file")
	}
	defer func() {
		if resultErr == nil {
			return
		}
		err := s.managedStorage.RemoveForBucket(s.modelUUID, path)
		if err != nil {
			logger.Errorf("failed to remove binary blob: %v", err)
		}
	}()

	newDoc := metadataDoc{
		Id:      metadata.Version,
		Version: metadata.Version,
		Size:    metadata.Size,
		SHA256:  metadata.SHA256,
		Path:    path,
	}

	// Add or replace metadata. If replacing, record the existing path so we
	// can remove it later.
	var oldPath string
	buildTxn := func(attempt int) ([]txn.Op, error) {
		op := txn.Op{
			C:  s.metadataCollection.Name,
			Id: newDoc.Id,
		}

		// On the first attempt we assume we're adding new binary files.
		// Subsequent attempts to add files will fetch the existing
		// doc, record the old path, and attempt to update the
		// size, path and hash fields.
		if attempt == 0 {
			op.Assert = txn.DocMissing
			op.Insert = &newDoc
		} else {
			oldDoc, err := s.findMetadata(metadata.Version)
			if err != nil {
				return nil, err
			}
			oldPath = oldDoc.Path
			op.Assert = bson.D{{"path", oldPath}}
			if oldPath != path {
				op.Update = bson.D{{
					"$set", bson.D{
						{"size", metadata.Size},
						{"sha256", metadata.SHA256},
						{"path", path},
					},
				}}
			}
		}
		return []txn.Op{op}, nil
	}
	err := s.txnRunner.Run(buildTxn)
	if err != nil {
		return errors.Annotate(err, "cannot store binary metadata")
	}

	if oldPath != "" && oldPath != path {
		// Attempt to remove the old path. Failure is non-fatal.
		err := s.managedStorage.RemoveForBucket(s.modelUUID, oldPath)
		if err != nil {
			logger.Errorf("failed to remove old binary blob: %v", err)
		} else {
			logger.Debugf("removed old binary blob")
		}
	}
	return nil
}
Beispiel #11
0
// machineStorageDecrefOp returns a txn.Op that will decrement the attachment
// count for a given machine storage entity (volume or filesystem), given its
// current attachment count and lifecycle state. If the attachment count goes
// to zero, then the entity should become Dead.
func machineStorageDecrefOp(
	collection, id string,
	attachmentCount int, life Life,
	machine names.MachineTag,
	binding string,
) txn.Op {
	op := txn.Op{
		C:  collection,
		Id: id,
	}
	if life == Dying {
		if attachmentCount == 1 {
			// This is the last attachment: the volume can be
			// marked Dead. There can be no concurrent attachments
			// since it is Dying.
			op.Assert = bson.D{
				{"life", Dying},
				{"attachmentcount", 1},
			}
			op.Update = bson.D{
				{"$inc", bson.D{{"attachmentcount", -1}}},
				{"$set", bson.D{{"life", Dead}}},
			}
		} else {
			// This is not the last attachment; just decref,
			// allowing for concurrent attachment removals but
			// ensuring we don't drop to zero without marking
			// the volume Dead.
			op.Assert = bson.D{
				{"life", Dying},
				{"attachmentcount", bson.D{{"$gt", 1}}},
			}
			op.Update = bson.D{
				{"$inc", bson.D{{"attachmentcount", -1}}},
			}
		}
	} else {
		// The volume is still Alive: decref, retrying if the
		// volume is destroyed concurrently or the binding changes.
		// If the volume is bound to the machine, advance it to
		// Dead; binding storage to a machine and attaching the
		// storage to multiple machines will be mutually exclusive.
		//
		// Otherwise, when DestroyVolume is called, the volume will
		// be marked Dead if it has no attachments.
		update := bson.D{
			{"$inc", bson.D{{"attachmentcount", -1}}},
		}
		if binding == machine.String() {
			update = append(update, bson.DocElem{
				"$set", bson.D{{"life", Dead}},
			})
		}
		op.Assert = bson.D{
			{"life", Alive},
			{"binding", binding},
			{"attachmentcount", bson.D{{"$gt", 0}}},
		}
		op.Update = update
	}
	return op
}
Beispiel #12
0
// AddImage is defined on the Storage interface.
func (s *imageStorage) AddImage(r io.Reader, metadata *Metadata) (resultErr error) {
	session := s.blobDb.Session.Copy()
	defer session.Close()
	managedStorage := s.getManagedStorage(session)
	path := imagePath(metadata.Kind, metadata.Series, metadata.Arch, metadata.SHA256)
	if err := managedStorage.PutForEnvironment(s.envUUID, path, r, metadata.Size); err != nil {
		return errors.Annotate(err, "cannot store image")
	}
	defer func() {
		if resultErr == nil {
			return
		}
		err := managedStorage.RemoveForEnvironment(s.envUUID, path)
		if err != nil {
			logger.Errorf("failed to remove image blob: %v", err)
		}
	}()

	newDoc := imageMetadataDoc{
		Id:        docId(metadata),
		EnvUUID:   s.envUUID,
		Kind:      metadata.Kind,
		Series:    metadata.Series,
		Arch:      metadata.Arch,
		Size:      metadata.Size,
		SHA256:    metadata.SHA256,
		SourceURL: metadata.SourceURL,
		Path:      path,
		Created:   time.Now(),
	}

	// Add or replace metadata. If replacing, record the
	// existing path so we can remove the blob later.
	var oldPath string
	buildTxn := func(attempt int) ([]txn.Op, error) {
		op := txn.Op{
			C:  imagemetadataC,
			Id: newDoc.Id,
		}

		// On the first attempt we assume we're adding a new image blob.
		// Subsequent attempts to add image will fetch the existing
		// doc, record the old path, and attempt to update the
		// size, path and hash fields.
		if attempt == 0 {
			op.Assert = txn.DocMissing
			op.Insert = &newDoc
		} else {
			oldDoc, err := s.imageMetadataDoc(metadata.EnvUUID, metadata.Kind, metadata.Series, metadata.Arch)
			if err != nil {
				return nil, err
			}
			oldPath = oldDoc.Path
			op.Assert = bson.D{{"path", oldPath}}
			if oldPath != path {
				op.Update = bson.D{{
					"$set", bson.D{
						{"size", metadata.Size},
						{"sha256", metadata.SHA256},
						{"path", path},
					},
				}}
			}
		}
		return []txn.Op{op}, nil
	}
	txnRunner := s.txnRunner(session)
	err := txnRunner.Run(buildTxn)
	if err != nil {
		return errors.Annotate(err, "cannot store image metadata")
	}

	if oldPath != "" && oldPath != path {
		// Attempt to remove the old path. Failure is non-fatal.
		err := managedStorage.RemoveForEnvironment(s.envUUID, oldPath)
		if err != nil {
			logger.Errorf("failed to remove old image blob: %v", err)
		} else {
			logger.Debugf("removed old image blob")
		}
	}
	return nil
}