Add protobuf preparation objects, guarded by proto tag
This commit is contained in:
		| @@ -83,6 +83,11 @@ import ( | |||||||
| // This format is intended to make it difficult to use these numbers without | // This format is intended to make it difficult to use these numbers without | ||||||
| // writing some sort of special handling code in the hopes that that will | // writing some sort of special handling code in the hopes that that will | ||||||
| // cause implementors to also use a fixed point implementation. | // cause implementors to also use a fixed point implementation. | ||||||
|  | // | ||||||
|  | // +protobuf=true | ||||||
|  | // +protobuf.embed=QuantityProto | ||||||
|  | // +protobuf.options.marshal=false | ||||||
|  | // +protobuf.options.(gogoproto.goproto_stringer)=false | ||||||
| type Quantity struct { | type Quantity struct { | ||||||
| 	// Amount is public, so you can manipulate it if the accessor | 	// Amount is public, so you can manipulate it if the accessor | ||||||
| 	// functions are not sufficient. | 	// functions are not sufficient. | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								pkg/api/resource/quantity_proto.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								pkg/api/resource/quantity_proto.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | // +build proto | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Copyright 2015 The Kubernetes Authors All rights reserved. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package resource | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math/big" | ||||||
|  |  | ||||||
|  | 	"speter.net/go/exp/math/dec/inf" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // QuantityProto is a struct that is equivalent to Quantity, but intended for | ||||||
|  | // protobuf marshalling/unmarshalling. It is generated into a serialization | ||||||
|  | // that matches Quantity. Do not use in Go structs. | ||||||
|  | // | ||||||
|  | // +protobuf=true | ||||||
|  | type QuantityProto struct { | ||||||
|  | 	// The format of the quantity | ||||||
|  | 	Format Format | ||||||
|  | 	// The scale dimension of the value | ||||||
|  | 	Scale int32 | ||||||
|  | 	// Bigint is serialized as a raw bytes array | ||||||
|  | 	Bigint []byte | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ProtoTime returns the Time as a new ProtoTime value. | ||||||
|  | func (q *Quantity) QuantityProto() *QuantityProto { | ||||||
|  | 	if q == nil { | ||||||
|  | 		return &QuantityProto{} | ||||||
|  | 	} | ||||||
|  | 	p := &QuantityProto{ | ||||||
|  | 		Format: q.Format, | ||||||
|  | 	} | ||||||
|  | 	if q.Amount != nil { | ||||||
|  | 		p.Scale = int32(q.Amount.Scale()) | ||||||
|  | 		p.Bigint = q.Amount.UnscaledBig().Bytes() | ||||||
|  | 	} | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Size implements the protobuf marshalling interface. | ||||||
|  | func (q *Quantity) Size() (n int) { return q.QuantityProto().Size() } | ||||||
|  |  | ||||||
|  | // Reset implements the protobuf marshalling interface. | ||||||
|  | func (q *Quantity) Unmarshal(data []byte) error { | ||||||
|  | 	p := QuantityProto{} | ||||||
|  | 	if err := p.Unmarshal(data); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	q.Format = p.Format | ||||||
|  | 	b := big.NewInt(0) | ||||||
|  | 	b.SetBytes(p.Bigint) | ||||||
|  | 	q.Amount = inf.NewDecBig(b, inf.Scale(p.Scale)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Marshal implements the protobuf marshalling interface. | ||||||
|  | func (q *Quantity) Marshal() (data []byte, err error) { | ||||||
|  | 	return q.QuantityProto().Marshal() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MarshalTo implements the protobuf marshalling interface. | ||||||
|  | func (q *Quantity) MarshalTo(data []byte) (int, error) { | ||||||
|  | 	return q.QuantityProto().MarshalTo(data) | ||||||
|  | } | ||||||
							
								
								
									
										95
									
								
								pkg/api/serialization_proto_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								pkg/api/serialization_proto_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | |||||||
|  | // +build proto | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Copyright 2015 The Kubernetes Authors All rights reserved. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package api_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"math/rand" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/gogo/protobuf/proto" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api" | ||||||
|  | 	apitesting "k8s.io/kubernetes/pkg/api/testing" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
|  | 	_ "k8s.io/kubernetes/pkg/apis/extensions" | ||||||
|  | 	_ "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
|  | 	"k8s.io/kubernetes/pkg/runtime" | ||||||
|  | 	"k8s.io/kubernetes/pkg/runtime/protobuf" | ||||||
|  | 	"k8s.io/kubernetes/pkg/util" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	codecsToTest = append(codecsToTest, func(version string, item runtime.Object) (runtime.Codec, error) { | ||||||
|  | 		return protobuf.NewCodec(version, api.Scheme, api.Scheme, api.Scheme), nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestProtobufRoundTrip(t *testing.T) { | ||||||
|  | 	obj := &v1.Pod{} | ||||||
|  | 	apitesting.FuzzerFor(t, "v1", rand.NewSource(benchmarkSeed)).Fuzz(obj) | ||||||
|  | 	data, err := obj.Marshal() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	out := &v1.Pod{} | ||||||
|  | 	if err := out.Unmarshal(data); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if !api.Semantic.Equalities.DeepEqual(out, obj) { | ||||||
|  | 		t.Logf("marshal\n%s", hex.Dump(data)) | ||||||
|  | 		t.Fatalf("Unmarshal is unequal\n%s", util.ObjectGoPrintSideBySide(out, obj)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkEncodeProtobufGeneratedMarshal(b *testing.B) { | ||||||
|  | 	items := benchmarkItems() | ||||||
|  | 	width := len(items) | ||||||
|  | 	b.ResetTimer() | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		if _, err := items[i%width].Marshal(); err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	b.StopTimer() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkDecodeJSON provides a baseline for regular JSON decode performance | ||||||
|  | func BenchmarkDecodeIntoProtobuf(b *testing.B) { | ||||||
|  | 	items := benchmarkItems() | ||||||
|  | 	width := len(items) | ||||||
|  | 	encoded := make([][]byte, width) | ||||||
|  | 	for i := range items { | ||||||
|  | 		data, err := (&items[i]).Marshal() | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 		encoded[i] = data | ||||||
|  | 		validate := &v1.Pod{} | ||||||
|  | 		if err := proto.Unmarshal(data, validate); err != nil { | ||||||
|  | 			b.Fatalf("Failed to unmarshal %d: %v\n%#v", i, err, items[i]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		obj := v1.Pod{} | ||||||
|  | 		if err := proto.Unmarshal(encoded[i%width], &obj); err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -18,29 +18,35 @@ package api_test | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
|  |  | ||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/davecgh/go-spew/spew" | 	"github.com/davecgh/go-spew/spew" | ||||||
|  | 	flag "github.com/spf13/pflag" | ||||||
|  | 	"github.com/ugorji/go/codec" | ||||||
|  |  | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	"k8s.io/kubernetes/pkg/api/meta" | 	"k8s.io/kubernetes/pkg/api/meta" | ||||||
| 	"k8s.io/kubernetes/pkg/api/testapi" | 	"k8s.io/kubernetes/pkg/api/testapi" | ||||||
| 	apitesting "k8s.io/kubernetes/pkg/api/testing" | 	apitesting "k8s.io/kubernetes/pkg/api/testing" | ||||||
| 	"k8s.io/kubernetes/pkg/api/unversioned" | 	"k8s.io/kubernetes/pkg/api/unversioned" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
|  | 	_ "k8s.io/kubernetes/pkg/apis/extensions" | ||||||
|  | 	_ "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
| 	"k8s.io/kubernetes/pkg/runtime" | 	"k8s.io/kubernetes/pkg/runtime" | ||||||
| 	"k8s.io/kubernetes/pkg/util" | 	"k8s.io/kubernetes/pkg/util" | ||||||
| 	"k8s.io/kubernetes/pkg/util/sets" | 	"k8s.io/kubernetes/pkg/util/sets" | ||||||
|  |  | ||||||
| 	_ "k8s.io/kubernetes/pkg/apis/extensions" |  | ||||||
| 	_ "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" |  | ||||||
|  |  | ||||||
| 	flag "github.com/spf13/pflag" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var fuzzIters = flag.Int("fuzz-iters", 20, "How many fuzzing iterations to do.") | var fuzzIters = flag.Int("fuzz-iters", 20, "How many fuzzing iterations to do.") | ||||||
|  |  | ||||||
|  | var codecsToTest = []func(version string, item runtime.Object) (runtime.Codec, error){ | ||||||
|  | 	func(version string, item runtime.Object) (runtime.Codec, error) { | ||||||
|  | 		return testapi.GetCodecForObject(item) | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  |  | ||||||
| func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, seed int64) runtime.Object { | func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, seed int64) runtime.Object { | ||||||
| 	apitesting.FuzzerFor(t, forVersion, rand.NewSource(seed)).Fuzz(item) | 	apitesting.FuzzerFor(t, forVersion, rand.NewSource(seed)).Fuzz(item) | ||||||
|  |  | ||||||
| @@ -92,16 +98,22 @@ func roundTripSame(t *testing.T, item runtime.Object, except ...string) { | |||||||
| 	seed := rand.Int63() | 	seed := rand.Int63() | ||||||
| 	fuzzInternalObject(t, "", item, seed) | 	fuzzInternalObject(t, "", item, seed) | ||||||
|  |  | ||||||
| 	codec, err := testapi.GetCodecForObject(item) | 	version := testapi.Default.VersionUnderTest | ||||||
| 	if err != nil { | 	codecs := []runtime.Codec{} | ||||||
| 		t.Errorf("unexpected error: %v", err) | 	for _, fn := range codecsToTest { | ||||||
| 		return | 		codec, err := fn(version, item) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Errorf("unable to get codec: %v", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		codecs = append(codecs, codec) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	version := testapi.Default.Version() |  | ||||||
| 	if !set.Has(version) { | 	if !set.Has(version) { | ||||||
| 		fuzzInternalObject(t, version, item, seed) | 		fuzzInternalObject(t, version, item, seed) | ||||||
| 		roundTrip(t, codec, item) | 		for _, codec := range codecs { | ||||||
|  | 			roundTrip(t, codec, item) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -163,7 +175,9 @@ func doRoundTripTest(kind string, t *testing.T) { | |||||||
| 	if _, err := meta.TypeAccessor(item); err != nil { | 	if _, err := meta.TypeAccessor(item); err != nil { | ||||||
| 		t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err) | 		t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err) | ||||||
| 	} | 	} | ||||||
| 	roundTripSame(t, item, nonRoundTrippableTypesByVersion[kind]...) | 	if api.Scheme.Recognizes(testapi.Default.VersionUnderTest, kind) { | ||||||
|  | 		roundTripSame(t, item, nonRoundTrippableTypesByVersion[kind]...) | ||||||
|  | 	} | ||||||
| 	if !nonInternalRoundTrippableTypes.Has(kind) { | 	if !nonInternalRoundTrippableTypes.Has(kind) { | ||||||
| 		roundTrip(t, api.Codec, fuzzInternalObject(t, "", item, rand.Int63())) | 		roundTrip(t, api.Codec, fuzzInternalObject(t, "", item, rand.Int63())) | ||||||
| 	} | 	} | ||||||
| @@ -247,54 +261,132 @@ func TestUnversionedTypes(t *testing.T) { | |||||||
|  |  | ||||||
| const benchmarkSeed = 100 | const benchmarkSeed = 100 | ||||||
|  |  | ||||||
| func BenchmarkEncode(b *testing.B) { | func benchmarkItems() []v1.Pod { | ||||||
| 	pod := api.Pod{} |  | ||||||
| 	apiObjectFuzzer := apitesting.FuzzerFor(nil, "", rand.NewSource(benchmarkSeed)) | 	apiObjectFuzzer := apitesting.FuzzerFor(nil, "", rand.NewSource(benchmarkSeed)) | ||||||
| 	apiObjectFuzzer.Fuzz(&pod) | 	items := make([]v1.Pod, 2) | ||||||
| 	for i := 0; i < b.N; i++ { | 	for i := range items { | ||||||
| 		testapi.Default.Codec().Encode(&pod) | 		apiObjectFuzzer.Fuzz(&items[i]) | ||||||
| 	} | 	} | ||||||
|  | 	return items | ||||||
| } | } | ||||||
|  |  | ||||||
| // BenchmarkEncodeJSON provides a baseline for regular JSON encode performance | // BenchmarkEncodeCodec measures the cost of performing a codec encode, which includes | ||||||
| func BenchmarkEncodeJSON(b *testing.B) { | // reflection (to clear APIVersion and Kind) | ||||||
| 	pod := api.Pod{} | func BenchmarkEncodeCodec(b *testing.B) { | ||||||
| 	apiObjectFuzzer := apitesting.FuzzerFor(nil, "", rand.NewSource(benchmarkSeed)) | 	items := benchmarkItems() | ||||||
| 	apiObjectFuzzer.Fuzz(&pod) | 	width := len(items) | ||||||
|  | 	b.ResetTimer() | ||||||
| 	for i := 0; i < b.N; i++ { | 	for i := 0; i < b.N; i++ { | ||||||
| 		json.Marshal(&pod) | 		if _, err := testapi.Default.Codec().Encode(&items[i%width]); err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  | 	b.StopTimer() | ||||||
| } | } | ||||||
|  |  | ||||||
| func BenchmarkDecode(b *testing.B) { | // BenchmarkEncodeJSONMarshal provides a baseline for regular JSON encode performance | ||||||
| 	pod := api.Pod{} | func BenchmarkEncodeJSONMarshal(b *testing.B) { | ||||||
| 	apiObjectFuzzer := apitesting.FuzzerFor(nil, "", rand.NewSource(benchmarkSeed)) | 	items := benchmarkItems() | ||||||
| 	apiObjectFuzzer.Fuzz(&pod) | 	width := len(items) | ||||||
| 	data, _ := testapi.Default.Codec().Encode(&pod) | 	b.ResetTimer() | ||||||
| 	for i := 0; i < b.N; i++ { | 	for i := 0; i < b.N; i++ { | ||||||
| 		testapi.Default.Codec().Decode(data) | 		if _, err := json.Marshal(&items[i%width]); err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  | 	b.StopTimer() | ||||||
| } | } | ||||||
|  |  | ||||||
| func BenchmarkDecodeInto(b *testing.B) { | func BenchmarkDecodeCodec(b *testing.B) { | ||||||
| 	pod := api.Pod{} | 	codec := testapi.Default.Codec() | ||||||
| 	apiObjectFuzzer := apitesting.FuzzerFor(nil, "", rand.NewSource(benchmarkSeed)) | 	items := benchmarkItems() | ||||||
| 	apiObjectFuzzer.Fuzz(&pod) | 	width := len(items) | ||||||
| 	data, _ := testapi.Default.Codec().Encode(&pod) | 	encoded := make([][]byte, width) | ||||||
| 	for i := 0; i < b.N; i++ { | 	for i := range items { | ||||||
| 		obj := api.Pod{} | 		data, err := codec.Encode(&items[i]) | ||||||
| 		testapi.Default.Codec().DecodeInto(data, &obj) | 		if err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 		encoded[i] = data | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	b.ResetTimer() | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		if _, err := codec.Decode(encoded[i%width]); err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	b.StopTimer() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkDecodeIntoCodec(b *testing.B) { | ||||||
|  | 	codec := testapi.Default.Codec() | ||||||
|  | 	items := benchmarkItems() | ||||||
|  | 	width := len(items) | ||||||
|  | 	encoded := make([][]byte, width) | ||||||
|  | 	for i := range items { | ||||||
|  | 		data, err := codec.Encode(&items[i]) | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 		encoded[i] = data | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.ResetTimer() | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		obj := v1.Pod{} | ||||||
|  | 		if err := codec.DecodeInto(encoded[i%width], &obj); err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	b.StopTimer() | ||||||
| } | } | ||||||
|  |  | ||||||
| // BenchmarkDecodeJSON provides a baseline for regular JSON decode performance | // BenchmarkDecodeJSON provides a baseline for regular JSON decode performance | ||||||
| func BenchmarkDecodeJSON(b *testing.B) { | func BenchmarkDecodeIntoJSON(b *testing.B) { | ||||||
| 	pod := api.Pod{} | 	codec := testapi.Default.Codec() | ||||||
| 	apiObjectFuzzer := apitesting.FuzzerFor(nil, "", rand.NewSource(benchmarkSeed)) | 	items := benchmarkItems() | ||||||
| 	apiObjectFuzzer.Fuzz(&pod) | 	width := len(items) | ||||||
| 	data, _ := testapi.Default.Codec().Encode(&pod) | 	encoded := make([][]byte, width) | ||||||
| 	for i := 0; i < b.N; i++ { | 	for i := range items { | ||||||
| 		obj := api.Pod{} | 		data, err := codec.Encode(&items[i]) | ||||||
| 		json.Unmarshal(data, &obj) | 		if err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 		encoded[i] = data | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	b.ResetTimer() | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		obj := v1.Pod{} | ||||||
|  | 		if err := json.Unmarshal(encoded[i%width], &obj); err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	b.StopTimer() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkDecodeJSON provides a baseline for codecgen JSON decode performance | ||||||
|  | func BenchmarkDecodeIntoJSONCodecGen(b *testing.B) { | ||||||
|  | 	kcodec := testapi.Default.Codec() | ||||||
|  | 	items := benchmarkItems() | ||||||
|  | 	width := len(items) | ||||||
|  | 	encoded := make([][]byte, width) | ||||||
|  | 	for i := range items { | ||||||
|  | 		data, err := kcodec.Encode(&items[i]) | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 		encoded[i] = data | ||||||
|  | 	} | ||||||
|  | 	handler := &codec.JsonHandle{} | ||||||
|  |  | ||||||
|  | 	b.ResetTimer() | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		obj := v1.Pod{} | ||||||
|  | 		if err := codec.NewDecoderBytes(encoded[i%width], handler).Decode(&obj); err != nil { | ||||||
|  | 			b.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	b.StopTimer() | ||||||
| } | } | ||||||
|   | |||||||
| @@ -43,5 +43,5 @@ func (d *Duration) UnmarshalJSON(b []byte) error { | |||||||
|  |  | ||||||
| // MarshalJSON implements the json.Marshaler interface. | // MarshalJSON implements the json.Marshaler interface. | ||||||
| func (d Duration) MarshalJSON() ([]byte, error) { | func (d Duration) MarshalJSON() ([]byte, error) { | ||||||
| 	return json.Marshal(d.String()) | 	return json.Marshal(d.Duration.String()) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -40,6 +40,8 @@ func (gvr *GroupVersionResource) String() string { | |||||||
|  |  | ||||||
| // GroupKind specifies a Group and a Kind, but does not force a version.  This is useful for identifying | // GroupKind specifies a Group and a Kind, but does not force a version.  This is useful for identifying | ||||||
| // concepts during lookup stages without having partially valid types | // concepts during lookup stages without having partially valid types | ||||||
|  | // | ||||||
|  | // +protobuf.options.(gogoproto.goproto_stringer)=false | ||||||
| type GroupKind struct { | type GroupKind struct { | ||||||
| 	Group string | 	Group string | ||||||
| 	Kind  string | 	Kind  string | ||||||
| @@ -55,6 +57,8 @@ func (gk *GroupKind) String() string { | |||||||
|  |  | ||||||
| // GroupVersionKind unambiguously identifies a kind.  It doesn't anonymously include GroupVersion | // GroupVersionKind unambiguously identifies a kind.  It doesn't anonymously include GroupVersion | ||||||
| // to avoid automatic coersion.  It doesn't use a GroupVersion to avoid custom marshalling | // to avoid automatic coersion.  It doesn't use a GroupVersion to avoid custom marshalling | ||||||
|  | // | ||||||
|  | // +protobuf.options.(gogoproto.goproto_stringer)=false | ||||||
| type GroupVersionKind struct { | type GroupVersionKind struct { | ||||||
| 	Group   string | 	Group   string | ||||||
| 	Version string | 	Version string | ||||||
| @@ -79,6 +83,8 @@ func (gvk *GroupVersionKind) String() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| // GroupVersion contains the "group" and the "version", which uniquely identifies the API. | // GroupVersion contains the "group" and the "version", which uniquely identifies the API. | ||||||
|  | // | ||||||
|  | // +protobuf.options.(gogoproto.goproto_stringer)=false | ||||||
| type GroupVersion struct { | type GroupVersion struct { | ||||||
| 	Group   string | 	Group   string | ||||||
| 	Version string | 	Version string | ||||||
|   | |||||||
| @@ -26,8 +26,10 @@ import ( | |||||||
| // Time is a wrapper around time.Time which supports correct | // Time is a wrapper around time.Time which supports correct | ||||||
| // marshaling to YAML and JSON.  Wrappers are provided for many | // marshaling to YAML and JSON.  Wrappers are provided for many | ||||||
| // of the factory methods that the time package offers. | // of the factory methods that the time package offers. | ||||||
|  | // | ||||||
|  | // +protobuf.options.marshal=false | ||||||
| type Time struct { | type Time struct { | ||||||
| 	time.Time | 	time.Time `protobuf:"Timestamp,1,req,name=time"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewTime returns a wrapped instance of the provided time | // NewTime returns a wrapped instance of the provided time | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								pkg/api/unversioned/time_proto.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								pkg/api/unversioned/time_proto.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | // +build proto | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Copyright 2015 The Kubernetes Authors All rights reserved. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package unversioned | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ProtoTime is a struct that is equivalent to Time, but intended for | ||||||
|  | // protobuf marshalling/unmarshalling. It is generated into a serialization | ||||||
|  | // that matches Time. Do not use in Go structs. | ||||||
|  | type ProtoTime struct { | ||||||
|  | 	// Represents the time of an event. | ||||||
|  | 	Timestamp Timestamp `json:"timestamp"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Timestamp is a protobuf Timestamp compatible representation of time.Time | ||||||
|  | type Timestamp struct { | ||||||
|  | 	// Represents seconds of UTC time since Unix epoch | ||||||
|  | 	// 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to | ||||||
|  | 	// 9999-12-31T23:59:59Z inclusive. | ||||||
|  | 	Seconds int64 `json:"seconds"` | ||||||
|  | 	// Non-negative fractions of a second at nanosecond resolution. Negative | ||||||
|  | 	// second values with fractions must still have non-negative nanos values | ||||||
|  | 	// that count forward in time. Must be from 0 to 999,999,999 | ||||||
|  | 	// inclusive. | ||||||
|  | 	Nanos int32 `json:"nanos"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ProtoTime returns the Time as a new ProtoTime value. | ||||||
|  | func (m *Time) ProtoTime() *ProtoTime { | ||||||
|  | 	if m == nil { | ||||||
|  | 		return &ProtoTime{} | ||||||
|  | 	} | ||||||
|  | 	return &ProtoTime{ | ||||||
|  | 		Timestamp: Timestamp{ | ||||||
|  | 			Seconds: m.Time.Unix(), | ||||||
|  | 			Nanos:   int32(m.Time.Nanosecond()), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Size implements the protobuf marshalling interface. | ||||||
|  | func (m *Time) Size() (n int) { return m.ProtoTime().Size() } | ||||||
|  |  | ||||||
|  | // Reset implements the protobuf marshalling interface. | ||||||
|  | func (m *Time) Unmarshal(data []byte) error { | ||||||
|  | 	p := ProtoTime{} | ||||||
|  | 	if err := p.Unmarshal(data); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	m.Time = time.Unix(p.Timestamp.Seconds, int64(p.Timestamp.Nanos)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Marshal implements the protobuf marshalling interface. | ||||||
|  | func (m *Time) Marshal() (data []byte, err error) { | ||||||
|  | 	return m.ProtoTime().Marshal() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MarshalTo implements the protobuf marshalling interface. | ||||||
|  | func (m *Time) MarshalTo(data []byte) (int, error) { | ||||||
|  | 	return m.ProtoTime().MarshalTo(data) | ||||||
|  | } | ||||||
| @@ -311,6 +311,8 @@ func (*APIResourceList) IsAnAPIObject() {} | |||||||
|  |  | ||||||
| // APIVersions lists the versions that are available, to allow clients to | // APIVersions lists the versions that are available, to allow clients to | ||||||
| // discover the API at /api, which is the root path of the legacy v1 API. | // discover the API at /api, which is the root path of the legacy v1 API. | ||||||
|  | // | ||||||
|  | // +protobuf.options.(gogoproto.goproto_stringer)=false | ||||||
| type APIVersions struct { | type APIVersions struct { | ||||||
| 	TypeMeta `json:",inline"` | 	TypeMeta `json:",inline"` | ||||||
| 	// versions are the api versions that are available. | 	// versions are the api versions that are available. | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								pkg/runtime/protobuf/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								pkg/runtime/protobuf/doc.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2015 The Kubernetes Authors All rights reserved. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | // Package protobuf implements ProtoBuf serialization and deserialization. | ||||||
|  | package protobuf | ||||||
							
								
								
									
										158
									
								
								pkg/runtime/protobuf/protobuf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								pkg/runtime/protobuf/protobuf.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | |||||||
|  | // +build proto | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Copyright 2015 The Kubernetes Authors All rights reserved. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package protobuf | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"net/url" | ||||||
|  | 	"reflect" | ||||||
|  |  | ||||||
|  | 	"github.com/gogo/protobuf/proto" | ||||||
|  |  | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/unversioned" | ||||||
|  | 	"k8s.io/kubernetes/pkg/runtime" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // NewCodec | ||||||
|  | func NewCodec(version string, creater runtime.ObjectCreater, typer runtime.ObjectTyper, convertor runtime.ObjectConvertor) runtime.Codec { | ||||||
|  | 	return &codec{ | ||||||
|  | 		version:   version, | ||||||
|  | 		creater:   creater, | ||||||
|  | 		typer:     typer, | ||||||
|  | 		convertor: convertor, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // codec decodes protobuf objects | ||||||
|  | type codec struct { | ||||||
|  | 	version       string | ||||||
|  | 	outputVersion string | ||||||
|  | 	creater       runtime.ObjectCreater | ||||||
|  | 	typer         runtime.ObjectTyper | ||||||
|  | 	convertor     runtime.ObjectConvertor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _ runtime.Codec = codec{} | ||||||
|  |  | ||||||
|  | func (c codec) Decode(data []byte) (runtime.Object, error) { | ||||||
|  | 	unknown := &runtime.Unknown{} | ||||||
|  | 	if err := proto.Unmarshal(data, unknown); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	obj, err := c.creater.New(unknown.APIVersion, unknown.Kind) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	pobj, ok := obj.(proto.Message) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj)) | ||||||
|  | 	} | ||||||
|  | 	if err := proto.Unmarshal(unknown.RawJSON, pobj); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if unknown.APIVersion != c.outputVersion { | ||||||
|  | 		out, err := c.convertor.ConvertToVersion(obj, c.outputVersion) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		obj = out | ||||||
|  | 	} | ||||||
|  | 	return obj, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c codec) DecodeToVersion(data []byte, version unversioned.GroupVersion) (runtime.Object, error) { | ||||||
|  | 	return nil, fmt.Errorf("unimplemented") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c codec) DecodeInto(data []byte, obj runtime.Object) error { | ||||||
|  | 	version, kind, err := c.typer.ObjectVersionAndKind(obj) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	unknown := &runtime.Unknown{} | ||||||
|  | 	if err := proto.Unmarshal(data, unknown); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if unknown.APIVersion == version && unknown.Kind == kind { | ||||||
|  | 		pobj, ok := obj.(proto.Message) | ||||||
|  | 		if !ok { | ||||||
|  | 			return fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj)) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return proto.Unmarshal(unknown.RawJSON, pobj) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	versioned, err := c.creater.New(unknown.APIVersion, unknown.Kind) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pobj, ok := versioned.(proto.Message) | ||||||
|  | 	if !ok { | ||||||
|  | 		return fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := proto.Unmarshal(unknown.RawJSON, pobj); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return c.convertor.Convert(versioned, obj) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c codec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, kind unversioned.GroupVersionKind) error { | ||||||
|  | 	return fmt.Errorf("unimplemented") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c codec) DecodeParametersInto(parameters url.Values, obj runtime.Object) error { | ||||||
|  | 	return fmt.Errorf("unimplemented") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c codec) Encode(obj runtime.Object) (data []byte, err error) { | ||||||
|  | 	version, kind, err := c.typer.ObjectVersionAndKind(obj) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(version) == 0 { | ||||||
|  | 		version = c.version | ||||||
|  | 		converted, err := c.convertor.ConvertToVersion(obj, version) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		obj = converted | ||||||
|  | 	} | ||||||
|  | 	m, ok := obj.(proto.Marshaler) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, fmt.Errorf("object %v (kind: %s in version: %s) does not implement ProtoBuf marshalling", reflect.TypeOf(obj), kind, c.version) | ||||||
|  | 	} | ||||||
|  | 	b, err := m.Marshal() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return (&runtime.Unknown{ | ||||||
|  | 		TypeMeta: runtime.TypeMeta{ | ||||||
|  | 			Kind:       kind, | ||||||
|  | 			APIVersion: version, | ||||||
|  | 		}, | ||||||
|  | 		RawJSON: b, | ||||||
|  | 	}).Marshal() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c codec) EncodeToStream(obj runtime.Object, stream io.Writer) error { | ||||||
|  | 	return fmt.Errorf("unimplemented") | ||||||
|  | } | ||||||
| @@ -30,6 +30,7 @@ package runtime | |||||||
| // TypeMeta is provided here for convenience. You may use it directly from this package or define | // TypeMeta is provided here for convenience. You may use it directly from this package or define | ||||||
| // your own with the same fields. | // your own with the same fields. | ||||||
| // | // | ||||||
|  | // +protobuf=true | ||||||
| type TypeMeta struct { | type TypeMeta struct { | ||||||
| 	APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` | 	APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` | ||||||
| 	Kind       string `json:"kind,omitempty" yaml:"kind,omitempty"` | 	Kind       string `json:"kind,omitempty" yaml:"kind,omitempty"` | ||||||
| @@ -98,6 +99,8 @@ type EmbeddedObject struct { | |||||||
| // JSON stored in RawExtension, turning it into the correct object type, and storing it | // JSON stored in RawExtension, turning it into the correct object type, and storing it | ||||||
| // in the EmbeddedObject. (TODO: In the case where the object is of an unknown type, a | // in the EmbeddedObject. (TODO: In the case where the object is of an unknown type, a | ||||||
| // runtime.Unknown object will be created and stored.) | // runtime.Unknown object will be created and stored.) | ||||||
|  | // | ||||||
|  | // +protobuf=true | ||||||
| type RawExtension struct { | type RawExtension struct { | ||||||
| 	RawJSON []byte | 	RawJSON []byte | ||||||
| } | } | ||||||
| @@ -107,6 +110,8 @@ type RawExtension struct { | |||||||
| // TypeMeta features-- kind, version, etc. | // TypeMeta features-- kind, version, etc. | ||||||
| // TODO: Make this object have easy access to field based accessors and settors for | // TODO: Make this object have easy access to field based accessors and settors for | ||||||
| // metadata and field mutatation. | // metadata and field mutatation. | ||||||
|  | // | ||||||
|  | // +protobuf=true | ||||||
| type Unknown struct { | type Unknown struct { | ||||||
| 	TypeMeta `json:",inline"` | 	TypeMeta `json:",inline"` | ||||||
| 	// RawJSON will hold the complete JSON of the object which couldn't be matched | 	// RawJSON will hold the complete JSON of the object which couldn't be matched | ||||||
|   | |||||||
| @@ -29,6 +29,9 @@ import ( | |||||||
| // inner type.  This allows you to have, for example, a JSON field that can | // inner type.  This allows you to have, for example, a JSON field that can | ||||||
| // accept a name or number. | // accept a name or number. | ||||||
| // TODO: Rename to Int32OrString | // TODO: Rename to Int32OrString | ||||||
|  | // | ||||||
|  | // +protobuf=true | ||||||
|  | // +protobuf.options.(gogoproto.goproto_stringer)=false | ||||||
| type IntOrString struct { | type IntOrString struct { | ||||||
| 	Type   Type | 	Type   Type | ||||||
| 	IntVal int32 | 	IntVal int32 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Clayton Coleman
					Clayton Coleman