blob: 38497ab533e31cbcf177df3a94465e1c840314c0 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package recognizer
18
19import (
20 "bufio"
21 "bytes"
22 "fmt"
23 "io"
24
25 "k8s.io/apimachinery/pkg/runtime"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27)
28
29type RecognizingDecoder interface {
30 runtime.Decoder
31 // RecognizesData should return true if the input provided in the provided reader
32 // belongs to this decoder, or an error if the data could not be read or is ambiguous.
33 // Unknown is true if the data could not be determined to match the decoder type.
34 // Decoders should assume that they can read as much of peek as they need (as the caller
35 // provides) and may return unknown if the data provided is not sufficient to make a
36 // a determination. When peek returns EOF that may mean the end of the input or the
37 // end of buffered input - recognizers should return the best guess at that time.
38 RecognizesData(peek io.Reader) (ok, unknown bool, err error)
39}
40
41// NewDecoder creates a decoder that will attempt multiple decoders in an order defined
42// by:
43//
44// 1. The decoder implements RecognizingDecoder and identifies the data
45// 2. All other decoders, and any decoder that returned true for unknown.
46//
47// The order passed to the constructor is preserved within those priorities.
48func NewDecoder(decoders ...runtime.Decoder) runtime.Decoder {
49 return &decoder{
50 decoders: decoders,
51 }
52}
53
54type decoder struct {
55 decoders []runtime.Decoder
56}
57
58var _ RecognizingDecoder = &decoder{}
59
60func (d *decoder) RecognizesData(peek io.Reader) (bool, bool, error) {
61 var (
62 lastErr error
63 anyUnknown bool
64 )
65 data, _ := bufio.NewReaderSize(peek, 1024).Peek(1024)
66 for _, r := range d.decoders {
67 switch t := r.(type) {
68 case RecognizingDecoder:
69 ok, unknown, err := t.RecognizesData(bytes.NewBuffer(data))
70 if err != nil {
71 lastErr = err
72 continue
73 }
74 anyUnknown = anyUnknown || unknown
75 if !ok {
76 continue
77 }
78 return true, false, nil
79 }
80 }
81 return false, anyUnknown, lastErr
82}
83
84func (d *decoder) Decode(data []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
85 var (
86 lastErr error
87 skipped []runtime.Decoder
88 )
89
90 // try recognizers, record any decoders we need to give a chance later
91 for _, r := range d.decoders {
92 switch t := r.(type) {
93 case RecognizingDecoder:
94 buf := bytes.NewBuffer(data)
95 ok, unknown, err := t.RecognizesData(buf)
96 if err != nil {
97 lastErr = err
98 continue
99 }
100 if unknown {
101 skipped = append(skipped, t)
102 continue
103 }
104 if !ok {
105 continue
106 }
107 return r.Decode(data, gvk, into)
108 default:
109 skipped = append(skipped, t)
110 }
111 }
112
113 // try recognizers that returned unknown or didn't recognize their data
114 for _, r := range skipped {
115 out, actual, err := r.Decode(data, gvk, into)
116 if err != nil {
117 lastErr = err
118 continue
119 }
120 return out, actual, nil
121 }
122
123 if lastErr == nil {
124 lastErr = fmt.Errorf("no serialization format matched the provided data")
125 }
126 return nil, nil, lastErr
127}