github.com/pachyderm/pachyderm@v1.13.4/src/server/pkg/serde/yaml_decoder.go (about)

     1  // yaml_decoder.go implements the structfmt.Decoder interfaces for the yaml text
     2  // format
     3  
     4  package serde
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"io"
    10  
    11  	"github.com/gogo/protobuf/jsonpb"
    12  	"github.com/gogo/protobuf/proto"
    13  	"github.com/pachyderm/pachyderm/src/client/pkg/errors"
    14  	"gopkg.in/pachyderm/yaml.v3"
    15  )
    16  
    17  // DecodeYAML is a convenience function that decodes yaml data using a
    18  // YAMLDecoder, but can be called inline
    19  func DecodeYAML(yamlData []byte, v interface{}) error {
    20  	d := NewYAMLDecoder(bytes.NewReader(yamlData))
    21  	return d.Decode(v)
    22  }
    23  
    24  // YAMLDecoder is an implementation of serde.Decoder that operates on yaml data
    25  type YAMLDecoder struct {
    26  	d *yaml.Decoder
    27  }
    28  
    29  // NewYAMLDecoder returns a new YAMLDecoder that reads from 'r'
    30  func NewYAMLDecoder(r io.Reader) *YAMLDecoder {
    31  	return &YAMLDecoder{d: yaml.NewDecoder(r)}
    32  }
    33  
    34  // Decode implements the corresponding method of serde.Decoder
    35  func (d *YAMLDecoder) Decode(v interface{}) error {
    36  	return d.DecodeTransform(v, nil)
    37  }
    38  
    39  // DecodeTransform implements the corresponding method of serde.Decoder
    40  func (d *YAMLDecoder) DecodeTransform(v interface{}, f func(map[string]interface{}) error) error {
    41  	jsonData, err := d.yamlToJSONTransform(f)
    42  	if err != nil {
    43  		return err
    44  	}
    45  
    46  	// parse transformed JSON into 'v'
    47  	if err := json.Unmarshal(jsonData, v); err != nil {
    48  		return errors.Wrapf(err, "parse error while canonicalizing yaml")
    49  	}
    50  	return nil
    51  }
    52  
    53  // DecodeProto implements the corresponding method of serde.Decoder
    54  func (d *YAMLDecoder) DecodeProto(v proto.Message) error {
    55  	return d.DecodeProtoTransform(v, nil)
    56  }
    57  
    58  // DecodeProtoTransform implements the corresponding method of serde.Decoder
    59  func (d *YAMLDecoder) DecodeProtoTransform(v proto.Message, f func(map[string]interface{}) error) error {
    60  	jsonData, err := d.yamlToJSONTransform(f)
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	// parse transformed JSON into 'v'
    66  	decoder := json.NewDecoder(bytes.NewReader(jsonData))
    67  	if err := jsonpb.UnmarshalNext(decoder, v); err != nil {
    68  		return errors.Wrapf(err, "error canonicalizing yaml while parsing to proto")
    69  	}
    70  	return nil
    71  }
    72  
    73  func (d *YAMLDecoder) yamlToJSONTransform(f func(map[string]interface{}) error) ([]byte, error) {
    74  	// deserialize yaml into 'holder'
    75  	holder := map[string]interface{}{}
    76  	if err := d.d.Decode(&holder); err != nil {
    77  		if errors.Is(err, io.EOF) {
    78  			return nil, err
    79  		}
    80  		return nil, errors.Wrapf(err, "could not parse yaml")
    81  	}
    82  
    83  	// transform 'holder' (e.g. stringifying TFJob)
    84  	if f != nil {
    85  		if err := f(holder); err != nil {
    86  			return nil, err
    87  		}
    88  	}
    89  
    90  	// serialize 'holder' to json
    91  	jsonData, err := json.Marshal(holder)
    92  	if err != nil {
    93  		return nil, errors.Wrapf(err, "serialization error while canonicalizing yaml")
    94  	}
    95  	return jsonData, nil
    96  }