mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 01:40:07 +00:00
Add protobuf preparation objects, guarded by proto tag
This commit is contained in:
parent
e2679abdb8
commit
9fea762917
@ -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,17 +98,23 @@ 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
|
||||||
|
codecs := []runtime.Codec{}
|
||||||
|
for _, fn := range codecsToTest {
|
||||||
|
codec, err := fn(version, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unable to get codec: %v", err)
|
||||||
return
|
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)
|
||||||
|
for _, codec := range codecs {
|
||||||
roundTrip(t, codec, item)
|
roundTrip(t, codec, item)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For debugging problems
|
// For debugging problems
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
if api.Scheme.Recognizes(testapi.Default.VersionUnderTest, kind) {
|
||||||
roundTripSame(t, item, nonRoundTrippableTypesByVersion[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
|
||||||
|
Loading…
Reference in New Issue
Block a user