mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
Merge pull request #23265 from AdoHe/big_yaml
Automatic merge from submit-queue use Reader.ReadLine instead of bufio.Scanner to support bigger yaml @smarterclayton ptal. Also refer #19603 #23125 for more details.
This commit is contained in:
commit
7b49d0c19d
@ -45,7 +45,7 @@ func ToJSON(data []byte) ([]byte, error) {
|
|||||||
// separating individual documents. It first converts the YAML
|
// separating individual documents. It first converts the YAML
|
||||||
// body to JSON, then unmarshals the JSON.
|
// body to JSON, then unmarshals the JSON.
|
||||||
type YAMLToJSONDecoder struct {
|
type YAMLToJSONDecoder struct {
|
||||||
scanner *bufio.Scanner
|
reader Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewYAMLToJSONDecoder decodes YAML documents from the provided
|
// NewYAMLToJSONDecoder decodes YAML documents from the provided
|
||||||
@ -53,10 +53,9 @@ type YAMLToJSONDecoder struct {
|
|||||||
// the YAML spec) into its own chunk, converting it to JSON via
|
// the YAML spec) into its own chunk, converting it to JSON via
|
||||||
// yaml.YAMLToJSON, and then passing it to json.Decoder.
|
// yaml.YAMLToJSON, and then passing it to json.Decoder.
|
||||||
func NewYAMLToJSONDecoder(r io.Reader) *YAMLToJSONDecoder {
|
func NewYAMLToJSONDecoder(r io.Reader) *YAMLToJSONDecoder {
|
||||||
scanner := bufio.NewScanner(r)
|
reader := bufio.NewReader(r)
|
||||||
scanner.Split(splitYAMLDocument)
|
|
||||||
return &YAMLToJSONDecoder{
|
return &YAMLToJSONDecoder{
|
||||||
scanner: scanner,
|
reader: NewYAMLReader(reader),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,17 +63,18 @@ func NewYAMLToJSONDecoder(r io.Reader) *YAMLToJSONDecoder {
|
|||||||
// an error. The decoding rules match json.Unmarshal, not
|
// an error. The decoding rules match json.Unmarshal, not
|
||||||
// yaml.Unmarshal.
|
// yaml.Unmarshal.
|
||||||
func (d *YAMLToJSONDecoder) Decode(into interface{}) error {
|
func (d *YAMLToJSONDecoder) Decode(into interface{}) error {
|
||||||
if d.scanner.Scan() {
|
bytes, err := d.reader.Read()
|
||||||
data, err := yaml.YAMLToJSON(d.scanner.Bytes())
|
if err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bytes) != 0 {
|
||||||
|
data, err := yaml.YAMLToJSON(bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return json.Unmarshal(data, into)
|
return json.Unmarshal(data, into)
|
||||||
}
|
}
|
||||||
err := d.scanner.Err()
|
|
||||||
if err == nil {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +137,7 @@ func (d *YAMLDecoder) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const yamlSeparator = "\n---"
|
const yamlSeparator = "\n---"
|
||||||
|
const separator = "---\n"
|
||||||
|
|
||||||
// splitYAMLDocument is a bufio.SplitFunc for splitting YAML streams into individual documents.
|
// splitYAMLDocument is a bufio.SplitFunc for splitting YAML streams into individual documents.
|
||||||
func splitYAMLDocument(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
func splitYAMLDocument(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
@ -222,6 +223,64 @@ func (d *YAMLOrJSONDecoder) Decode(into interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Reader interface {
|
||||||
|
Read() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type YAMLReader struct {
|
||||||
|
reader Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewYAMLReader(r *bufio.Reader) *YAMLReader {
|
||||||
|
return &YAMLReader{
|
||||||
|
reader: &LineReader{reader: r},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read returns a full YAML document.
|
||||||
|
func (r *YAMLReader) Read() ([]byte, error) {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
for {
|
||||||
|
line, err := r.reader.Read()
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(line) == separator || err == io.EOF {
|
||||||
|
if buffer.Len() != 0 {
|
||||||
|
return buffer.Bytes(), nil
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buffer.Write(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type LineReader struct {
|
||||||
|
reader *bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read returns a single line (with '\n' ended) from the underlying reader.
|
||||||
|
// An error is returned iff there is an error with the underlying reader.
|
||||||
|
func (r *LineReader) Read() ([]byte, error) {
|
||||||
|
var (
|
||||||
|
isPrefix bool = true
|
||||||
|
err error = nil
|
||||||
|
line []byte
|
||||||
|
buffer bytes.Buffer
|
||||||
|
)
|
||||||
|
|
||||||
|
for isPrefix && err == nil {
|
||||||
|
line, isPrefix, err = r.reader.ReadLine()
|
||||||
|
buffer.Write(line)
|
||||||
|
}
|
||||||
|
buffer.WriteByte('\n')
|
||||||
|
return buffer.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
// GuessJSONStream scans the provided reader up to size, looking
|
// GuessJSONStream scans the provided reader up to size, looking
|
||||||
// for an open brace indicating this is JSON. It will return the
|
// for an open brace indicating this is JSON. It will return the
|
||||||
// bufio.Reader it creates for the consumer.
|
// bufio.Reader it creates for the consumer.
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -138,8 +140,9 @@ stuff: 1
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error with yaml: prefix, got no error")
|
t.Fatal("expected error with yaml: prefix, got no error")
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(err.Error(), "yaml: line 2:") {
|
fmt.Printf("err: %s", err.Error())
|
||||||
t.Fatalf("expected %q to have 'yaml: line 2:' prefix", err.Error())
|
if !strings.HasPrefix(err.Error(), "yaml: line 1:") {
|
||||||
|
t.Fatalf("expected %q to have 'yaml: line 1:' prefix", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,3 +254,63 @@ func TestYAMLOrJSONDecoder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadSingleLongLine(t *testing.T) {
|
||||||
|
testReadLines(t, []int{128 * 1024})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadRandomLineLengths(t *testing.T) {
|
||||||
|
minLength := 100
|
||||||
|
maxLength := 96 * 1024
|
||||||
|
maxLines := 100
|
||||||
|
|
||||||
|
lineLengths := make([]int, maxLines)
|
||||||
|
for i := 0; i < maxLines; i++ {
|
||||||
|
lineLengths[i] = rand.Intn(maxLength-minLength) + minLength
|
||||||
|
}
|
||||||
|
|
||||||
|
testReadLines(t, lineLengths)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReadLines(t *testing.T, lineLengths []int) {
|
||||||
|
var (
|
||||||
|
lines [][]byte
|
||||||
|
inputStream []byte
|
||||||
|
)
|
||||||
|
for _, lineLength := range lineLengths {
|
||||||
|
inputLine := make([]byte, lineLength+1)
|
||||||
|
for i := 0; i < lineLength; i++ {
|
||||||
|
char := rand.Intn('z'-'A') + 'A'
|
||||||
|
inputLine[i] = byte(char)
|
||||||
|
}
|
||||||
|
inputLine[len(inputLine)-1] = '\n'
|
||||||
|
lines = append(lines, inputLine)
|
||||||
|
}
|
||||||
|
for _, line := range lines {
|
||||||
|
inputStream = append(inputStream, line...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init Reader
|
||||||
|
reader := bufio.NewReader(bytes.NewReader(inputStream))
|
||||||
|
lineReader := &LineReader{reader: reader}
|
||||||
|
|
||||||
|
// read lines
|
||||||
|
var readLines [][]byte
|
||||||
|
for range lines {
|
||||||
|
bytes, err := lineReader.Read()
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
t.Fatalf("failed to read lines: %v", err)
|
||||||
|
}
|
||||||
|
readLines = append(readLines, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate
|
||||||
|
for i := range lines {
|
||||||
|
if len(lines[i]) != len(readLines[i]) {
|
||||||
|
t.Fatalf("expected line length: %d, but got %d", len(lines[i]), len(readLines[i]))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(lines[i], readLines[i]) {
|
||||||
|
t.Fatalf("expected line: %v, but got %v", lines[i], readLines[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user