github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/webhook/json/decode.go (about) 1 /* 2 Copyright 2021 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package json 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "io" 23 ) 24 25 var ( 26 emptyMeta = []byte(`:{}`) 27 metaPrefix = []byte(`{"metadata"`) 28 metaSuffix = []byte(`}`) 29 ) 30 31 var ( 32 // Unmarshal is an alias for json.Unmarshal 33 Unmarshal = json.Unmarshal 34 35 //Marshal is an alias for json.Marshal 36 Marshal = json.Marshal 37 ) 38 39 // Decode will parse the json byte array to the target object. When 40 // unknown fields are _not_ allowed we still accept unknown 41 // fields in the Object's metadata 42 // 43 // See https://github.com/knative/serving/issues/11448 for details 44 func Decode(bites []byte, target interface{}, disallowUnknownFields bool) error { 45 if !disallowUnknownFields { 46 return json.Unmarshal(bites, target) 47 } 48 49 // If we don't allow unknown fields we skip validating fields in the metadata 50 // block since that is opaque to us and validated by the API server 51 start, end, err := findMetadataOffsets(bites) 52 if err != nil { 53 return err 54 } else if start == -1 || end == -1 { 55 // If for some reason the json does not have metadata continue with normal parsing 56 dec := json.NewDecoder(bytes.NewReader(bites)) 57 dec.DisallowUnknownFields() 58 return dec.Decode(target) 59 } 60 61 before := bites[:start] 62 metadata := bites[start:end] 63 after := bites[end:] 64 65 // Parse everything but skip metadata 66 dec := json.NewDecoder(io.MultiReader( 67 bytes.NewReader(before), 68 bytes.NewReader(emptyMeta), 69 bytes.NewReader(after), 70 )) 71 72 dec.DisallowUnknownFields() 73 if err := dec.Decode(target); err != nil { 74 return err 75 } 76 77 // Now we parse just the metadata 78 dec = json.NewDecoder(io.MultiReader( 79 bytes.NewReader(metaPrefix), 80 bytes.NewReader(metadata), 81 bytes.NewReader(metaSuffix), 82 )) 83 84 if err := dec.Decode(target); err != nil { 85 return err 86 } 87 88 return nil 89 } 90 91 func findMetadataOffsets(bites []byte) (start, end int64, err error) { 92 start, end = -1, -1 93 level := 0 94 95 var ( 96 dec = json.NewDecoder(bytes.NewReader(bites)) 97 t json.Token 98 ) 99 100 for { 101 t, err = dec.Token() 102 if err == io.EOF { //nolint 103 break 104 } 105 if err != nil { 106 return 107 } 108 109 switch v := t.(type) { 110 case json.Delim: 111 if v == '{' { 112 level++ 113 } else if v == '}' { 114 level-- 115 } 116 case string: 117 if v == "metadata" && level == 1 { 118 start = dec.InputOffset() 119 x := struct{}{} 120 if err = dec.Decode(&x); err != nil { 121 return -1, -1, err 122 } 123 end = dec.InputOffset() 124 125 // we exit early to stop processing the rest of the object 126 return 127 } 128 } 129 } 130 return -1, -1, nil 131 }