Skip to content

Commit

Permalink
Merge pull request kubernetes#89833 from liggitt/json-raw
Browse files Browse the repository at this point in the history
preserve integers decoding raw JSON values
  • Loading branch information
k8s-ci-robot authored Apr 9, 2020
2 parents 0c9245a + 8b91658 commit ec00b4f
Show file tree
Hide file tree
Showing 38 changed files with 241 additions and 87 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ require (
k8s.io/klog v1.0.0
k8s.io/kube-aggregator v0.0.0
k8s.io/kube-controller-manager v0.0.0
k8s.io/kube-openapi v0.0.0-20200401025727-01dfbe2eec3d
k8s.io/kube-openapi v0.0.0-20200403204345-e1beb1bd0f35
k8s.io/kube-proxy v0.0.0
k8s.io/kube-scheduler v0.0.0
k8s.io/kubectl v0.0.0
Expand Down Expand Up @@ -537,7 +537,7 @@ replace (
k8s.io/klog => k8s.io/klog v1.0.0
k8s.io/kube-aggregator => ./staging/src/k8s.io/kube-aggregator
k8s.io/kube-controller-manager => ./staging/src/k8s.io/kube-controller-manager
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20200401025727-01dfbe2eec3d
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20200403204345-e1beb1bd0f35
k8s.io/kube-proxy => ./staging/src/k8s.io/kube-proxy
k8s.io/kube-scheduler => ./staging/src/k8s.io/kube-scheduler
k8s.io/kubectl => ./staging/src/k8s.io/kubectl
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -606,8 +606,8 @@ k8s.io/heapster v1.2.0-beta.1 h1:lUsE/AHOMHpi3MLlBEkaU8Esxm5QhdyCrv1o7ot0s84=
k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20200401025727-01dfbe2eec3d h1:SaMrAnTezfJejw3Y3Ysw9DHTUW2qwW/EzzaTw1xtL74=
k8s.io/kube-openapi v0.0.0-20200401025727-01dfbe2eec3d/go.mod h1:NwPpO8COeh/j9Q9ModsqBxwHcWDo/PmrJOPyquZCC1A=
k8s.io/kube-openapi v0.0.0-20200403204345-e1beb1bd0f35 h1:FDWYFE3itI1G8UFOMjUuLbROZExo+Rrfm/Qaf473rm4=
k8s.io/kube-openapi v0.0.0-20200403204345-e1beb1bd0f35/go.mod h1:NwPpO8COeh/j9Q9ModsqBxwHcWDo/PmrJOPyquZCC1A=
k8s.io/repo-infra v0.0.1-alpha.1 h1:2us1n30u3cOcoPsacNfCvCssS9B9Yldr1ZGOdK0728U=
k8s.io/repo-infra v0.0.1-alpha.1/go.mod h1:wO1t9WaB99V80ljbeENTnayuEEwNZt7gECYh/CEyOJ8=
k8s.io/system-validators v1.1.2 h1:0xzEb0PqnDnUOuf/2E/gaJBOBN7j+qf0LIn12jw3oc4=
Expand Down
2 changes: 1 addition & 1 deletion staging/src/k8s.io/api/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion staging/src/k8s.io/apiextensions-apiserver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ require (
k8s.io/code-generator v0.0.0
k8s.io/component-base v0.0.0
k8s.io/klog v1.0.0
k8s.io/kube-openapi v0.0.0-20200401025727-01dfbe2eec3d
k8s.io/kube-openapi v0.0.0-20200403204345-e1beb1bd0f35
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89
sigs.k8s.io/yaml v1.2.0
)
Expand Down
4 changes: 2 additions & 2 deletions staging/src/k8s.io/apiextensions-apiserver/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ func TestStructuralRoundtrip(t *testing.T) {
f.RandSource(rand.New(rand.NewSource(seed)))
f.Funcs(
func(s *JSON, c fuzz.Continue) {
switch c.Intn(6) {
switch c.Intn(7) {
case 0:
s.Object = float64(42.0)
s.Object = float64(42.2)
case 1:
s.Object = map[string]interface{}{"foo": "bar"}
case 2:
Expand All @@ -51,6 +51,8 @@ func TestStructuralRoundtrip(t *testing.T) {
s.Object = map[string]interface{}{}
case 5:
s.Object = nil
case 6:
s.Object = int64(42)
}
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ var defaultingFixture = &apiextensionsv1.CustomResourceDefinition{
Served: true,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas",
},
},
},
{
Expand All @@ -61,6 +65,10 @@ var defaultingFixture = &apiextensionsv1.CustomResourceDefinition{
Served: false,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas",
},
},
},
},
Expand Down Expand Up @@ -94,6 +102,11 @@ properties:
default: "v1beta1"
v1beta2:
type: string
replicas:
default: 1
format: int32
minimum: 0
type: integer
status:
type: object
properties:
Expand All @@ -110,6 +123,11 @@ properties:
default: "v1beta1"
v1beta2:
type: string
replicas:
default: 0
format: int32
minimum: 0
type: integer
`

const defaultingFooV1beta2Schema = `
Expand All @@ -131,6 +149,11 @@ properties:
v1beta2:
type: string
default: "v1beta2"
replicas:
default: 1
format: int32
minimum: 0
type: integer
status:
type: object
properties:
Expand All @@ -147,6 +170,11 @@ properties:
v1beta2:
type: string
default: "v1beta2"
replicas:
default: 0
format: int32
minimum: 0
type: integer
`

const defaultingFooInstance = `
Expand Down Expand Up @@ -274,15 +302,15 @@ func testDefaulting(t *testing.T, watchCache bool) {
// spec.a and spec.b are defaulted in both versions
// spec.v1beta1 is defaulted when reading the incoming request
// spec.v1beta2 is defaulted when reading the storage response
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"spec", "v1beta1"}, {"spec", "v1beta2"}})
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"spec", "v1beta1"}, {"spec", "v1beta2"}, {"spec", "replicas"}})
mustNotExist(foo.Object, [][]string{{"status"}})

t.Logf("Updating status and expecting 'a' and 'b' to show up.")
unstructured.SetNestedField(foo.Object, map[string]interface{}{}, "status")
if foo, err = fooClient.UpdateStatus(context.TODO(), foo, metav1.UpdateOptions{}); err != nil {
t.Fatal(err)
}
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"status", "a"}, {"status", "b"}})
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"status", "a"}, {"status", "b"}, {"status", "replicas"}})

t.Logf("Add 'c' default to the storage version and wait until GET sees it in both status and spec")
addDefault("v1beta2", "c", "C")
Expand Down
2 changes: 1 addition & 1 deletion staging/src/k8s.io/apimachinery/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ require (
gopkg.in/inf.v0 v0.9.1
gopkg.in/yaml.v2 v2.2.8
k8s.io/klog v1.0.0
k8s.io/kube-openapi v0.0.0-20200401025727-01dfbe2eec3d
k8s.io/kube-openapi v0.0.0-20200403204345-e1beb1bd0f35
sigs.k8s.io/structured-merge-diff/v3 v3.0.0
sigs.k8s.io/yaml v1.2.0
)
Expand Down
4 changes: 2 additions & 2 deletions staging/src/k8s.io/apimachinery/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions staging/src/k8s.io/apimachinery/pkg/util/json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,36 @@ func Unmarshal(data []byte, v interface{}) error {
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
return convertSliceNumbers(*v, 0)

case *interface{}:
// Build a decoder from the given data
decoder := json.NewDecoder(bytes.NewBuffer(data))
// Preserve numbers, rather than casting to float64 automatically
decoder.UseNumber()
// Run the decode
if err := decoder.Decode(v); err != nil {
return err
}
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
return convertInterfaceNumbers(v, 0)

default:
return json.Unmarshal(data, v)
}
}

func convertInterfaceNumbers(v *interface{}, depth int) error {
var err error
switch v2 := (*v).(type) {
case json.Number:
*v, err = convertNumber(v2)
case map[string]interface{}:
err = convertMapNumbers(v2, depth+1)
case []interface{}:
err = convertSliceNumbers(v2, depth+1)
}
return err
}

// convertMapNumbers traverses the map, converting any json.Number values to int64 or float64.
// values which are map[string]interface{} or []interface{} are recursively visited
func convertMapNumbers(m map[string]interface{}, depth int) error {
Expand Down
Loading

0 comments on commit ec00b4f

Please sign in to comment.