Ejemplo n.º 1
0
// Destroy ensures that the service and all its relations will be removed at
// some point; if the service has no units, and no relation involving the
// service has any units in scope, they are all removed immediately.
func (s *Service) Destroy() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot destroy service %q", s)
	defer func() {
		if err == nil {
			// This is a white lie; the document might actually be removed.
			s.doc.Life = Dying
		}
	}()
	svc := &Service{st: s.st, doc: s.doc}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		if attempt > 0 {
			if err := svc.Refresh(); errors.IsNotFound(err) {
				return nil, jujutxn.ErrNoOperations
			} else if err != nil {
				return nil, err
			}
		}
		switch ops, err := svc.destroyOps(); err {
		case errRefresh:
		case errAlreadyDying:
			return nil, jujutxn.ErrNoOperations
		case nil:
			return ops, nil
		default:
			return nil, err
		}
		return nil, jujutxn.ErrTransientFailure
	}
	return s.st.run(buildTxn)
}
Ejemplo n.º 2
0
// validate returns an error if the state violates expectations.
func (st State) validate() (err error) {
	defer errors.DeferredAnnotatef(&err, "invalid operation state")
	hasHook := st.Hook != nil
	hasActionId := st.ActionId != nil
	hasCharm := st.CharmURL != nil
	switch st.Kind {
	case Install:
		if st.Installed {
			return errors.New("unexpected hook info with Kind Install")
		}
		fallthrough
	case Upgrade:
		switch {
		case !hasCharm:
			return errors.New("missing charm URL")
		case hasActionId:
			return errors.New("unexpected action id")
		}
	case RunAction:
		switch {
		case !hasActionId:
			return errors.New("missing action id")
		case hasCharm:
			return errors.New("unexpected charm URL")
		}
	case RunHook:
		switch {
		case !hasHook:
			return errors.New("missing hook info with Kind RunHook")
		case hasCharm:
			return errors.New("unexpected charm URL")
		case hasActionId:
			return errors.New("unexpected action id")
		}
	case Continue:
		// TODO(jw4) LP-1438489
		// ModeContinue should no longer have a Hook, but until the upgrade is
		// fixed we can't fail the validation if it does.
		if hasHook {
			logger.Errorf("unexpected hook info with Kind Continue")
		}
		switch {
		case hasCharm:
			return errors.New("unexpected charm URL")
		case hasActionId:
			return errors.New("unexpected action id")
		}
	default:
		return errors.Errorf("unknown operation %q", st.Kind)
	}
	switch st.Step {
	case Queued, Pending, Done:
	default:
		return errors.Errorf("unknown operation step %q", st.Step)
	}
	if hasHook {
		return st.Hook.Validate()
	}
	return nil
}
Ejemplo n.º 3
0
// Write atomically writes to disk the relation state change in hi.
// It must be called after the respective hook was executed successfully.
// Write doesn't validate hi but guarantees that successive writes of
// the same hi are idempotent.
func (d *StateDir) Write(hi hook.Info) (err error) {
	defer errors.DeferredAnnotatef(&err, "failed to write %q hook info for %q on state directory", hi.Kind, hi.RemoteUnit)
	if hi.Kind == hooks.RelationBroken {
		return d.Remove()
	}
	name := strings.Replace(hi.RemoteUnit, "/", "-", 1)
	path := filepath.Join(d.path, name)
	if hi.Kind == hooks.RelationDeparted {
		if err = os.Remove(path); err != nil && !os.IsNotExist(err) {
			return err
		}
		// If atomic delete succeeded, update own state.
		delete(d.state.Members, hi.RemoteUnit)
		return nil
	}
	di := diskInfo{&hi.ChangeVersion, hi.Kind == hooks.RelationJoined}
	if err := utils.WriteYaml(path, &di); err != nil {
		return err
	}
	// If write was successful, update own state.
	d.state.Members[hi.RemoteUnit] = hi.ChangeVersion
	if hi.Kind == hooks.RelationJoined {
		d.state.ChangedPending = hi.RemoteUnit
	} else {
		d.state.ChangedPending = ""
	}
	return nil
}
Ejemplo n.º 4
0
// Validate returns an error if the supplied hook.Info does not represent
// a valid change to the relation state. Hooks must always be validated
// against the current state before they are run, to ensure that the system
// meets its guarantees about hook execution order.
func (s *State) Validate(hi hook.Info) (err error) {
	defer errors.DeferredAnnotatef(&err, "inappropriate %q for %q", hi.Kind, hi.RemoteUnit)
	if hi.RelationId != s.RelationId {
		return fmt.Errorf("expected relation %d, got relation %d", s.RelationId, hi.RelationId)
	}
	if s.Members == nil {
		return fmt.Errorf(`relation is broken and cannot be changed further`)
	}
	unit, kind := hi.RemoteUnit, hi.Kind
	if kind == hooks.RelationBroken {
		if len(s.Members) == 0 {
			return nil
		}
		return fmt.Errorf(`cannot run "relation-broken" while units still present`)
	}
	if s.ChangedPending != "" {
		if unit != s.ChangedPending || kind != hooks.RelationChanged {
			return fmt.Errorf(`expected "relation-changed" for %q`, s.ChangedPending)
		}
	} else if _, joined := s.Members[unit]; joined && kind == hooks.RelationJoined {
		return fmt.Errorf("unit already joined")
	} else if !joined && kind != hooks.RelationJoined {
		return fmt.Errorf("unit has not joined")
	}
	return nil
}
Ejemplo n.º 5
0
// AddMachines adds new machines configured according to the
// given templates.
func (st *State) AddMachines(templates ...MachineTemplate) (_ []*Machine, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot add a new machine")
	var ms []*Machine
	var ops []txn.Op
	var mdocs []*machineDoc
	for _, template := range templates {
		mdoc, addOps, err := st.addMachineOps(template)
		if err != nil {
			return nil, errors.Trace(err)
		}
		mdocs = append(mdocs, mdoc)
		ms = append(ms, newMachine(st, mdoc))
		ops = append(ops, addOps...)
	}
	ssOps, err := st.maintainControllersOps(mdocs, nil)
	if err != nil {
		return nil, errors.Trace(err)
	}
	ops = append(ops, ssOps...)
	ops = append(ops, assertModelActiveOp(st.ModelUUID()))
	if err := st.runTransaction(ops); err != nil {
		if errors.Cause(err) == txn.ErrAborted {
			if err := checkModelActive(st); err != nil {
				return nil, errors.Trace(err)
			}
		}
		return nil, errors.Trace(err)
	}
	return ms, nil
}
Ejemplo n.º 6
0
// ReadAllStateDirs loads and returns every StateDir persisted directly inside
// the supplied dirPath. If dirPath does not exist, no error is returned.
func ReadAllStateDirs(dirPath string) (dirs map[int]*StateDir, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot load relations state from %q", dirPath)
	if _, err := os.Stat(dirPath); os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, err
	}
	fis, err := ioutil.ReadDir(dirPath)
	if err != nil {
		return nil, err
	}
	dirs = map[int]*StateDir{}
	for _, fi := range fis {
		// Entries with integer names must be directories containing StateDir
		// data; all other names will be ignored.
		relationId, err := strconv.Atoi(fi.Name())
		if err != nil {
			// This doesn't look like a relation.
			continue
		}
		dir, err := ReadStateDir(dirPath, relationId)
		if err != nil {
			return nil, err
		}
		dirs[relationId] = dir
	}
	return dirs, nil
}
Ejemplo n.º 7
0
// Destroy sets the environment's lifecycle to Dying, preventing
// addition of services or machines to state.
func (e *Environment) Destroy() (err error) {
	defer errors.DeferredAnnotatef(&err, "failed to destroy environment")

	buildTxn := func(attempt int) ([]txn.Op, error) {

		// On the first attempt, we assume memory state is recent
		// enough to try using...
		if attempt != 0 {
			// ...but on subsequent attempts, we read fresh environ
			// state from the DB. Note that we do *not* refresh `e`
			// itself, as detailed in doc/hacking-state.txt
			if e, err = e.st.Environment(); err != nil {
				return nil, errors.Trace(err)
			}
		}

		ops, err := e.destroyOps()
		if err == errEnvironNotAlive {
			return nil, jujutxn.ErrNoOperations
		} else if err != nil {
			return nil, errors.Trace(err)
		}

		return ops, nil
	}
	return e.st.run(buildTxn)
}
Ejemplo n.º 8
0
Archivo: model.go Proyecto: bac/juju
func (m *Model) destroy(ensureNoHostedModels bool) (err error) {
	defer errors.DeferredAnnotatef(&err, "failed to destroy model")

	st, closeState, err := m.getState()
	if err != nil {
		return errors.Trace(err)
	}
	defer closeState()

	buildTxn := func(attempt int) ([]txn.Op, error) {
		// On the first attempt, we assume memory state is recent
		// enough to try using...
		if attempt != 0 {
			// ...but on subsequent attempts, we read fresh environ
			// state from the DB. Note that we do *not* refresh `e`
			// itself, as detailed in doc/hacking-state.txt
			if m, err = st.Model(); err != nil {
				return nil, errors.Trace(err)
			}
		}

		ops, err := m.destroyOps(ensureNoHostedModels, false)
		if err == errModelNotAlive {
			return nil, jujutxn.ErrNoOperations
		} else if err != nil {
			return nil, errors.Trace(err)
		}

		return ops, nil
	}

	return st.run(buildTxn)
}
Ejemplo n.º 9
0
// Remove removes a dead subnet. If the subnet is not dead it returns an error.
// It also removes any IP addresses associated with the subnet.
func (s *Subnet) Remove() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot remove subnet %q", s)

	if s.doc.Life != Dead {
		return errors.New("subnet is not dead")
	}
	addresses, closer := s.st.getCollection(ipaddressesC)
	defer closer()

	ops := []txn.Op{}
	id := s.ID()
	var doc struct {
		DocID string `bson:"_id"`
	}
	iter := addresses.Find(bson.D{{"subnetid", id}}).Iter()
	for iter.Next(&doc) {
		ops = append(ops, txn.Op{
			C:      ipaddressesC,
			Id:     doc.DocID,
			Remove: true,
		})
	}
	if err = iter.Close(); err != nil {
		return errors.Annotate(err, "cannot read addresses")
	}

	ops = append(ops, txn.Op{
		C:      subnetsC,
		Id:     s.doc.DocID,
		Remove: true,
	})
	return s.st.runTransaction(ops)
}
Ejemplo n.º 10
0
// SetMinUnits changes the number of minimum units required by the service.
func (s *Application) SetMinUnits(minUnits int) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set minimum units for application %q", s)
	defer func() {
		if err == nil {
			s.doc.MinUnits = minUnits
		}
	}()
	if minUnits < 0 {
		return errors.New("cannot set a negative minimum number of units")
	}
	service := &Application{st: s.st, doc: s.doc}
	// Removing the document never fails. Racing clients trying to create the
	// document generate one failure, but the second attempt should succeed.
	// If one client tries to update the document, and a racing client removes
	// it, the former should be able to re-create the document in the second
	// attempt. If the referred-to service advanced its life cycle to a not
	// alive state, an error is returned after the first failing attempt.
	buildTxn := func(attempt int) ([]txn.Op, error) {
		if attempt > 0 {
			if err := service.Refresh(); err != nil {
				return nil, err
			}
		}
		if service.doc.Life != Alive {
			return nil, errors.New("application is no longer alive")
		}
		if minUnits == service.doc.MinUnits {
			return nil, jujutxn.ErrNoOperations
		}
		return setMinUnitsOps(service, minUnits), nil
	}
	return s.st.run(buildTxn)
}
Ejemplo n.º 11
0
// SetConstraints replaces the current service constraints.
func (s *Service) SetConstraints(cons constraints.Value) (err error) {
	unsupported, err := s.st.validateConstraints(cons)
	if len(unsupported) > 0 {
		logger.Warningf(
			"setting constraints on service %q: unsupported constraints: %v", s.Name(), strings.Join(unsupported, ","))
	} else if err != nil {
		return err
	}
	if s.doc.Subordinate {
		return ErrSubordinateConstraints
	}
	defer errors.DeferredAnnotatef(&err, "cannot set constraints")
	if s.doc.Life != Alive {
		return errNotAlive
	}
	ops := []txn.Op{
		{
			C:      servicesC,
			Id:     s.doc.DocID,
			Assert: isAliveDoc,
		},
		setConstraintsOp(s.st, s.globalKey(), cons),
	}
	return onAbort(s.st.runTransaction(ops), errNotAlive)
}
Ejemplo n.º 12
0
// download fetches the supplied charm and checks that it has the correct sha256
// hash, then copies it into the directory. If a value is received on abort, the
// download will be stopped.
func (d *BundlesDir) download(info BundleInfo, target string, abort <-chan struct{}) (err error) {
	// First download...
	curl, err := url.Parse(info.URL().String())
	if err != nil {
		return errors.Annotate(err, "could not parse charm URL")
	}
	expectedSha256, err := info.ArchiveSha256()
	req := downloader.Request{
		URL:       curl,
		TargetDir: d.downloadsPath(),
		Verify:    downloader.NewSha256Verifier(expectedSha256),
	}
	logger.Infof("downloading %s from API server", info.URL())
	filename, err := d.downloader.Download(req, abort)
	if err != nil {
		return errors.Annotatef(err, "failed to download charm %q from API server", info.URL())
	}
	defer errors.DeferredAnnotatef(&err, "downloaded but failed to copy charm to %q from %q", target, filename)

	// ...then move the right location.
	if err := os.MkdirAll(d.path, 0755); err != nil {
		return errors.Trace(err)
	}
	if err := os.Rename(filename, target); err != nil {
		return errors.Trace(err)
	}
	return nil
}
Ejemplo n.º 13
0
// Remove removes the storage attachment from state, and may remove its storage
// instance as well, if the storage instance is Dying and no other references to
// it exist. It will fail if the storage attachment is not Dead.
func (st *State) RemoveStorageAttachment(storage names.StorageTag, unit names.UnitTag) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot remove storage attachment %s:%s", storage.Id(), unit.Id())
	buildTxn := func(attempt int) ([]txn.Op, error) {
		s, err := st.storageAttachment(storage, unit)
		if errors.IsNotFound(err) {
			return nil, jujutxn.ErrNoOperations
		} else if err != nil {
			return nil, errors.Trace(err)
		}
		inst, err := st.storageInstance(storage)
		if errors.IsNotFound(err) {
			// This implies that the attachment was removed
			// after the call to st.storageAttachment.
			return nil, jujutxn.ErrNoOperations
		} else if err != nil {
			return nil, errors.Trace(err)
		}
		ops, err := removeStorageAttachmentOps(st, s, inst)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return ops, nil
	}
	return st.run(buildTxn)
}
Ejemplo n.º 14
0
// setStatus inteprets the supplied params as documented on the type.
func setStatus(st *State, params setStatusParams) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set status")

	// TODO(fwereade): this can/should probably be recording the time the
	// status was *set*, not the time it happened to arrive in state.
	// We should almost certainly be accepting StatusInfo in the exposed
	// SetStatus methods, for symetry with the Status methods.
	now := time.Now().UnixNano()
	doc := statusDoc{
		Status:     params.status,
		StatusInfo: params.message,
		StatusData: escapeKeys(params.rawData),
		Updated:    now,
	}
	probablyUpdateStatusHistory(st, params.globalKey, doc)

	// Set the authoritative status document, or fail trying.
	buildTxn := updateStatusSource(st, params.globalKey, doc)
	if params.token != nil {
		buildTxn = buildTxnWithLeadership(buildTxn, params.token)
	}
	err = st.run(buildTxn)
	if cause := errors.Cause(err); cause == mgo.ErrNotFound {
		return errors.NotFoundf(params.badge)
	}
	return errors.Trace(err)
}
Ejemplo n.º 15
0
func (st *State) Close() (err error) {
	defer errors.DeferredAnnotatef(&err, "closing state failed")
	err1 := st.watcher.Stop()
	var err2 error
	if st.pwatcher != nil {
		err2 = st.pwatcher.Stop()
	}
	st.mu.Lock()
	var err3 error
	if st.allManager != nil {
		err3 = st.allManager.Stop()
	}
	st.mu.Unlock()
	st.db.Session.Close()
	var i int
	for i, err = range []error{err1, err2, err3} {
		if err != nil {
			switch i {
			case 0:
				err = errors.Annotatef(err, "failed to stop state watcher")
			case 1:
				err = errors.Annotatef(err, "failed to stop presence watcher")
			case 2:
				err = errors.Annotatef(err, "failed to stop all manager")
			}
			return err
		}
	}
	return nil
}
Ejemplo n.º 16
0
// Destroy sets the environment's lifecycle to Dying, preventing
// addition of services or machines to state.
func (e *Environment) Destroy() (err error) {
	defer errors.DeferredAnnotatef(&err, "failed to destroy environment")
	if e.Life() != Alive {
		return nil
	}

	if err := e.ensureDestroyable(); err != nil {
		return errors.Trace(err)
	}

	if err := e.startDestroy(); err != nil {
		if abortErr := e.abortDestroy(); abortErr != nil {
			return errors.Annotate(abortErr, err.Error())
		}
		return errors.Trace(err)
	}

	// Check that no new environments or machines were added between the first
	// check and the Environment.startDestroy().
	if err := e.ensureDestroyable(); err != nil {
		if abortErr := e.abortDestroy(); abortErr != nil {
			return errors.Annotate(abortErr, err.Error())
		}
		return errors.Trace(err)
	}

	if err := e.finishDestroy(); err != nil {
		if abortErr := e.abortDestroy(); abortErr != nil {
			return errors.Annotate(abortErr, err.Error())
		}
		return errors.Trace(err)
	}

	return nil
}
Ejemplo n.º 17
0
func (st *State) allProviderIDsForModelCollection(collectionName, entityLabelPlural string) (_ set.Strings, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot get ProviderIDs for all %s", entityLabelPlural)

	entities, closer := st.getCollection(collectionName)
	defer closer()

	allProviderIDs := set.NewStrings()
	var doc struct {
		ProviderID string `bson:"providerid"`
	}

	pattern := fmt.Sprintf("^%s:.+$", st.ModelUUID())
	modelProviderIDs := bson.D{{"providerid", bson.D{{"$regex", pattern}}}}
	onlyProviderIDField := bson.D{{"providerid", 1}}

	iter := entities.Find(modelProviderIDs).Select(onlyProviderIDField).Iter()
	for iter.Next(&doc) {
		localProviderID := st.localID(doc.ProviderID)
		allProviderIDs.Add(localProviderID)
	}
	if err := iter.Close(); err != nil {
		return nil, errors.Trace(err)
	}
	return allProviderIDs, nil
}
Ejemplo n.º 18
0
// SetState sets the State of an IPAddress. Valid state transitions
// are Unknown to Allocated or Unavailable, as well as setting the
// same state more than once. Any other transition will result in
// returning an error satisfying errors.IsNotValid().
func (i *IPAddress) SetState(newState AddressState) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set IP address %q to state %q", i, newState)

	validStates := []AddressState{AddressStateUnknown, newState}
	unknownOrSame := bson.DocElem{"state", bson.D{{"$in", validStates}}}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		if attempt > 0 {
			if err := i.Refresh(); errors.IsNotFound(err) {
				return nil, err
			} else if i.Life() == Dead {
				return nil, errors.New("address is dead")
			} else if i.State() != AddressStateUnknown {
				return nil, errors.NotValidf("transition from %q", i.doc.State)
			} else if err != nil {
				return nil, err
			}

		}
		return []txn.Op{{
			C:      ipaddressesC,
			Id:     i.doc.DocID,
			Assert: append(isAliveDoc, unknownOrSame),
			Update: bson.D{{"$set", bson.D{{"state", string(newState)}}}},
		}}, nil
	}

	err = i.st.run(buildTxn)
	if err != nil {
		return err
	}

	i.doc.State = newState
	return nil
}
Ejemplo n.º 19
0
// getAvailableRoleSizes returns the role sizes available for the configured
// location.
func (env *azureEnviron) getAvailableRoleSizes() (_ set.Strings, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot get available role sizes")

	snap := env.getSnapshot()
	if snap.availableRoleSizes != nil {
		return snap.availableRoleSizes, nil
	}
	locations, err := snap.api.ListLocations()
	if err != nil {
		return nil, errors.Annotate(err, "cannot list locations")
	}
	var available set.Strings
	for _, location := range locations {
		if location.Name != snap.ecfg.location() {
			continue
		}
		if location.ComputeCapabilities == nil {
			return nil, errors.Annotate(err, "cannot determine compute capabilities")
		}
		available = set.NewStrings(location.ComputeCapabilities.VirtualMachineRoleSizes...)
		break
	}
	if available == nil {
		return nil, errors.NotFoundf("location %q", snap.ecfg.location())
	}
	env.Lock()
	env.availableRoleSizes = available
	env.Unlock()
	return available, nil
}
Ejemplo n.º 20
0
// EnsureDead sets the Life of the IP address to Dead, if it's Alive. It
// does nothing otherwise.
func (i *IPAddress) EnsureDead() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set address %q to dead", i)

	if i.doc.Life == Dead {
		return nil
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		if attempt > 0 {
			if err := i.Refresh(); err != nil {
				// Address is either gone or
				// another error occurred.
				return nil, err
			}
			if i.Life() == Dead {
				return nil, jujutxn.ErrNoOperations
			}
			return nil, errors.Errorf("unexpected life value: %s", i.Life().String())
		}
		op := ensureIPAddressDeadOp(i)
		op.Assert = isAliveDoc
		return []txn.Op{op}, nil
	}

	err = i.st.run(buildTxn)
	if err != nil {
		return err
	}

	i.doc.Life = Dead
	return nil
}
Ejemplo n.º 21
0
// Remove removes an existing IP address. Trying to remove a missing
// address is not an error.
func (i *IPAddress) Remove() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot remove IP address %q", i)

	if i.doc.Life != Dead {
		return errors.New("IP address is not dead")
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		if attempt > 0 {
			if err := i.Refresh(); errors.IsNotFound(err) {
				return nil, jujutxn.ErrNoOperations
			} else if err != nil {
				return nil, err
			}
			if i.Life() != Dead {
				return nil, errors.New("address is not dead")
			}
		}
		return []txn.Op{{
			C:      ipaddressesC,
			Id:     i.doc.DocID,
			Assert: isDeadDoc,
			Remove: true,
		}}, nil
	}

	return i.st.run(buildTxn)
}
Ejemplo n.º 22
0
// readAllStateFiles loads and returns every stateFile persisted inside
// the supplied dirPath. If dirPath does not exist, no error is returned.
func readAllStateFiles(dirPath string) (files map[names.StorageTag]*stateFile, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot load storage state from %q", dirPath)
	if _, err := os.Stat(dirPath); os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, err
	}
	fis, err := ioutil.ReadDir(dirPath)
	if err != nil {
		return nil, err
	}
	files = make(map[names.StorageTag]*stateFile)
	for _, fi := range fis {
		if fi.IsDir() {
			continue
		}
		storageId := strings.Replace(fi.Name(), "-", "/", -1)
		if !names.IsValidStorage(storageId) {
			continue
		}
		tag := names.NewStorageTag(storageId)
		f, err := readStateFile(dirPath, tag)
		if err != nil {
			return nil, err
		}
		files[tag] = f
	}
	return files, nil
}
Ejemplo n.º 23
0
Archivo: add.go Proyecto: kat-co/juju
// Init is defined on the cmd.Command interface. It checks the
// arguments for sanity and sets up the command to run.
func (c *addCommand) Init(args []string) (err error) {
	defer errors.DeferredAnnotatef(&err, "invalid arguments specified")

	// Ensure we have 2 or more arguments.
	switch len(args) {
	case 0:
		return errNoCIDROrID
	case 1:
		return errNoSpace
	}

	// Try to validate first argument as a CIDR first.
	c.RawCIDR = args[0]
	c.CIDR, err = c.ValidateCIDR(args[0], false)
	if err != nil {
		// If it's not a CIDR it could be a ProviderId, so ignore the
		// error.
		c.ProviderId = args[0]
		c.RawCIDR = ""
	}

	// Validate the space name.
	c.Space, err = c.ValidateSpace(args[1])
	if err != nil {
		return err
	}

	// Add any given zones.
	for _, zone := range args[2:] {
		c.Zones = append(c.Zones, zone)
	}
	return nil
}
Ejemplo n.º 24
0
// Destroy ensures that the relation will be removed at some point; if no units
// are currently in scope, it will be removed immediately.
func (r *Relation) Destroy() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot destroy relation %q", r)
	if len(r.doc.Endpoints) == 1 && r.doc.Endpoints[0].Role == charm.RolePeer {
		return errors.Errorf("is a peer relation")
	}
	defer func() {
		if err == nil {
			// This is a white lie; the document might actually be removed.
			r.doc.Life = Dying
		}
	}()
	rel := &Relation{r.st, r.doc}
	// In this context, aborted transactions indicate that the number of units
	// in scope have changed between 0 and not-0. The chances of 5 successive
	// attempts each hitting this change -- which is itself an unlikely one --
	// are considered to be extremely small.
	buildTxn := func(attempt int) ([]txn.Op, error) {
		if attempt > 0 {
			if err := rel.Refresh(); errors.IsNotFound(err) {
				return []txn.Op{}, nil
			} else if err != nil {
				return nil, err
			}
		}
		ops, _, err := rel.destroyOps("")
		if err == errAlreadyDying {
			return nil, jujutxn.ErrNoOperations
		} else if err != nil {
			return nil, err
		}
		return ops, nil
	}
	return rel.st.run(buildTxn)
}
Ejemplo n.º 25
0
Archivo: open.go Proyecto: bac/juju
// Close the connection to the database.
func (st *State) Close() (err error) {
	defer errors.DeferredAnnotatef(&err, "closing state failed")

	var errs []error
	handle := func(name string, err error) {
		if err != nil {
			errs = append(errs, errors.Annotatef(err, "error stopping %s", name))
		}
	}
	if st.workers != nil {
		handle("standard workers", worker.Stop(st.workers))
	}

	st.mu.Lock()
	if st.allManager != nil {
		handle("allwatcher manager", st.allManager.Stop())
	}
	if st.allModelManager != nil {
		handle("allModelWatcher manager", st.allModelManager.Stop())
	}
	if st.allModelWatcherBacking != nil {
		handle("allModelWatcher backing", st.allModelWatcherBacking.Release())
	}
	st.session.Close()
	st.mu.Unlock()

	if len(errs) > 0 {
		for _, err := range errs[1:] {
			logger.Errorf("while closing state: %v", err)
		}
		return errs[0]
	}
	logger.Debugf("closed state without error")
	return nil
}
Ejemplo n.º 26
0
Archivo: volume.go Proyecto: bac/juju
// SetVolumeInfo sets the VolumeInfo for the specified volume.
func (st *State) SetVolumeInfo(tag names.VolumeTag, info VolumeInfo) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set info for volume %q", tag.Id())
	if info.VolumeId == "" {
		return errors.New("volume ID not set")
	}
	// TODO(axw) we should reject info without VolumeId set; can't do this
	// until the providers all set it correctly.
	buildTxn := func(attempt int) ([]txn.Op, error) {
		v, err := st.Volume(tag)
		if err != nil {
			return nil, errors.Trace(err)
		}
		// If the volume has parameters, unset them when
		// we set info for the first time, ensuring that
		// params and info are mutually exclusive.
		var unsetParams bool
		var ops []txn.Op
		if params, ok := v.Params(); ok {
			info.Pool = params.Pool
			unsetParams = true
		} else {
			// Ensure immutable properties do not change.
			oldInfo, err := v.Info()
			if err != nil {
				return nil, err
			}
			if err := validateVolumeInfoChange(info, oldInfo); err != nil {
				return nil, err
			}
		}
		ops = append(ops, setVolumeInfoOps(tag, info, unsetParams)...)
		return ops, nil
	}
	return st.run(buildTxn)
}
Ejemplo n.º 27
0
Archivo: subnets.go Proyecto: bac/juju
// AddSubnet creates and returns a new subnet
func (st *State) AddSubnet(args SubnetInfo) (subnet *Subnet, err error) {
	defer errors.DeferredAnnotatef(&err, "adding subnet %q", args.CIDR)

	subnet, err = st.newSubnetFromArgs(args)
	if err != nil {
		return nil, errors.Trace(err)
	}
	ops := st.addSubnetOps(args)
	ops = append(ops, assertModelActiveOp(st.ModelUUID()))
	buildTxn := func(attempt int) ([]txn.Op, error) {

		if attempt != 0 {
			if err := checkModelActive(st); err != nil {
				return nil, errors.Trace(err)
			}
			if _, err = st.Subnet(args.CIDR); err == nil {
				return nil, errors.AlreadyExistsf("subnet %q", args.CIDR)
			}
			if err := subnet.Refresh(); err != nil {
				if errors.IsNotFound(err) {
					return nil, errors.Errorf("ProviderId %q not unique", args.ProviderId)
				}
				return nil, errors.Trace(err)
			}
		}
		return ops, nil
	}
	err = st.run(buildTxn)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return subnet, nil
}
Ejemplo n.º 28
0
// VerifyConfig verifies that the InstanceConfig is valid.
func (cfg *InstanceConfig) VerifyConfig() (err error) {
	defer errors.DeferredAnnotatef(&err, "invalid machine configuration")
	if !names.IsValidMachine(cfg.MachineId) {
		return errors.New("invalid machine id")
	}
	if cfg.DataDir == "" {
		return errors.New("missing var directory")
	}
	if cfg.LogDir == "" {
		return errors.New("missing log directory")
	}
	if cfg.MetricsSpoolDir == "" {
		return errors.New("missing metrics spool directory")
	}
	if len(cfg.Jobs) == 0 {
		return errors.New("missing machine jobs")
	}
	if cfg.CloudInitOutputLog == "" {
		return errors.New("missing cloud-init output log path")
	}
	if cfg.tools == nil {
		// SetTools() has never been called successfully.
		return errors.New("missing tools")
	}
	// We don't need to check cfg.toolsURLs since SetTools() does.
	if cfg.APIInfo == nil {
		return errors.New("missing API info")
	}
	if cfg.APIInfo.ModelTag.Id() == "" {
		return errors.New("missing model tag")
	}
	if len(cfg.APIInfo.CACert) == 0 {
		return errors.New("missing API CA certificate")
	}
	if cfg.MachineAgentServiceName == "" {
		return errors.New("missing machine agent service name")
	}
	if cfg.MachineNonce == "" {
		return errors.New("missing machine nonce")
	}
	if cfg.Controller != nil {
		if err := cfg.verifyControllerConfig(); err != nil {
			return errors.Trace(err)
		}
	}
	if cfg.Bootstrap != nil {
		if err := cfg.verifyBootstrapConfig(); err != nil {
			return errors.Trace(err)
		}
	} else {
		if cfg.APIInfo.Tag != names.NewMachineTag(cfg.MachineId) {
			return errors.New("API entity tag must match started machine")
		}
		if len(cfg.APIInfo.Addrs) == 0 {
			return errors.New("missing API hosts")
		}
	}
	return nil
}
Ejemplo n.º 29
0
func (m *Machine) prepareOneSetDevicesAddresses(args *LinkLayerDeviceAddress) (_ *ipAddressDoc, err error) {
	defer errors.DeferredAnnotatef(&err, "invalid address %q", args.CIDRAddress)

	if err := m.validateSetDevicesAddressesArgs(args); err != nil {
		return nil, errors.Trace(err)
	}
	return m.newIPAddressDocFromArgs(args)
}
Ejemplo n.º 30
0
Archivo: spaces.go Proyecto: bac/juju
// AddSpace creates and returns a new space.
func (st *State) AddSpace(name string, providerId network.Id, subnets []string, isPublic bool) (newSpace *Space, err error) {
	defer errors.DeferredAnnotatef(&err, "adding space %q", name)
	if !names.IsValidSpace(name) {
		return nil, errors.NewNotValid(nil, "invalid space name")
	}

	spaceDoc := spaceDoc{
		Life:       Alive,
		Name:       name,
		IsPublic:   isPublic,
		ProviderId: string(providerId),
	}
	newSpace = &Space{doc: spaceDoc, st: st}

	ops := []txn.Op{{
		C:      spacesC,
		Id:     name,
		Assert: txn.DocMissing,
		Insert: spaceDoc,
	}}

	if providerId != "" {
		ops = append(ops, st.networkEntityGlobalKeyOp("space", providerId))
	}

	for _, subnetId := range subnets {
		// TODO:(mfoord) once we have refcounting for subnets we should
		// also assert that the refcount is zero as moving the space of a
		// subnet in use is not permitted.
		ops = append(ops, txn.Op{
			C:      subnetsC,
			Id:     subnetId,
			Assert: txn.DocExists,
			Update: bson.D{{"$set", bson.D{{"space-name", name}}}},
		})
	}

	if err := st.runTransaction(ops); err == txn.ErrAborted {
		if _, err := st.Space(name); err == nil {
			return nil, errors.AlreadyExistsf("space %q", name)
		}
		for _, subnetId := range subnets {
			if _, err := st.Subnet(subnetId); errors.IsNotFound(err) {
				return nil, err
			}
		}
		if err := newSpace.Refresh(); err != nil {
			if errors.IsNotFound(err) {
				return nil, errors.Errorf("ProviderId %q not unique", providerId)
			}
			return nil, errors.Trace(err)
		}
		return nil, errors.Trace(err)
	} else if err != nil {
		return nil, err
	}
	return newSpace, nil
}