// 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) }
// 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 }
// 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 }
// 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 }
// 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 }
// 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 }
// 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) }
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) }
// 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) }
// 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) }
// 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) }
// 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 }
// 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) }
// 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) }
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 }
// 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 }
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 }
// 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 }
// 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 }
// 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 }
// 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) }
// 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 }
// 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 }
// 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) }
// 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 }
// 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) }
// 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 }
// 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 }
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) }
// 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 }