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 }