mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 18:24:07 +00:00
Implement cbor.Marshaler and cbor.Unmarshaler for IntOrString.
This commit is contained in:
parent
ee2c1ffa80
commit
d93a9121b8
@ -25,6 +25,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
cbor "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -92,6 +93,20 @@ func (intstr *IntOrString) UnmarshalJSON(value []byte) error {
|
|||||||
return json.Unmarshal(value, &intstr.IntVal)
|
return json.Unmarshal(value, &intstr.IntVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (intstr *IntOrString) UnmarshalCBOR(value []byte) error {
|
||||||
|
if err := cbor.Unmarshal(value, &intstr.StrVal); err == nil {
|
||||||
|
intstr.Type = String
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cbor.Unmarshal(value, &intstr.IntVal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
intstr.Type = Int
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the string value, or the Itoa of the int value.
|
// String returns the string value, or the Itoa of the int value.
|
||||||
func (intstr *IntOrString) String() string {
|
func (intstr *IntOrString) String() string {
|
||||||
if intstr == nil {
|
if intstr == nil {
|
||||||
@ -126,6 +141,17 @@ func (intstr IntOrString) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (intstr IntOrString) MarshalCBOR() ([]byte, error) {
|
||||||
|
switch intstr.Type {
|
||||||
|
case Int:
|
||||||
|
return cbor.Marshal(intstr.IntVal)
|
||||||
|
case String:
|
||||||
|
return cbor.Marshal(intstr.StrVal)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("impossible IntOrString.Type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OpenAPISchemaType is used by the kube-openapi generator when constructing
|
// OpenAPISchemaType is used by the kube-openapi generator when constructing
|
||||||
// the OpenAPI spec of this type.
|
// the OpenAPI spec of this type.
|
||||||
//
|
//
|
||||||
|
@ -18,10 +18,16 @@ package intstr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
cbor "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
fuzz "github.com/google/gofuzz"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFromInt(t *testing.T) {
|
func TestFromInt(t *testing.T) {
|
||||||
@ -324,3 +330,239 @@ func TestParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMarshalCBOR(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
in IntOrString
|
||||||
|
want []byte
|
||||||
|
assertOnError func(*testing.T, error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
in: IntOrString{Type: 42},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected non-nil error")
|
||||||
|
}
|
||||||
|
const want = "impossible IntOrString.Type"
|
||||||
|
if got := err.Error(); got != want {
|
||||||
|
t.Fatalf("want error message %q, got %q", want, got)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromString(""),
|
||||||
|
want: []byte{0x40},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromString("abc"),
|
||||||
|
want: []byte{0x43, 'a', 'b', 'c'},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(0), // min positive integer representable in one byte
|
||||||
|
want: []byte{0x00},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(23), // max positive integer representable in one byte
|
||||||
|
want: []byte{0x17},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(24), // min positive integer representable in two bytes
|
||||||
|
want: []byte{0x18, 0x18},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(math.MaxUint8), // max positive integer representable in two bytes
|
||||||
|
want: []byte{0x18, 0xff},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(math.MaxUint8 + 1), // min positive integer representable in three bytes
|
||||||
|
want: []byte{0x19, 0x01, 0x00},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(math.MaxUint16), // max positive integer representable in three bytes
|
||||||
|
want: []byte{0x19, 0xff, 0xff},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(math.MaxUint16 + 1), // min positive integer representable in five bytes
|
||||||
|
want: []byte{0x1a, 0x00, 0x01, 0x00, 0x00},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(math.MaxInt32), // max positive integer representable by Go int32
|
||||||
|
want: []byte{0x1a, 0x7f, 0xff, 0xff, 0xff},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(-1), // max negative integer representable in one byte
|
||||||
|
want: []byte{0x20},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(-24), // min negative integer representable in one byte
|
||||||
|
want: []byte{0x37},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(-1 - 24), // max negative integer representable in two bytes
|
||||||
|
want: []byte{0x38, 0x18},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(-1 - math.MaxUint8), // min negative integer representable in two bytes
|
||||||
|
want: []byte{0x38, 0xff},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(-2 - math.MaxUint8), // max negative integer representable in three bytes
|
||||||
|
want: []byte{0x39, 0x01, 0x00},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(-1 - math.MaxUint16), // min negative integer representable in three bytes
|
||||||
|
want: []byte{0x39, 0xff, 0xff},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(-2 - math.MaxUint16), // max negative integer representable in five bytes
|
||||||
|
want: []byte{0x3a, 0x00, 0x01, 0x00, 0x00},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: FromInt32(math.MinInt32), // min negative integer representable by Go int32
|
||||||
|
want: []byte{0x3a, 0x7f, 0xff, 0xff, 0xff},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("{Type:%d,IntVal:%d,StrVal:%q}", tc.in.Type, tc.in.IntVal, tc.in.StrVal), func(t *testing.T) {
|
||||||
|
got, err := tc.in.MarshalCBOR()
|
||||||
|
if tc.assertOnError != nil {
|
||||||
|
tc.assertOnError(t, err)
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(got, tc.want); diff != "" {
|
||||||
|
t.Errorf("unexpected difference between expected and actual output:\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalCBOR(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
in []byte
|
||||||
|
want IntOrString
|
||||||
|
assertOnError func(*testing.T, error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
in: []byte{0xa0}, // {}
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected non-nil error")
|
||||||
|
}
|
||||||
|
const want = "cbor: cannot unmarshal map into Go value of type int32"
|
||||||
|
if got := err.Error(); got != want {
|
||||||
|
t.Fatalf("want error message %q, got %q", want, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x40},
|
||||||
|
want: FromString(""),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x43, 'a', 'b', 'c'},
|
||||||
|
want: FromString("abc"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x00},
|
||||||
|
want: FromInt32(0), // min positive integer representable in one byte
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x17},
|
||||||
|
want: FromInt32(23), // max positive integer representable in one byte
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x18, 0x18},
|
||||||
|
want: FromInt32(24), // min positive integer representable in two bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x18, 0xff},
|
||||||
|
want: FromInt32(math.MaxUint8), // max positive integer representable in two bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x19, 0x01, 0x00},
|
||||||
|
want: FromInt32(math.MaxUint8 + 1), // min positive integer representable in three bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x19, 0xff, 0xff},
|
||||||
|
want: FromInt32(math.MaxUint16), // max positive integer representable in three bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x1a, 0x00, 0x01, 0x00, 0x00},
|
||||||
|
want: FromInt32(math.MaxUint16 + 1), // min positive integer representable in five bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x1a, 0x7f, 0xff, 0xff, 0xff},
|
||||||
|
want: FromInt32(math.MaxInt32), // max positive integer representable by Go int32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x20},
|
||||||
|
want: FromInt32(-1), // max negative integer representable in one byte
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x37},
|
||||||
|
want: FromInt32(-24), // min negative integer representable in one byte
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x38, 0x18},
|
||||||
|
want: FromInt32(-1 - 24), // max negative integer representable in two bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x38, 0xff},
|
||||||
|
want: FromInt32(-1 - math.MaxUint8), // min negative integer representable in two bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x39, 0x01, 0x00},
|
||||||
|
want: FromInt32(-2 - math.MaxUint8), // max negative integer representable in three bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x39, 0xff, 0xff},
|
||||||
|
want: FromInt32(-1 - math.MaxUint16), // min negative integer representable in three bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x3a, 0x00, 0x01, 0x00, 0x00},
|
||||||
|
want: FromInt32(-2 - math.MaxUint16), // max negative integer representable in five bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0x3a, 0x7f, 0xff, 0xff, 0xff},
|
||||||
|
want: FromInt32(math.MinInt32), // min negative integer representable by Go int32
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("{Type:%d,IntVal:%d,StrVal:%q}", tc.want.Type, tc.want.IntVal, tc.want.StrVal), func(t *testing.T) {
|
||||||
|
var got IntOrString
|
||||||
|
err := got.UnmarshalCBOR(tc.in)
|
||||||
|
if tc.assertOnError != nil {
|
||||||
|
tc.assertOnError(t, err)
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(got, tc.want); diff != "" {
|
||||||
|
t.Errorf("unexpected difference between expected and actual output:\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntOrStringRoundtripCBOR(t *testing.T) {
|
||||||
|
fuzzer := fuzz.New()
|
||||||
|
for i := 0; i < 500; i++ {
|
||||||
|
var initial, final IntOrString
|
||||||
|
fuzzer.Fuzz(&initial)
|
||||||
|
b, err := cbor.Marshal(initial)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error encoding %v: %v", initial, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = cbor.Unmarshal(b, &final)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v: error decoding %v: %v", initial, string(b), err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(initial, final); diff != "" {
|
||||||
|
diag, err := cbor.Diagnose(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to produce diagnostic encoding of 0x%x: %v", b, err)
|
||||||
|
}
|
||||||
|
t.Errorf("unexpected diff:\n%s\ncbor: %s", diff, diag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user