func (mr *Machiner) Handle(_ <-chan struct{}) error { if err := mr.machine.Refresh(); params.IsCodeNotFoundOrCodeUnauthorized(err) { // NOTE(axw) we can distinguish between NotFound and CodeUnauthorized, // so we could call NotifyMachineDead here in case the agent failed to // call NotifyMachineDead directly after setting the machine Dead in // the first place. We're not doing that to be cautious: the machine // could be missing from state due to invalid global state. return worker.ErrTerminateAgent } else if err != nil { return err } life := mr.machine.Life() if life == params.Alive { observedConfig, err := getObservedNetworkConfig(networkingcommon.DefaultNetworkConfigSource()) if err != nil { return errors.Annotate(err, "cannot discover observed network config") } else if len(observedConfig) == 0 { logger.Warningf("not updating network config: no observed config found to update") } if len(observedConfig) > 0 { if err := mr.machine.SetObservedNetworkConfig(observedConfig); err != nil { return errors.Annotate(err, "cannot update observed network config") } } logger.Debugf("observed network config updated") return nil } logger.Debugf("%q is now %s", mr.config.Tag, life) if err := mr.machine.SetStatus(status.Stopped, "", nil); err != nil { return errors.Annotatef(err, "%s failed to set status stopped", mr.config.Tag) } // Attempt to mark the machine Dead. If the machine still has units // assigned, or storage attached, this will fail with // CodeHasAssignedUnits or CodeMachineHasAttachedStorage respectively. // Once units or storage are removed, the watcher will trigger again // and we'll reattempt. if err := mr.machine.EnsureDead(); err != nil { if params.IsCodeHasAssignedUnits(err) { return nil } if params.IsCodeMachineHasAttachedStorage(err) { logger.Tracef("machine still has storage attached") return nil } return errors.Annotatef(err, "%s failed to set machine to dead", mr.config.Tag) } // Report on the machine's death. It is important that we do this after // the machine is Dead, because this is the mechanism we use to clean up // the machine (uninstall). If we were to report before marking the machine // as Dead, then we would risk uninstalling prematurely. if mr.config.NotifyMachineDead != nil { if err := mr.config.NotifyMachineDead(); err != nil { return errors.Annotate(err, "reporting machine death") } } return worker.ErrTerminateAgent }
// RestoreError makes a best effort at converting the given error // back into an error originally converted by ServerError(). If the // error could not be converted then false is returned. func RestoreError(err error) (error, bool) { err = errors.Cause(err) if apiErr, ok := err.(*params.Error); !ok { return err, false } else if apiErr == nil { return nil, true } if params.ErrCode(err) == "" { return err, false } msg := err.Error() if singleton, ok := singletonError(err); ok { return singleton, true } // TODO(ericsnow) Support the other error types handled by ServerError(). switch { case params.IsCodeUnauthorized(err): return errors.NewUnauthorized(nil, msg), true case params.IsCodeNotFound(err): // TODO(ericsnow) UnknownModelError should be handled here too. // ...by parsing msg? return errors.NewNotFound(nil, msg), true case params.IsCodeAlreadyExists(err): return errors.NewAlreadyExists(nil, msg), true case params.IsCodeNotAssigned(err): return errors.NewNotAssigned(nil, msg), true case params.IsCodeHasAssignedUnits(err): // TODO(ericsnow) Handle state.HasAssignedUnitsError here. // ...by parsing msg? return err, false case params.IsCodeNoAddressSet(err): // TODO(ericsnow) Handle isNoAddressSetError here. // ...by parsing msg? return err, false case params.IsCodeNotProvisioned(err): return errors.NewNotProvisioned(nil, msg), true case params.IsCodeUpgradeInProgress(err): // TODO(ericsnow) Handle state.UpgradeInProgressError here. // ...by parsing msg? return err, false case params.IsCodeMachineHasAttachedStorage(err): // TODO(ericsnow) Handle state.HasAttachmentsError here. // ...by parsing msg? return err, false case params.IsCodeNotSupported(err): return errors.NewNotSupported(nil, msg), true case params.IsBadRequest(err): return errors.NewBadRequest(nil, msg), true case params.IsMethodNotAllowed(err): return errors.NewMethodNotAllowed(nil, msg), true case params.ErrCode(err) == params.CodeDischargeRequired: // TODO(ericsnow) Handle DischargeRequiredError here. return err, false default: return err, false } }
func (mr *Machiner) Handle(_ <-chan struct{}) error { if err := mr.machine.Refresh(); params.IsCodeNotFoundOrCodeUnauthorized(err) { return worker.ErrTerminateAgent } else if err != nil { return err } life := mr.machine.Life() if life == params.Alive { return nil } logger.Debugf("%q is now %s", mr.tag, life) if err := mr.machine.SetStatus(params.StatusStopped, "", nil); err != nil { return errors.Annotatef(err, "%s failed to set status stopped", mr.tag) } // Attempt to mark the machine Dead. If the machine still has units // assigned, or storage attached, this will fail with // CodeHasAssignedUnits or CodeMachineHasAttachedStorage respectively. // Once units or storage are removed, the watcher will trigger again // and we'll reattempt. if err := mr.machine.EnsureDead(); err != nil { if params.IsCodeHasAssignedUnits(err) { return nil } if params.IsCodeMachineHasAttachedStorage(err) { logger.Tracef("machine still has storage attached") return nil } return errors.Annotatef(err, "%s failed to set machine to dead", mr.tag) } return worker.ErrTerminateAgent }