github.com/Jeffail/benthos/v3@v3.65.0/lib/output/resource.go (about) 1 package output 2 3 import ( 4 "context" 5 "time" 6 7 "github.com/Jeffail/benthos/v3/internal/docs" 8 "github.com/Jeffail/benthos/v3/internal/interop" 9 "github.com/Jeffail/benthos/v3/lib/log" 10 "github.com/Jeffail/benthos/v3/lib/metrics" 11 "github.com/Jeffail/benthos/v3/lib/types" 12 ) 13 14 func init() { 15 Constructors[TypeResource] = TypeSpec{ 16 constructor: fromSimpleConstructor(NewResource), 17 Summary: ` 18 Resource is an output type that runs a resource output by its name.`, 19 Description: ` 20 This output allows you to reference the same configured output resource in multiple places, and can also tidy up large nested configs. For example, the config: 21 22 ` + "```yaml" + ` 23 output: 24 broker: 25 pattern: fan_out 26 outputs: 27 - kafka: 28 addresses: [ TODO ] 29 topic: foo 30 - gcp_pubsub: 31 project: bar 32 topic: baz 33 ` + "```" + ` 34 35 Could also be expressed as: 36 37 ` + "``` yaml" + ` 38 output: 39 broker: 40 pattern: fan_out 41 outputs: 42 - resource: foo 43 - resource: bar 44 45 output_resources: 46 - label: foo 47 kafka: 48 addresses: [ TODO ] 49 topic: foo 50 51 - label: bar 52 gcp_pubsub: 53 project: bar 54 topic: baz 55 ` + "```" + ` 56 57 You can find out more about resources [in this document.](/docs/configuration/resources)`, 58 Categories: []Category{ 59 CategoryUtility, 60 }, 61 config: docs.FieldComponent().HasType(docs.FieldTypeString).HasDefault(""), 62 } 63 } 64 65 //------------------------------------------------------------------------------ 66 67 // Resource is a processor that returns the result of a output resource. 68 type Resource struct { 69 mgr types.Manager 70 name string 71 log log.Modular 72 stats metrics.Type 73 74 transactions <-chan types.Transaction 75 76 ctx context.Context 77 done func() 78 79 mErrNotFound metrics.StatCounter 80 } 81 82 // NewResource returns a resource output. 83 func NewResource( 84 conf Config, mgr types.Manager, log log.Modular, stats metrics.Type, 85 ) (Type, error) { 86 if err := interop.ProbeOutput(context.Background(), mgr, conf.Resource); err != nil { 87 return nil, err 88 } 89 ctx, done := context.WithCancel(context.Background()) 90 return &Resource{ 91 mgr: mgr, 92 name: conf.Resource, 93 log: log, 94 stats: stats, 95 ctx: ctx, 96 done: done, 97 mErrNotFound: stats.GetCounter("error_not_found"), 98 }, nil 99 } 100 101 //------------------------------------------------------------------------------ 102 103 func (r *Resource) loop() { 104 // Metrics paths 105 var ( 106 mCount = r.stats.GetCounter("count") 107 ) 108 109 var ts *types.Transaction 110 for { 111 if ts == nil { 112 select { 113 case t, open := <-r.transactions: 114 if !open { 115 r.done() 116 return 117 } 118 ts = &t 119 case <-r.ctx.Done(): 120 return 121 } 122 } 123 mCount.Incr(1) 124 125 var err error 126 if oerr := interop.AccessOutput(context.Background(), r.mgr, r.name, func(o types.OutputWriter) { 127 err = o.WriteTransaction(r.ctx, *ts) 128 }); oerr != nil { 129 err = oerr 130 } 131 if err != nil { 132 r.log.Debugf("Failed to obtain output resource '%v': %v", r.name, err) 133 r.mErrNotFound.Incr(1) 134 select { 135 case <-time.After(time.Second): 136 case <-r.ctx.Done(): 137 return 138 } 139 } else { 140 ts = nil 141 } 142 } 143 } 144 145 //------------------------------------------------------------------------------ 146 147 // Consume assigns a messages channel for the output to read. 148 func (r *Resource) Consume(ts <-chan types.Transaction) error { 149 if r.transactions != nil { 150 return types.ErrAlreadyStarted 151 } 152 r.transactions = ts 153 go r.loop() 154 return nil 155 } 156 157 // Connected returns a boolean indicating whether this output is currently 158 // connected to its target. 159 func (r *Resource) Connected() (isConnected bool) { 160 var err error 161 if err = interop.AccessOutput(context.Background(), r.mgr, r.name, func(o types.OutputWriter) { 162 isConnected = o.Connected() 163 }); err != nil { 164 r.log.Debugf("Failed to obtain output resource '%v': %v", r.name, err) 165 r.mErrNotFound.Incr(1) 166 } 167 return 168 } 169 170 // CloseAsync shuts down the output and stops processing requests. 171 func (r *Resource) CloseAsync() { 172 r.done() 173 } 174 175 // WaitForClose blocks until the output has closed down. 176 func (r *Resource) WaitForClose(timeout time.Duration) error { 177 select { 178 case <-r.ctx.Done(): 179 case <-time.After(timeout): 180 return types.ErrTimeout 181 } 182 return nil 183 } 184 185 //------------------------------------------------------------------------------