github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/specs/decode.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package specs
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  
    13  	"github.com/juju/errors"
    14  	k8syaml "k8s.io/apimachinery/pkg/util/yaml"
    15  )
    16  
    17  // YAMLOrJSONDecoder attempts to decode a stream of JSON documents or
    18  // YAML documents by sniffing for a leading { character.
    19  type YAMLOrJSONDecoder struct {
    20  	bufferSize int
    21  	r          io.Reader
    22  	rawData    []byte
    23  
    24  	strict bool
    25  }
    26  
    27  func newStrictYAMLOrJSONDecoder(r io.Reader, bufferSize int) *YAMLOrJSONDecoder {
    28  	return newYAMLOrJSONDecoder(r, bufferSize, true)
    29  }
    30  
    31  func newYAMLOrJSONDecoder(r io.Reader, bufferSize int, strict bool) *YAMLOrJSONDecoder {
    32  	return &YAMLOrJSONDecoder{
    33  		r:          r,
    34  		bufferSize: bufferSize,
    35  		strict:     strict,
    36  	}
    37  }
    38  
    39  func (d *YAMLOrJSONDecoder) jsonify() (err error) {
    40  	buffer := bufio.NewReaderSize(d.r, d.bufferSize)
    41  	rawData, _ := buffer.Peek(d.bufferSize)
    42  	if rawData, err = k8syaml.ToJSON(rawData); err != nil {
    43  		return errors.Trace(err)
    44  	}
    45  	d.r = bytes.NewReader(rawData)
    46  	d.rawData = rawData
    47  	return nil
    48  }
    49  
    50  func (d *YAMLOrJSONDecoder) processError(err error, decoder *json.Decoder) error {
    51  	syntax, ok := err.(*json.SyntaxError)
    52  	if !ok {
    53  		return err
    54  	}
    55  	return k8syaml.JSONSyntaxError{
    56  		Offset: syntax.Offset,
    57  		Err:    fmt.Errorf(syntax.Error()),
    58  	}
    59  }
    60  
    61  // Decode unmarshals the next object from the underlying stream into the
    62  // provide object, or returns an error.
    63  func (d *YAMLOrJSONDecoder) Decode(into interface{}) error {
    64  	if err := d.jsonify(); err != nil {
    65  		return errors.Trace(err)
    66  	}
    67  	logger.Debugf("decoding stream as JSON")
    68  	decoder := json.NewDecoder(d.r)
    69  	decoder.UseNumber()
    70  	if d.strict {
    71  		decoder.DisallowUnknownFields()
    72  	}
    73  
    74  	for decoder.More() {
    75  		if err := decoder.Decode(into); err != nil {
    76  			return d.processError(err, decoder)
    77  		}
    78  	}
    79  
    80  	return nil
    81  }