Implement cbor.Marshaler and cbor.Unmarshaler for IntOrString.
This commit is contained in:
		@@ -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)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user