Esempio n. 1
0
File: sender.go Progetto: bac/juju
func (s *sender) sendMetrics(reader spool.MetricReader) error {
	batches, err := reader.Read()
	if err != nil {
		return errors.Annotate(err, "failed to open the metric reader")
	}
	var sendBatches []params.MetricBatchParam
	for _, batch := range batches {
		sendBatches = append(sendBatches, spool.APIMetricBatch(batch))
	}
	results, err := s.client.AddMetricBatches(sendBatches)
	if err != nil {
		return errors.Annotate(err, "could not send metrics")
	}
	for batchUUID, resultErr := range results {
		// if we fail to send any metric batch we log a warning with the assumption that
		// the unsent metric batches remain in the spool directory and will be sent to the
		// controller when the network partition is restored.
		if _, ok := resultErr.(*params.Error); ok || params.IsCodeAlreadyExists(resultErr) {
			err := reader.Remove(batchUUID)
			if err != nil {
				logger.Errorf("could not remove batch %q from spool: %v", batchUUID, err)
			}
		} else {
			logger.Errorf("failed to send batch %q: %v", batchUUID, resultErr)
		}
	}
	return nil
}
Esempio n. 2
0
// 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
	}
}
Esempio n. 3
0
// Do sends metrics from the metric spool to the
// state server via an api call.
func (s *sender) Do(stop <-chan struct{}) error {
	reader, err := s.factory.Reader()
	if err != nil {
		return errors.Trace(err)
	}
	batches, err := reader.Read()
	if err != nil {
		logger.Warningf("failed to open the metric reader: %v", err)
		return errors.Trace(err)
	}
	defer reader.Close()
	var sendBatches []params.MetricBatchParam
	for _, batch := range batches {
		sendBatches = append(sendBatches, spool.APIMetricBatch(batch))
	}
	results, err := s.client.AddMetricBatches(sendBatches)
	if err != nil {
		logger.Warningf("could not send metrics: %v", err)
		return errors.Trace(err)
	}
	for batchUUID, resultErr := range results {
		// if we fail to send any metric batch we log a warning with the assumption that
		// the unsent metric batches remain in the spool directory and will be sent to the
		// state server when the network partition is restored.
		if _, ok := resultErr.(*params.Error); ok || params.IsCodeAlreadyExists(resultErr) {
			err = reader.Remove(batchUUID)
			if err != nil {
				logger.Warningf("could not remove batch %q from spool: %v", batchUUID, err)
			}
		} else {
			logger.Warningf("failed to send batch %q: %v", batchUUID, resultErr)
		}
	}
	return nil
}
Esempio n. 4
0
// FlushContext implements the Context interface.
func (ctx *HookContext) FlushContext(process string, ctxErr error) (err error) {
	// A non-existant metricsRecorder simply means that metrics were disabled
	// for this hook run.
	if ctx.metricsRecorder != nil {
		err := ctx.metricsRecorder.Close()
		if err != nil {
			return errors.Trace(err)
		}
	}

	writeChanges := ctxErr == nil

	// In the case of Actions, handle any errors using finalizeAction.
	if ctx.actionData != nil {
		// If we had an error in err at this point, it's part of the
		// normal behavior of an Action.  Errors which happen during
		// the finalize should be handed back to the uniter.  Close
		// over the existing err, clear it, and only return errors
		// which occur during the finalize, e.g. API call errors.
		defer func(ctxErr error) {
			err = ctx.finalizeAction(ctxErr, err)
		}(ctxErr)
		ctxErr = nil
	} else {
		// TODO(gsamfira): Just for now, reboot will not be supported in actions.
		defer ctx.handleReboot(&err)
	}

	for id, rctx := range ctx.relations {
		if writeChanges {
			if e := rctx.WriteSettings(); e != nil {
				e = errors.Errorf(
					"could not write settings from %q to relation %d: %v",
					process, id, e,
				)
				logger.Errorf("%v", e)
				if ctxErr == nil {
					ctxErr = e
				}
			}
		}
	}

	for rangeKey, rangeInfo := range ctx.pendingPorts {
		if writeChanges {
			var e error
			var op string
			if rangeInfo.ShouldOpen {
				e = ctx.unit.OpenPorts(
					rangeKey.Ports.Protocol,
					rangeKey.Ports.FromPort,
					rangeKey.Ports.ToPort,
				)
				op = "open"
			} else {
				e = ctx.unit.ClosePorts(
					rangeKey.Ports.Protocol,
					rangeKey.Ports.FromPort,
					rangeKey.Ports.ToPort,
				)
				op = "close"
			}
			if e != nil {
				e = errors.Annotatef(e, "cannot %s %v", op, rangeKey.Ports)
				logger.Errorf("%v", e)
				if ctxErr == nil {
					ctxErr = e
				}
			}
		}
	}

	// add storage to unit dynamically
	if len(ctx.storageAddConstraints) > 0 && writeChanges {
		err := ctx.unit.AddStorage(ctx.storageAddConstraints)
		if err != nil {
			err = errors.Annotatef(err, "cannot add storage")
			logger.Errorf("%v", err)
			if ctxErr == nil {
				ctxErr = err
			}
		}
	}

	// TODO (tasdomas) 2014 09 03: context finalization needs to modified to apply all
	//                             changes in one api call to minimize the risk
	//                             of partial failures.

	if ctx.metricsReader == nil {
		return ctxErr
	}

	if !writeChanges {
		return ctxErr
	}

	batches, err := ctx.metricsReader.Open()
	if err != nil {
		return errors.Trace(err)
	}
	defer ctx.metricsReader.Close()

	var sendBatches []params.MetricBatch
	for _, batch := range batches {
		if len(batch.Metrics) == 0 { // empty batches not supported yet.
			logger.Infof("skipping and removing empty metrics batch with UUID %q", batch.UUID)
			err := ctx.metricsReader.Remove(batch.UUID)
			if err != nil {
				return errors.Trace(err)
			}
			continue
		}
		metrics := make([]params.Metric, len(batch.Metrics))
		for i, metric := range batch.Metrics {
			metrics[i] = params.Metric{Key: metric.Key, Value: metric.Value, Time: metric.Time}
		}
		batchParam := params.MetricBatch{
			UUID:     batch.UUID,
			CharmURL: batch.CharmURL,
			Created:  batch.Created,
			Metrics:  metrics,
		}
		sendBatches = append(sendBatches, batchParam)
	}
	results, err := ctx.metricsSender.AddMetricBatches(sendBatches)
	if err != nil {
		// Do not return metric sending error.
		logger.Errorf("%v", err)
	}
	for batchUUID, resultErr := range results {
		if resultErr == nil || resultErr == (*params.Error)(nil) || params.IsCodeAlreadyExists(resultErr) {
			err = ctx.metricsReader.Remove(batchUUID)
			if err != nil {
				logger.Errorf("could not remove batch %q from spool: %v", batchUUID, err)
			}
		} else {
			logger.Errorf("failed to send batch %q: %v", batchUUID, resultErr)
		}
	}

	return ctxErr
}