github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/dependency/context.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package dependency
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  
    11  	"github.com/juju/juju/worker"
    12  )
    13  
    14  // context encapsulates a snapshot of workers and output funcs and implements Context.
    15  type context struct {
    16  
    17  	// clientName is the name of the manifold for whose convenience this exists.
    18  	clientName string
    19  
    20  	// abort is closed when the worker being started is no longer required.
    21  	abort <-chan struct{}
    22  
    23  	// expired is closed when the context should no longer be used.
    24  	expired chan struct{}
    25  
    26  	// workers holds the snapshot of manifold workers.
    27  	workers map[string]worker.Worker
    28  
    29  	// outputs holds the snapshot of manifold output funcs.
    30  	outputs map[string]OutputFunc
    31  
    32  	// accessLog holds the names and types of resource requests, and any error
    33  	// encountered. It does not include requests made after expiry.
    34  	accessLog []resourceAccess
    35  }
    36  
    37  // Abort is part of the Context interface.
    38  func (ctx *context) Abort() <-chan struct{} {
    39  	return ctx.abort
    40  }
    41  
    42  // Get is part of the Context interface.
    43  func (ctx *context) Get(resourceName string, out interface{}) error {
    44  	logger.Tracef("%q manifold requested %q resource", ctx.clientName, resourceName)
    45  	select {
    46  	case <-ctx.expired:
    47  		return errors.New("expired context: cannot be used outside Start func")
    48  	default:
    49  		err := ctx.rawAccess(resourceName, out)
    50  		ctx.accessLog = append(ctx.accessLog, resourceAccess{
    51  			name: resourceName,
    52  			as:   fmt.Sprintf("%T", out),
    53  			err:  err,
    54  		})
    55  		return err
    56  	}
    57  }
    58  
    59  // expire closes the expired channel. Calling it more than once will panic.
    60  func (ctx *context) expire() {
    61  	close(ctx.expired)
    62  }
    63  
    64  // rawAccess is a GetResourceFunc that neither checks enpiry nor records access.
    65  func (ctx *context) rawAccess(resourceName string, out interface{}) error {
    66  	input, found := ctx.workers[resourceName]
    67  	if !found {
    68  		return errors.Annotatef(ErrMissing, "%q not declared", resourceName)
    69  	} else if input == nil {
    70  		return errors.Annotatef(ErrMissing, "%q not running", resourceName)
    71  	}
    72  	if out == nil {
    73  		// No conversion necessary, just an exist check.
    74  		return nil
    75  	}
    76  	convert := ctx.outputs[resourceName]
    77  	if convert == nil {
    78  		return errors.Annotatef(ErrMissing, "%q not exposed", resourceName)
    79  	}
    80  	return convert(input, out)
    81  }
    82  
    83  // resourceAccess describes a call made to (*context).Get.
    84  type resourceAccess struct {
    85  
    86  	// name is the name of the resource requested.
    87  	name string
    88  
    89  	// as is the string representation of the type of the out param.
    90  	as string
    91  
    92  	// err is any error returned from rawAccess.
    93  	err error
    94  }
    95  
    96  // report returns a convenient representation of ra.
    97  func (ra resourceAccess) report() map[string]interface{} {
    98  	report := map[string]interface{}{
    99  		KeyName: ra.name,
   100  		KeyType: ra.as,
   101  	}
   102  	if ra.err != nil {
   103  		report[KeyError] = ra.err.Error()
   104  	}
   105  	return report
   106  }
   107  
   108  // resourceLogReport returns a convenient representation of accessLog.
   109  func resourceLogReport(accessLog []resourceAccess) []map[string]interface{} {
   110  	result := make([]map[string]interface{}, len(accessLog))
   111  	for i, access := range accessLog {
   112  		result[i] = access.report()
   113  	}
   114  	return result
   115  }