// 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 } }
// updateContext fills in all unspecialized fields that require an API call to // discover. // // Approximately *every* line of code in this function represents a bug: ie, some // piece of information we expose to the charm but which we fail to report changes // to via hooks. Furthermore, the fact that we make multiple API calls at this // time, rather than grabbing everything we need in one go, is unforgivably yucky. func (f *contextFactory) updateContext(ctx *HookContext) (err error) { defer errors.Trace(err) ctx.apiAddrs, err = f.state.APIAddresses() if err != nil { return err } ctx.machinePorts, err = f.state.AllMachinePorts(f.machineTag) if err != nil { return errors.Trace(err) } statusCode, statusInfo, err := f.unit.MeterStatus() if err != nil { return errors.Annotate(err, "could not retrieve meter status for unit") } ctx.meterStatus = &meterStatus{ code: statusCode, info: statusInfo, } // TODO(fwereade) 23-10-2014 bug 1384572 // Nothing here should ever be getting the environ config directly. environConfig, err := f.state.ModelConfig() if err != nil { return err } ctx.proxySettings = environConfig.ProxySettings() // Calling these last, because there's a potential race: they're not guaranteed // to be set in time to be needed for a hook. If they're not, we just leave them // unset as we always have; this isn't great but it's about behaviour preservation. ctx.publicAddress, err = f.unit.PublicAddress() if err != nil && !params.IsCodeNoAddressSet(err) { return err } ctx.privateAddress, err = f.unit.PrivateAddress() if err != nil && !params.IsCodeNoAddressSet(err) { return err } return nil }
func NewHookContext( unit *uniter.Unit, id, uuid, envName string, relationId int, remoteUnitName string, relations map[int]*ContextRelation, apiAddrs []string, serviceOwner string, proxySettings proxy.Settings, actionParams map[string]interface{}, ) (*HookContext, error) { ctx := &HookContext{ unit: unit, id: id, uuid: uuid, envName: envName, relationId: relationId, remoteUnitName: remoteUnitName, relations: relations, apiAddrs: apiAddrs, serviceOwner: serviceOwner, proxySettings: proxySettings, actionParams: actionParams, } // Get and cache the addresses. var err error ctx.publicAddress, err = unit.PublicAddress() if err != nil && !params.IsCodeNoAddressSet(err) { return nil, err } ctx.privateAddress, err = unit.PrivateAddress() if err != nil && !params.IsCodeNoAddressSet(err) { return nil, err } return ctx, nil }
func NewHookContext( unit *uniter.Unit, state *uniter.State, id, uuid, envName string, relationId int, remoteUnitName string, relations map[int]*ContextRelation, apiAddrs []string, serviceOwner names.UserTag, proxySettings proxy.Settings, canAddMetrics bool, metrics *charm.Metrics, actionData *ActionData, assignedMachineTag names.MachineTag, paths Paths, ) (*HookContext, error) { ctx := &HookContext{ unit: unit, state: state, id: id, uuid: uuid, envName: envName, unitName: unit.Name(), relationId: relationId, remoteUnitName: remoteUnitName, relations: relations, apiAddrs: apiAddrs, serviceOwner: serviceOwner, proxySettings: proxySettings, metricsRecorder: nil, definedMetrics: metrics, metricsSender: unit, actionData: actionData, pendingPorts: make(map[PortRange]PortRangeInfo), assignedMachineTag: assignedMachineTag, } if canAddMetrics { charmURL, err := unit.CharmURL() if err != nil { return nil, err } ctx.metricsRecorder, err = NewJSONMetricsRecorder(paths.GetMetricsSpoolDir(), charmURL.String()) if err != nil { return nil, err } ctx.metricsReader, err = NewJSONMetricsReader(paths.GetMetricsSpoolDir()) if err != nil { return nil, err } } // Get and cache the addresses. var err error ctx.publicAddress, err = unit.PublicAddress() if err != nil && !params.IsCodeNoAddressSet(err) { return nil, err } ctx.privateAddress, err = unit.PrivateAddress() if err != nil && !params.IsCodeNoAddressSet(err) { return nil, err } ctx.availabilityzone, err = unit.AvailabilityZone() if err != nil { return nil, err } ctx.machinePorts, err = state.AllMachinePorts(ctx.assignedMachineTag) if err != nil { return nil, errors.Trace(err) } statusCode, statusInfo, err := unit.MeterStatus() if err != nil { return nil, errors.Annotate(err, "could not retrieve meter status for unit") } ctx.meterStatus = &meterStatus{ code: statusCode, info: statusInfo, } return ctx, nil }