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  }