github.com/observiq/carbon@v0.9.11-0.20200820160507-1b872e368a5e/operator/helper/writer.go (about) 1 package helper 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 8 "github.com/observiq/carbon/entry" 9 "github.com/observiq/carbon/operator" 10 ) 11 12 func NewWriterConfig(operatorID, operatorType string) WriterConfig { 13 return WriterConfig{ 14 BasicConfig: NewBasicConfig(operatorID, operatorType), 15 } 16 } 17 18 // WriterConfig is the configuration of a writer operator. 19 type WriterConfig struct { 20 BasicConfig `yaml:",inline"` 21 OutputIDs OutputIDs `json:"output" yaml:"output"` 22 } 23 24 // Build will build a writer operator from the config. 25 func (c WriterConfig) Build(context operator.BuildContext) (WriterOperator, error) { 26 basicOperator, err := c.BasicConfig.Build(context) 27 if err != nil { 28 return WriterOperator{}, err 29 } 30 31 writer := WriterOperator{ 32 OutputIDs: c.OutputIDs, 33 BasicOperator: basicOperator, 34 } 35 return writer, nil 36 } 37 38 // SetNamespace will namespace the output ids of the writer. 39 func (c *WriterConfig) SetNamespace(namespace string, exclusions ...string) { 40 c.BasicConfig.SetNamespace(namespace, exclusions...) 41 for i, outputID := range c.OutputIDs { 42 if CanNamespace(outputID, exclusions) { 43 c.OutputIDs[i] = AddNamespace(outputID, namespace) 44 } 45 } 46 } 47 48 // WriterOperator is an operator that can write to other operators. 49 type WriterOperator struct { 50 BasicOperator 51 OutputIDs OutputIDs 52 OutputOperators []operator.Operator 53 } 54 55 // Write will write an entry to the outputs of the operator. 56 func (w *WriterOperator) Write(ctx context.Context, e *entry.Entry) { 57 for i, operator := range w.OutputOperators { 58 if i == len(w.OutputOperators)-1 { 59 _ = operator.Process(ctx, e) 60 return 61 } 62 operator.Process(ctx, e.Copy()) 63 } 64 } 65 66 // CanOutput always returns true for a writer operator. 67 func (w *WriterOperator) CanOutput() bool { 68 return true 69 } 70 71 // Outputs returns the outputs of the writer operator. 72 func (w *WriterOperator) Outputs() []operator.Operator { 73 return w.OutputOperators 74 } 75 76 // SetOutputs will set the outputs of the operator. 77 func (w *WriterOperator) SetOutputs(operators []operator.Operator) error { 78 outputOperators := make([]operator.Operator, 0) 79 80 for _, operatorID := range w.OutputIDs { 81 operator, ok := w.findOperator(operators, operatorID) 82 if !ok { 83 return fmt.Errorf("operator '%s' does not exist", operatorID) 84 } 85 86 if !operator.CanProcess() { 87 return fmt.Errorf("operator '%s' can not process entries", operatorID) 88 } 89 90 outputOperators = append(outputOperators, operator) 91 } 92 93 // No outputs have been set, so use the next configured operator 94 if len(w.OutputIDs) == 0 { 95 currentOperatorIndex := -1 96 for i, operator := range operators { 97 if operator.ID() == w.ID() { 98 currentOperatorIndex = i 99 break 100 } 101 } 102 if currentOperatorIndex == -1 { 103 return fmt.Errorf("unexpectedly could not find self in array of operators") 104 } 105 nextOperatorIndex := currentOperatorIndex + 1 106 if nextOperatorIndex == len(operators) { 107 return fmt.Errorf("cannot omit output for the last operator in the pipeline") 108 } 109 nextOperator := operators[nextOperatorIndex] 110 if !nextOperator.CanProcess() { 111 return fmt.Errorf("operator '%s' cannot process entries, but it was selected as a receiver because 'output' was omitted", nextOperator.ID()) 112 } 113 outputOperators = append(outputOperators, nextOperator) 114 } 115 116 w.OutputOperators = outputOperators 117 return nil 118 } 119 120 // FindOperator will find an operator matching the supplied id. 121 func (w *WriterOperator) findOperator(operators []operator.Operator, operatorID string) (operator.Operator, bool) { 122 for _, operator := range operators { 123 if operator.ID() == operatorID { 124 return operator, true 125 } 126 } 127 return nil, false 128 } 129 130 // OutputIDs is a collection of operator IDs used as outputs. 131 type OutputIDs []string 132 133 // UnmarshalJSON will unmarshal a string or array of strings to OutputIDs. 134 func (o *OutputIDs) UnmarshalJSON(bytes []byte) error { 135 var value interface{} 136 err := json.Unmarshal(bytes, &value) 137 if err != nil { 138 return err 139 } 140 141 ids, err := o.fromInterface(value) 142 if err != nil { 143 return err 144 } 145 146 *o = ids 147 return nil 148 } 149 150 // UnmarshalYAML will unmarshal a string or array of strings to OutputIDs. 151 func (o *OutputIDs) UnmarshalYAML(unmarshal func(interface{}) error) error { 152 var value interface{} 153 err := unmarshal(&value) 154 if err != nil { 155 return err 156 } 157 158 ids, err := o.fromInterface(value) 159 if err != nil { 160 return err 161 } 162 163 *o = ids 164 return nil 165 } 166 167 // fromInterface will parse OutputIDs from a raw interface. 168 func (o *OutputIDs) fromInterface(value interface{}) (OutputIDs, error) { 169 if str, ok := value.(string); ok { 170 return OutputIDs{str}, nil 171 } 172 173 if array, ok := value.([]interface{}); ok { 174 return o.fromArray(array) 175 } 176 177 return nil, fmt.Errorf("value is not of type string or string array") 178 } 179 180 // fromArray will parse OutputIDs from a raw array. 181 func (o *OutputIDs) fromArray(array []interface{}) (OutputIDs, error) { 182 ids := OutputIDs{} 183 for _, rawValue := range array { 184 strValue, ok := rawValue.(string) 185 if !ok { 186 return nil, fmt.Errorf("value in array is not of type string") 187 } 188 ids = append(ids, strValue) 189 } 190 return ids, nil 191 }