Make runtime less global for Codec

* Make Codec separate from Scheme
* Move EncodeOrDie off Scheme to take a Codec
* Make Copy work without a Codec
* Create a "latest" package that imports all versions and
  sets global defaults for "most recent encoding"
  * v1beta1 is the current "latest", v1beta2 exists
  * Kill DefaultCodec, replace it with "latest.Codec"
  * This updates the client and etcd to store the latest known version
* EmbeddedObject is per schema and per package now
* Move runtime.DefaultScheme to api.Scheme
* Split out WatchEvent since it's not an API object today, treat it
like a special object in api
* Kill DefaultResourceVersioner, instead place it on "latest" (as the
  package that understands all packages)
* Move objDiff to runtime.ObjectDiff
This commit is contained in:
Clayton Coleman 2014-09-11 13:02:53 -04:00
parent 154a91cd33
commit 61e3ce7ddc
58 changed files with 944 additions and 389 deletions

View File

@ -29,6 +29,7 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller" "github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
@ -206,7 +207,7 @@ func runAtomicPutTest(c *client.Client) {
var svc api.Service var svc api.Service
err := c.Post().Path("services").Body( err := c.Post().Path("services").Body(
&api.Service{ &api.Service{
JSONBase: api.JSONBase{ID: "atomicservice", APIVersion: "v1beta1"}, JSONBase: api.JSONBase{ID: "atomicservice", APIVersion: latest.Version},
Port: 12345, Port: 12345,
Labels: map[string]string{ Labels: map[string]string{
"name": "atomicService", "name": "atomicService",

View File

@ -30,6 +30,7 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"

View File

@ -25,7 +25,7 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/golang/glog" "github.com/golang/glog"

55
pkg/api/conversion.go Normal file
View File

@ -0,0 +1,55 @@
/*
Copyright 2014 Google Inc. 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
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
// Codec is the identity codec for this package - it can only convert itself
// to itself.
var Codec = runtime.CodecFor(Scheme, "")
// EmbeddedObject implements a Codec specific version of an
// embedded object.
type EmbeddedObject struct {
runtime.Object
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (a *EmbeddedObject) UnmarshalJSON(b []byte) error {
obj, err := runtime.CodecUnmarshalJSON(Codec, b)
a.Object = obj
return err
}
// MarshalJSON implements the json.Marshaler interface.
func (a EmbeddedObject) MarshalJSON() ([]byte, error) {
return runtime.CodecMarshalJSON(Codec, a.Object)
}
// SetYAML implements the yaml.Setter interface.
func (a *EmbeddedObject) SetYAML(tag string, value interface{}) bool {
obj, ok := runtime.CodecSetYAML(Codec, tag, value)
a.Object = obj
return ok
}
// GetYAML implements the yaml.Getter interface.
func (a EmbeddedObject) GetYAML() (tag string, value interface{}) {
return runtime.CodecGetYAML(Codec, a.Object)
}

20
pkg/api/latest/doc.go Normal file
View File

@ -0,0 +1,20 @@
/*
Copyright 2014 Google Inc. 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 latest defines the default output serializations that code should
// use and imports the required schemas. It also ensures all previously known
// and supported API versions are available for conversion.
package latest

37
pkg/api/latest/latest.go Normal file
View File

@ -0,0 +1,37 @@
/*
Copyright 2014 Google Inc. 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 latest
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
// Version is the string that represents the current external default version
var Version = "v1beta1"
// Codec is the default codec for serializing output that should use
// the latest supported version. Use this Codec when writing to
// disk, a data store that is not dynamically versioned, or in tests.
// This codec can decode any object that Kubernetes is aware of.
var Codec = v1beta1.Codec
// ResourceVersioner describes a default versioner that can handle all types
// of versioning.
// TODO: when versioning changes, make this part of each API definition.
var ResourceVersioner = runtime.NewJSONBaseResourceVersioner()

View File

@ -0,0 +1,146 @@
/*
Copyright 2014 Google Inc. 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 latest
import (
"encoding/json"
"reflect"
"testing"
internal "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/fsouza/go-dockerclient"
"github.com/google/gofuzz"
)
// apiObjectFuzzer can randomly populate api objects.
var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
func(j *internal.JSONBase, c fuzz.Continue) {
// We have to customize the randomization of JSONBases because their
// APIVersion and Kind must remain blank in memory.
j.APIVersion = ""
j.Kind = ""
j.ID = c.RandString()
// TODO: Fix JSON/YAML packages and/or write custom encoding
// for uint64's. Somehow the LS *byte* of this is lost, but
// only when all 8 bytes are set.
j.ResourceVersion = c.RandUint64() >> 8
j.SelfLink = c.RandString()
var sec, nsec int64
c.Fuzz(&sec)
c.Fuzz(&nsec)
j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy()
},
func(intstr *util.IntOrString, c fuzz.Continue) {
// util.IntOrString will panic if its kind is set wrong.
if c.RandBool() {
intstr.Kind = util.IntstrInt
intstr.IntVal = int(c.RandUint64())
intstr.StrVal = ""
} else {
intstr.Kind = util.IntstrString
intstr.IntVal = 0
intstr.StrVal = c.RandString()
}
},
func(u64 *uint64, c fuzz.Continue) {
// TODO: uint64's are NOT handled right.
*u64 = c.RandUint64() >> 8
},
func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) {
// This is necessary because keys with nil values get omitted.
// TODO: Is this a bug?
pb[docker.Port(c.RandString())] = []docker.PortBinding{
{c.RandString(), c.RandString()},
{c.RandString(), c.RandString()},
}
},
func(pm map[string]docker.PortMapping, c fuzz.Continue) {
// This is necessary because keys with nil values get omitted.
// TODO: Is this a bug?
pm[c.RandString()] = docker.PortMapping{
c.RandString(): c.RandString(),
}
},
)
func TestInternalRoundTrip(t *testing.T) {
latest := "v1beta2"
for k, _ := range internal.Scheme.KnownTypes("") {
obj, err := internal.Scheme.New("", k)
if err != nil {
t.Errorf("%s: unexpected error: %v", k, err)
continue
}
apiObjectFuzzer.Fuzz(obj)
newer, err := internal.Scheme.New(latest, k)
if err != nil {
t.Errorf("%s: unexpected error: %v", k, err)
continue
}
if err := internal.Scheme.Convert(obj, newer); err != nil {
t.Errorf("unable to convert %#v to %#v: %v", obj, newer, err)
}
actual, err := internal.Scheme.New("", k)
if err != nil {
t.Errorf("%s: unexpected error: %v", k, err)
continue
}
if err := internal.Scheme.Convert(newer, actual); err != nil {
t.Errorf("unable to convert %#v to %#v: %v", newer, actual, err)
}
if !reflect.DeepEqual(obj, actual) {
t.Errorf("%s: diff %s", k, runtime.ObjectDiff(obj, actual))
}
}
}
func TestResourceVersioner(t *testing.T) {
pod := internal.Pod{JSONBase: internal.JSONBase{ResourceVersion: 10}}
version, err := ResourceVersioner.ResourceVersion(&pod)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if version != 10 {
t.Errorf("unexpected version %d", version)
}
}
func TestCodec(t *testing.T) {
pod := internal.Pod{}
data, err := Codec.Encode(&pod)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
other := internal.Pod{}
if err := json.Unmarshal(data, &other); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if other.APIVersion != Version || other.Kind != "Pod" {
t.Errorf("unexpected unmarshalled object %#v", other)
}
}

View File

@ -20,8 +20,10 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
var Scheme = runtime.NewScheme()
func init() { func init() {
runtime.DefaultScheme.AddKnownTypes("", Scheme.AddKnownTypes("",
&PodList{}, &PodList{},
&Pod{}, &Pod{},
&ReplicationControllerList{}, &ReplicationControllerList{},

View File

@ -17,14 +17,14 @@ limitations under the License.
package api_test package api_test
import ( import (
"encoding/json"
"flag" "flag"
"fmt"
"reflect" "reflect"
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/fsouza/go-dockerclient" "github.com/fsouza/go-dockerclient"
@ -105,27 +105,7 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
}, },
) )
func objDiff(a, b runtime.Object) string { func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
ab, err := json.Marshal(a)
if err != nil {
panic("a")
}
bb, err := json.Marshal(b)
if err != nil {
panic("b")
}
return util.StringDiff(string(ab), string(bb))
// An alternate diff attempt, in case json isn't showing you
// the difference. (reflect.DeepEqual makes a distinction between
// nil and empty slices, for example.)
return util.StringDiff(
fmt.Sprintf("%#v", a),
fmt.Sprintf("%#v", b),
)
}
func runTest(t *testing.T, source runtime.Object) {
name := reflect.TypeOf(source).Elem().Name() name := reflect.TypeOf(source).Elem().Name()
apiObjectFuzzer.Fuzz(source) apiObjectFuzzer.Fuzz(source)
j, err := runtime.FindJSONBase(source) j, err := runtime.FindJSONBase(source)
@ -135,30 +115,30 @@ func runTest(t *testing.T, source runtime.Object) {
j.SetKind("") j.SetKind("")
j.SetAPIVersion("") j.SetAPIVersion("")
data, err := latest.Codec.Encode(source) data, err := codec.Encode(source)
if err != nil { if err != nil {
t.Errorf("%v: %v (%#v)", name, err, source) t.Errorf("%v: %v (%#v)", name, err, source)
return return
} }
obj2, err := latest.Codec.Decode(data) obj2, err := codec.Decode(data)
if err != nil { if err != nil {
t.Errorf("%v: %v", name, err) t.Errorf("%v: %v", name, err)
return return
} else { } else {
if !reflect.DeepEqual(source, obj2) { if !reflect.DeepEqual(source, obj2) {
t.Errorf("1: %v: diff: %v", name, objDiff(source, obj2)) t.Errorf("1: %v: diff: %v", name, runtime.ObjectDiff(source, obj2))
return return
} }
} }
obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface().(runtime.Object) obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface().(runtime.Object)
err = latest.Codec.DecodeInto(data, obj3) err = codec.DecodeInto(data, obj3)
if err != nil { if err != nil {
t.Errorf("2: %v: %v", name, err) t.Errorf("2: %v: %v", name, err)
return return
} else { } else {
if !reflect.DeepEqual(source, obj3) { if !reflect.DeepEqual(source, obj3) {
t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3)) t.Errorf("3: %v: diff: %v", name, runtime.ObjectDiff(source, obj3))
return return
} }
} }
@ -184,7 +164,8 @@ func TestTypes(t *testing.T) {
for _, item := range table { for _, item := range table {
// Try a few times, since runTest uses random values. // Try a few times, since runTest uses random values.
for i := 0; i < *fuzzIters; i++ { for i := 0; i < *fuzzIters; i++ {
runTest(t, item) runTest(t, v1beta1.Codec, item)
runTest(t, v1beta2.Codec, item)
} }
} }
} }

View File

@ -17,9 +17,7 @@ limitations under the License.
package api package api
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/fsouza/go-dockerclient" "github.com/fsouza/go-dockerclient"
) )
@ -623,13 +621,3 @@ type ServerOpList struct {
} }
func (*ServerOpList) IsAnAPIObject() {} func (*ServerOpList) IsAnAPIObject() {}
// WatchEvent objects are streamed from the api server in response to a watch request.
type WatchEvent struct {
// The type of the watch event; added, modified, or deleted.
Type watch.EventType
// For added or modified objects, this is the new object; for deleted objects,
// it's the state of the object immediately prior to its deletion.
Object runtime.EmbeddedObject
}

View File

@ -17,14 +17,13 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
// Alias this so it can be easily changed when we cut the next version.
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api" newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
func init() { func init() {
runtime.DefaultScheme.AddConversionFuncs( newer.Scheme.AddConversionFuncs(
// EnvVar's Key is deprecated in favor of Name. // EnvVar's Key is deprecated in favor of Name.
func(in *newer.EnvVar, out *EnvVar, s conversion.Scope) error { func(in *newer.EnvVar, out *EnvVar, s conversion.Scope) error {
out.Value = in.Value out.Value = in.Value
@ -81,3 +80,33 @@ func init() {
) )
} }
// EmbeddedObject implements a Codec specific version of an
// embedded object.
type EmbeddedObject struct {
runtime.Object
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (a *EmbeddedObject) UnmarshalJSON(b []byte) error {
obj, err := runtime.CodecUnmarshalJSON(Codec, b)
a.Object = obj
return err
}
// MarshalJSON implements the json.Marshaler interface.
func (a EmbeddedObject) MarshalJSON() ([]byte, error) {
return runtime.CodecMarshalJSON(Codec, a.Object)
}
// SetYAML implements the yaml.Setter interface.
func (a *EmbeddedObject) SetYAML(tag string, value interface{}) bool {
obj, ok := runtime.CodecSetYAML(Codec, tag, value)
a.Object = obj
return ok
}
// GetYAML implements the yaml.Getter interface.
func (a EmbeddedObject) GetYAML() (tag string, value interface{}) {
return runtime.CodecGetYAML(Codec, a.Object)
}

View File

@ -22,10 +22,9 @@ import (
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api" newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
var Convert = runtime.DefaultScheme.Convert var Convert = newer.Scheme.Convert
func TestEnvConversion(t *testing.T) { func TestEnvConversion(t *testing.T) {
nonCanonical := []v1beta1.EnvVar{ nonCanonical := []v1beta1.EnvVar{

View File

@ -17,11 +17,15 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
// Codec encodes internal objects to the v1beta1 scheme
var Codec = runtime.CodecFor(api.Scheme, "v1beta1")
func init() { func init() {
runtime.DefaultScheme.AddKnownTypes("v1beta1", api.Scheme.AddKnownTypes("v1beta1",
&PodList{}, &PodList{},
&Pod{}, &Pod{},
&ReplicationControllerList{}, &ReplicationControllerList{},

View File

@ -17,9 +17,7 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/fsouza/go-dockerclient" "github.com/fsouza/go-dockerclient"
) )
@ -625,13 +623,3 @@ type ServerOpList struct {
} }
func (*ServerOpList) IsAnAPIObject() {} func (*ServerOpList) IsAnAPIObject() {}
// WatchEvent objects are streamed from the api server in response to a watch request.
type WatchEvent struct {
// The type of the watch event; added, modified, or deleted.
Type watch.EventType
// For added or modified objects, this is the new object; for deleted objects,
// it's the state of the object immediately prior to its deletion.
Object runtime.EmbeddedObject
}

View File

@ -17,11 +17,67 @@ limitations under the License.
package v1beta2 package v1beta2
import ( import (
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
func init() { func init() {
runtime.DefaultScheme.AddConversionFuncs() newer.Scheme.AddConversionFuncs(
// EnvVar's Key is deprecated in favor of Name.
func(in *newer.EnvVar, out *EnvVar, s conversion.Scope) error {
out.Value = in.Value
out.Key = in.Name
out.Name = in.Name
return nil
},
func(in *EnvVar, out *newer.EnvVar, s conversion.Scope) error {
out.Value = in.Value
if in.Name != "" {
out.Name = in.Name
} else {
out.Name = in.Key
}
return nil
},
// Path & MountType are deprecated.
func(in *newer.VolumeMount, out *VolumeMount, s conversion.Scope) error {
out.Name = in.Name
out.ReadOnly = in.ReadOnly
out.MountPath = in.MountPath
out.Path = in.MountPath
out.MountType = "" // MountType is ignored.
return nil
},
func(in *VolumeMount, out *newer.VolumeMount, s conversion.Scope) error {
out.Name = in.Name
out.ReadOnly = in.ReadOnly
if in.MountPath == "" {
out.MountPath = in.Path
} else {
out.MountPath = in.MountPath
}
return nil
},
// MinionList.Items had a wrong name in v1beta1
func(in *newer.MinionList, out *MinionList, s conversion.Scope) error {
s.Convert(&in.JSONBase, &out.JSONBase, 0)
s.Convert(&in.Items, &out.Items, 0)
out.Minions = out.Items
return nil
},
func(in *MinionList, out *newer.MinionList, s conversion.Scope) error {
s.Convert(&in.JSONBase, &out.JSONBase, 0)
if len(in.Items) == 0 {
s.Convert(&in.Minions, &out.Items, 0)
} else {
s.Convert(&in.Items, &out.Items, 0)
}
return nil
},
)
} }
// EmbeddedObject implements a Codec specific version of an // EmbeddedObject implements a Codec specific version of an

View File

@ -17,14 +17,15 @@ limitations under the License.
package v1beta2 package v1beta2
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
// Codec encodes runtime objects to the v1beta2 scheme // Codec encodes internal objects to the v1beta2 scheme
var Codec = runtime.CodecFor(runtime.DefaultScheme, "v1beta2") var Codec = runtime.CodecFor(api.Scheme, "v1beta2")
func init() { func init() {
runtime.DefaultScheme.AddKnownTypes("v1beta2", api.Scheme.AddKnownTypes("v1beta2",
&PodList{}, &PodList{},
&Pod{}, &Pod{},
&ReplicationControllerList{}, &ReplicationControllerList{},

View File

@ -17,9 +17,7 @@ limitations under the License.
package v1beta2 package v1beta2
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/fsouza/go-dockerclient" "github.com/fsouza/go-dockerclient"
) )
@ -55,8 +53,8 @@ type ContainerManifest struct {
// Required: This must be a DNS_SUBDOMAIN. // Required: This must be a DNS_SUBDOMAIN.
// TODO: ID on Manifest is deprecated and will be removed in the future. // TODO: ID on Manifest is deprecated and will be removed in the future.
ID string `yaml:"id" json:"id"` ID string `yaml:"id" json:"id"`
// TODO: UUID on Manifext is deprecated in the future once we are done // TODO: UUID on Manifest is deprecated in the future once we are done
// with the API refactory. It is required for now to determine the instance // with the API refactoring. It is required for now to determine the instance
// of a Pod. // of a Pod.
UUID string `yaml:"uuid,omitempty" json:"uuid,omitempty"` UUID string `yaml:"uuid,omitempty" json:"uuid,omitempty"`
Volumes []Volume `yaml:"volumes" json:"volumes"` Volumes []Volume `yaml:"volumes" json:"volumes"`
@ -166,7 +164,6 @@ type ExecAction struct {
// command is root ('/') in the container's filesystem. The command is simply exec'd, it is // command is root ('/') in the container's filesystem. The command is simply exec'd, it is
// not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use // not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use
// a shell, you need to explicitly call out to that shell. // a shell, you need to explicitly call out to that shell.
// A return code of zero is treated as 'Healthy', non-zero is 'Unhealthy'
Command []string `yaml:"command,omitempty" json:"command,omitempty"` Command []string `yaml:"command,omitempty" json:"command,omitempty"`
} }
@ -210,7 +207,6 @@ type Container struct {
} }
// Handler defines a specific action that should be taken // Handler defines a specific action that should be taken
// TODO: merge this with liveness probing?
// TODO: pass structured data to these actions, and document that data here. // TODO: pass structured data to these actions, and document that data here.
type Handler struct { type Handler struct {
// One and only one of the following should be specified. // One and only one of the following should be specified.
@ -252,8 +248,6 @@ type JSONBase struct {
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
} }
func (*JSONBase) IsAnAPIObject() {}
// PodStatus represents a status of a pod. // PodStatus represents a status of a pod.
type PodStatus string type PodStatus string
@ -303,18 +297,19 @@ type ContainerStatus struct {
} }
// PodInfo contains one entry for every container with available info. // PodInfo contains one entry for every container with available info.
// TODO(dchen1107): Replace docker.Container below with ContainerStatus defined above.
type PodInfo map[string]docker.Container type PodInfo map[string]docker.Container
type RestartPolicyAlways struct{} type RestartPolicyAlways struct{}
// TODO(dchen1107): Define what kinds of failures should restart // TODO(dchen1107): Define what kinds of failures should restart.
// TODO(dchen1107): Decide whether to support policy knobs, and, if so, which ones. // TODO(dchen1107): Decide whether to support policy knobs, and, if so, which ones.
type RestartPolicyOnFailure struct{} type RestartPolicyOnFailure struct{}
type RestartPolicyNever struct{} type RestartPolicyNever struct{}
type RestartPolicy struct { type RestartPolicy struct {
// Only one of the following restart policy may be specified. // Only one of the following restart policies may be specified.
// If none of the following policies is specified, the default one // If none of the following policies is specified, the default one
// is RestartPolicyAlways. // is RestartPolicyAlways.
Always *RestartPolicyAlways `json:"always,omitempty" yaml:"always,omitempty"` Always *RestartPolicyAlways `json:"always,omitempty" yaml:"always,omitempty"`
@ -333,9 +328,9 @@ type PodState struct {
// The key of this map is the *name* of the container within the manifest; it has one // The key of this map is the *name* of the container within the manifest; it has one
// entry per container in the manifest. The value of this map is currently the output // entry per container in the manifest. The value of this map is currently the output
// of `docker inspect`. This output format is *not* final and should not be relied // of `docker inspect`. This output format is *not* final and should not be relied
// upon. To allow marshalling/unmarshalling, we copied the client's structs and added // upon.
// json/yaml tags. // TODO: Make real decisions about what our info should look like. Re-enable fuzz test
// TODO: Make real decisions about what our info should look like. // when we have done this.
Info PodInfo `json:"info,omitempty" yaml:"info,omitempty"` Info PodInfo `json:"info,omitempty" yaml:"info,omitempty"`
} }
@ -550,14 +545,14 @@ const (
// resource. // resource.
// "id" string - the identifier of the missing resource // "id" string - the identifier of the missing resource
// Status code 404 // Status code 404
StatusReasonNotFound StatusReason = "notFound" StatusReasonNotFound StatusReason = "not_found"
// StatusReasonAlreadyExists means the resource you are creating already exists. // StatusReasonAlreadyExists means the resource you are creating already exists.
// Details (optional): // Details (optional):
// "kind" string - the kind attribute of the conflicting resource // "kind" string - the kind attribute of the conflicting resource
// "id" string - the identifier of the conflicting resource // "id" string - the identifier of the conflicting resource
// Status code 409 // Status code 409
StatusReasonAlreadyExists StatusReason = "alreadyExists" StatusReasonAlreadyExists StatusReason = "already_exists"
// StatusReasonConflict means the requested update operation cannot be completed // StatusReasonConflict means the requested update operation cannot be completed
// due to a conflict in the operation. The client may need to alter the request. // due to a conflict in the operation. The client may need to alter the request.
@ -565,6 +560,19 @@ const (
// conflict. // conflict.
// Status code 409 // Status code 409
StatusReasonConflict StatusReason = "conflict" StatusReasonConflict StatusReason = "conflict"
// StatusReasonInvalid means the requested create or update operation cannot be
// completed due to invalid data provided as part of the request. The client may
// need to alter the request. When set, the client may use the StatusDetails
// message field as a summary of the issues encountered.
// Details (optional):
// "kind" string - the kind attribute of the invalid resource
// "id" string - the identifier of the invalid resource
// "causes" - one or more StatusCause entries indicating the data in the
// provided resource that was invalid. The code, message, and
// field attributes will be set.
// Status code 422
StatusReasonInvalid StatusReason = "invalid"
) )
// StatusCause provides more information about an api.Status failure, including // StatusCause provides more information about an api.Status failure, including
@ -625,13 +633,3 @@ type ServerOpList struct {
} }
func (*ServerOpList) IsAnAPIObject() {} func (*ServerOpList) IsAnAPIObject() {}
// WatchEvent objects are streamed from the api server in response to a watch request.
type WatchEvent struct {
// The type of the watch event; added, modified, or deleted.
Type watch.EventType
// For added or modified objects, this is the new object; for deleted objects,
// it's the state of the object immediately prior to its deletion.
Object runtime.EmbeddedObject
}

View File

@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package v1beta1 package v1beta3
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/fsouza/go-dockerclient" "github.com/fsouza/go-dockerclient"
) )
@ -55,8 +53,8 @@ type ContainerManifest struct {
// Required: This must be a DNS_SUBDOMAIN. // Required: This must be a DNS_SUBDOMAIN.
// TODO: ID on Manifest is deprecated and will be removed in the future. // TODO: ID on Manifest is deprecated and will be removed in the future.
ID string `yaml:"id" json:"id"` ID string `yaml:"id" json:"id"`
// TODO: UUID on Manifext is deprecated in the future once we are done // TODO: UUID on Manifest is deprecated in the future once we are done
// with the API refactory. It is required for now to determine the instance // with the API refactoring. It is required for now to determine the instance
// of a Pod. // of a Pod.
UUID string `yaml:"uuid,omitempty" json:"uuid,omitempty"` UUID string `yaml:"uuid,omitempty" json:"uuid,omitempty"`
Volumes []Volume `yaml:"volumes" json:"volumes"` Volumes []Volume `yaml:"volumes" json:"volumes"`
@ -124,22 +122,13 @@ type VolumeMount struct {
// Optional: Defaults to false (read-write). // Optional: Defaults to false (read-write).
ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"` ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"`
// Required. // Required.
// Exactly one of the following must be set. If both are set, prefer MountPath.
// DEPRECATED: Path will be removed in a future version of the API.
MountPath string `yaml:"mountPath,omitempty" json:"mountPath,omitempty"` MountPath string `yaml:"mountPath,omitempty" json:"mountPath,omitempty"`
Path string `yaml:"path,omitempty" json:"path,omitempty"`
// One of: "LOCAL" (local volume) or "HOST" (external mount from the host). Default: LOCAL.
// DEPRECATED: MountType will be removed in a future version of the API.
MountType string `yaml:"mountType,omitempty" json:"mountType,omitempty"`
} }
// EnvVar represents an environment variable present in a Container. // EnvVar represents an environment variable present in a Container.
type EnvVar struct { type EnvVar struct {
// Required: This must be a C_IDENTIFIER. // Required: This must be a C_IDENTIFIER.
// Exactly one of the following must be set. If both are set, prefer Name.
// DEPRECATED: EnvVar.Key will be removed in a future version of the API.
Name string `yaml:"name" json:"name"` Name string `yaml:"name" json:"name"`
Key string `yaml:"key,omitempty" json:"key,omitempty"`
// Optional: defaults to "". // Optional: defaults to "".
Value string `yaml:"value,omitempty" json:"value,omitempty"` Value string `yaml:"value,omitempty" json:"value,omitempty"`
} }
@ -166,7 +155,6 @@ type ExecAction struct {
// command is root ('/') in the container's filesystem. The command is simply exec'd, it is // command is root ('/') in the container's filesystem. The command is simply exec'd, it is
// not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use // not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use
// a shell, you need to explicitly call out to that shell. // a shell, you need to explicitly call out to that shell.
// A return code of zero is treated as 'Healthy', non-zero is 'Unhealthy'
Command []string `yaml:"command,omitempty" json:"command,omitempty"` Command []string `yaml:"command,omitempty" json:"command,omitempty"`
} }
@ -210,7 +198,6 @@ type Container struct {
} }
// Handler defines a specific action that should be taken // Handler defines a specific action that should be taken
// TODO: merge this with liveness probing?
// TODO: pass structured data to these actions, and document that data here. // TODO: pass structured data to these actions, and document that data here.
type Handler struct { type Handler struct {
// One and only one of the following should be specified. // One and only one of the following should be specified.
@ -252,8 +239,6 @@ type JSONBase struct {
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
} }
func (*JSONBase) IsAnAPIObject() {}
// PodStatus represents a status of a pod. // PodStatus represents a status of a pod.
type PodStatus string type PodStatus string
@ -303,18 +288,19 @@ type ContainerStatus struct {
} }
// PodInfo contains one entry for every container with available info. // PodInfo contains one entry for every container with available info.
// TODO(dchen1107): Replace docker.Container below with ContainerStatus defined above.
type PodInfo map[string]docker.Container type PodInfo map[string]docker.Container
type RestartPolicyAlways struct{} type RestartPolicyAlways struct{}
// TODO(dchen1107): Define what kinds of failures should restart // TODO(dchen1107): Define what kinds of failures should restart.
// TODO(dchen1107): Decide whether to support policy knobs, and, if so, which ones. // TODO(dchen1107): Decide whether to support policy knobs, and, if so, which ones.
type RestartPolicyOnFailure struct{} type RestartPolicyOnFailure struct{}
type RestartPolicyNever struct{} type RestartPolicyNever struct{}
type RestartPolicy struct { type RestartPolicy struct {
// Only one of the following restart policy may be specified. // Only one of the following restart policies may be specified.
// If none of the following policies is specified, the default one // If none of the following policies is specified, the default one
// is RestartPolicyAlways. // is RestartPolicyAlways.
Always *RestartPolicyAlways `json:"always,omitempty" yaml:"always,omitempty"` Always *RestartPolicyAlways `json:"always,omitempty" yaml:"always,omitempty"`
@ -333,9 +319,9 @@ type PodState struct {
// The key of this map is the *name* of the container within the manifest; it has one // The key of this map is the *name* of the container within the manifest; it has one
// entry per container in the manifest. The value of this map is currently the output // entry per container in the manifest. The value of this map is currently the output
// of `docker inspect`. This output format is *not* final and should not be relied // of `docker inspect`. This output format is *not* final and should not be relied
// upon. To allow marshalling/unmarshalling, we copied the client's structs and added // upon.
// json/yaml tags. // TODO: Make real decisions about what our info should look like. Re-enable fuzz test
// TODO: Make real decisions about what our info should look like. // when we have done this.
Info PodInfo `json:"info,omitempty" yaml:"info,omitempty"` Info PodInfo `json:"info,omitempty" yaml:"info,omitempty"`
} }
@ -451,9 +437,6 @@ func (*Minion) IsAnAPIObject() {}
// MinionList is a list of minions. // MinionList is a list of minions.
type MinionList struct { type MinionList struct {
JSONBase `json:",inline" yaml:",inline"` JSONBase `json:",inline" yaml:",inline"`
// DEPRECATED: the below Minions is due to a naming mistake and
// will be replaced with Items in the future.
Minions []Minion `json:"minions,omitempty" yaml:"minions,omitempty"`
Items []Minion `json:"items,omitempty" yaml:"items,omitempty"` Items []Minion `json:"items,omitempty" yaml:"items,omitempty"`
} }
@ -550,14 +533,14 @@ const (
// resource. // resource.
// "id" string - the identifier of the missing resource // "id" string - the identifier of the missing resource
// Status code 404 // Status code 404
StatusReasonNotFound StatusReason = "notFound" StatusReasonNotFound StatusReason = "not_found"
// StatusReasonAlreadyExists means the resource you are creating already exists. // StatusReasonAlreadyExists means the resource you are creating already exists.
// Details (optional): // Details (optional):
// "kind" string - the kind attribute of the conflicting resource // "kind" string - the kind attribute of the conflicting resource
// "id" string - the identifier of the conflicting resource // "id" string - the identifier of the conflicting resource
// Status code 409 // Status code 409
StatusReasonAlreadyExists StatusReason = "alreadyExists" StatusReasonAlreadyExists StatusReason = "already_exists"
// StatusReasonConflict means the requested update operation cannot be completed // StatusReasonConflict means the requested update operation cannot be completed
// due to a conflict in the operation. The client may need to alter the request. // due to a conflict in the operation. The client may need to alter the request.
@ -565,6 +548,19 @@ const (
// conflict. // conflict.
// Status code 409 // Status code 409
StatusReasonConflict StatusReason = "conflict" StatusReasonConflict StatusReason = "conflict"
// StatusReasonInvalid means the requested create or update operation cannot be
// completed due to invalid data provided as part of the request. The client may
// need to alter the request. When set, the client may use the StatusDetails
// message field as a summary of the issues encountered.
// Details (optional):
// "kind" string - the kind attribute of the invalid resource
// "id" string - the identifier of the invalid resource
// "causes" - one or more StatusCause entries indicating the data in the
// provided resource that was invalid. The code, message, and
// field attributes will be set.
// Status code 422
StatusReasonInvalid StatusReason = "invalid"
) )
// StatusCause provides more information about an api.Status failure, including // StatusCause provides more information about an api.Status failure, including
@ -625,13 +621,3 @@ type ServerOpList struct {
} }
func (*ServerOpList) IsAnAPIObject() {} func (*ServerOpList) IsAnAPIObject() {}
// WatchEvent objects are streamed from the api server in response to a watch request.
type WatchEvent struct {
// The type of the watch event; added, modified, or deleted.
Type watch.EventType
// For added or modified objects, this is the new object; for deleted objects,
// it's the state of the object immediately prior to its deletion.
Object runtime.EmbeddedObject
}

57
pkg/api/watch.go Normal file
View File

@ -0,0 +1,57 @@
/*
Copyright 2014 Google Inc. 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
import (
"encoding/json"
"fmt"
"reflect"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
// WatchEvent objects are streamed from the api server in response to a watch request.
// These are not API objects and are unversioned today.
type WatchEvent struct {
// The type of the watch event; added, modified, or deleted.
Type watch.EventType
// For added or modified objects, this is the new object; for deleted objects,
// it's the state of the object immediately prior to its deletion.
Object EmbeddedObject
}
// watchSerialization defines the JSON wire equivalent of watch.Event
type watchSerialization struct {
Type watch.EventType
Object json.RawMessage
}
// NewJSONWatcHEvent returns an object that will serialize to JSON and back
// to a WatchEvent.
func NewJSONWatchEvent(codec runtime.Codec, event watch.Event) (interface{}, error) {
obj, ok := event.Object.(runtime.Object)
if !ok {
return nil, fmt.Errorf("The event object cannot be safely converted to JSON: %v", reflect.TypeOf(event.Object).Name())
}
data, err := codec.Encode(obj)
if err != nil {
return nil, err
}
return &watchSerialization{event.Type, json.RawMessage(data)}, nil
}

43
pkg/api/watch_test.go Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright 2014 Google Inc. 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
import (
"encoding/json"
"reflect"
"testing"
)
func TestEmbeddedDefaultSerialization(t *testing.T) {
expected := WatchEvent{
Type: "foo",
Object: EmbeddedObject{&Pod{}},
}
data, err := json.Marshal(expected)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
actual := WatchEvent{}
if err := json.Unmarshal(data, &actual); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Expected %#v, Got %#v", expected, actual)
}
}

View File

@ -32,6 +32,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@ -46,8 +47,8 @@ func convert(obj runtime.Object) (runtime.Object, error) {
var codec = latest.Codec var codec = latest.Codec
func init() { func init() {
latest.Codec.AddKnownTypes("", &Simple{}, &SimpleList{}) api.Scheme.AddKnownTypes("", &Simple{}, &SimpleList{})
latest.Codec.AddKnownTypes("v1beta1", &Simple{}, &SimpleList{}) api.Scheme.AddKnownTypes(latest.Version, &Simple{}, &SimpleList{})
} }
type Simple struct { type Simple struct {
@ -95,7 +96,7 @@ func (storage *SimpleRESTStorage) List(labels.Selector) (runtime.Object, error)
} }
func (storage *SimpleRESTStorage) Get(id string) (runtime.Object, error) { func (storage *SimpleRESTStorage) Get(id string) (runtime.Object, error) {
return latest.Codec.CopyOrDie(&storage.item), storage.errors["get"] return api.Scheme.CopyOrDie(&storage.item), storage.errors["get"]
} }
func (storage *SimpleRESTStorage) Delete(id string) (<-chan runtime.Object, error) { func (storage *SimpleRESTStorage) Delete(id string) (<-chan runtime.Object, error) {

View File

@ -74,7 +74,7 @@ func (h *WatchHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// TODO: This is one watch per connection. We want to multiplex, so that // TODO: This is one watch per connection. We want to multiplex, so that
// multiple watches of the same thing don't create two watches downstream. // multiple watches of the same thing don't create two watches downstream.
watchServer := &WatchServer{watching} watchServer := &WatchServer{watching, h.codec}
if req.Header.Get("Connection") == "Upgrade" && req.Header.Get("Upgrade") == "websocket" { if req.Header.Get("Connection") == "Upgrade" && req.Header.Get("Upgrade") == "websocket" {
websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(w), req) websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(w), req)
} else { } else {
@ -89,6 +89,7 @@ func (h *WatchHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// WatchServer serves a watch.Interface over a websocket or vanilla HTTP. // WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
type WatchServer struct { type WatchServer struct {
watching watch.Interface watching watch.Interface
codec runtime.Codec
} }
// HandleWS implements a websocket handler. // HandleWS implements a websocket handler.
@ -111,15 +112,17 @@ func (w *WatchServer) HandleWS(ws *websocket.Conn) {
// End of results. // End of results.
return return
} }
err := websocket.JSON.Send(ws, &api.WatchEvent{ obj, err := api.NewJSONWatchEvent(w.codec, event)
Type: event.Type,
Object: runtime.EmbeddedObject{event.Object},
})
if err != nil { if err != nil {
// Client disconnect. // Client disconnect.
w.watching.Stop() w.watching.Stop()
return return
} }
if err := websocket.JSON.Send(ws, obj); err != nil {
// Client disconnect.
w.watching.Stop()
return
}
} }
} }
} }
@ -158,15 +161,17 @@ func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// End of results. // End of results.
return return
} }
err := encoder.Encode(&api.WatchEvent{ obj, err := api.NewJSONWatchEvent(self.codec, event)
Type: event.Type,
Object: runtime.EmbeddedObject{event.Object},
})
if err != nil { if err != nil {
// Client disconnect. // Client disconnect.
self.watching.Stop() self.watching.Stop()
return return
} }
if err := encoder.Encode(obj); err != nil {
// Client disconnect.
self.watching.Stop()
return
}
flusher.Flush() flusher.Flush()
} }
} }

View File

@ -114,26 +114,22 @@ func TestWatchHTTP(t *testing.T) {
decoder := json.NewDecoder(response.Body) decoder := json.NewDecoder(response.Body)
try := func(action watch.EventType, object runtime.Object) { for i, item := range watchTestTable {
// Send // Send
simpleStorage.fakeWatch.Action(action, object) simpleStorage.fakeWatch.Action(item.t, item.obj)
// Test receive // Test receive
var got api.WatchEvent var got api.WatchEvent
err := decoder.Decode(&got) err := decoder.Decode(&got)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("%d: Unexpected error: %v", i, err)
} }
if got.Type != action { if got.Type != item.t {
t.Errorf("Unexpected type: %v", got.Type) t.Errorf("%d: Unexpected type: %v", i, got.Type)
} }
if e, a := object, got.Object.Object; !reflect.DeepEqual(e, a) { if e, a := item.obj, got.Object.Object; !reflect.DeepEqual(e, a) {
t.Errorf("Expected %v, got %v", e, a) t.Errorf("%d: Expected %v, got %v", i, e, a)
} }
} }
for _, item := range watchTestTable {
try(item.t, item.obj)
}
simpleStorage.fakeWatch.Stop() simpleStorage.fakeWatch.Stop()
var got api.WatchEvent var got api.WatchEvent

View File

@ -26,7 +26,7 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/version" "github.com/GoogleCloudPlatform/kubernetes/pkg/version"

View File

@ -26,6 +26,7 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"

View File

@ -19,7 +19,6 @@ package client
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/version" "github.com/GoogleCloudPlatform/kubernetes/pkg/version"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
) )
@ -44,7 +43,7 @@ type Fake struct {
func (c *Fake) ListPods(selector labels.Selector) (*api.PodList, error) { func (c *Fake) ListPods(selector labels.Selector) (*api.PodList, error) {
c.Actions = append(c.Actions, FakeAction{Action: "list-pods"}) c.Actions = append(c.Actions, FakeAction{Action: "list-pods"})
return runtime.DefaultScheme.CopyOrDie(&c.Pods).(*api.PodList), nil return api.Scheme.CopyOrDie(&c.Pods).(*api.PodList), nil
} }
func (c *Fake) GetPod(name string) (*api.Pod, error) { func (c *Fake) GetPod(name string) (*api.Pod, error) {
@ -74,7 +73,7 @@ func (c *Fake) ListReplicationControllers(selector labels.Selector) (*api.Replic
func (c *Fake) GetReplicationController(name string) (*api.ReplicationController, error) { func (c *Fake) GetReplicationController(name string) (*api.ReplicationController, error) {
c.Actions = append(c.Actions, FakeAction{Action: "get-controller", Value: name}) c.Actions = append(c.Actions, FakeAction{Action: "get-controller", Value: name})
return runtime.DefaultScheme.CopyOrDie(&c.Ctrl).(*api.ReplicationController), nil return api.Scheme.CopyOrDie(&c.Ctrl).(*api.ReplicationController), nil
} }
func (c *Fake) CreateReplicationController(controller *api.ReplicationController) (*api.ReplicationController, error) { func (c *Fake) CreateReplicationController(controller *api.ReplicationController) (*api.ReplicationController, error) {
@ -129,7 +128,7 @@ func (c *Fake) WatchServices(label, field labels.Selector, resourceVersion uint6
func (c *Fake) ListEndpoints(selector labels.Selector) (*api.EndpointsList, error) { func (c *Fake) ListEndpoints(selector labels.Selector) (*api.EndpointsList, error) {
c.Actions = append(c.Actions, FakeAction{Action: "list-endpoints"}) c.Actions = append(c.Actions, FakeAction{Action: "list-endpoints"})
return runtime.DefaultScheme.CopyOrDie(&c.EndpointsList).(*api.EndpointsList), c.Err return api.Scheme.CopyOrDie(&c.EndpointsList).(*api.EndpointsList), c.Err
} }
func (c *Fake) WatchEndpoints(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) { func (c *Fake) WatchEndpoints(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {

View File

@ -28,9 +28,9 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
cwatch "github.com/GoogleCloudPlatform/kubernetes/pkg/client/watch"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/golang/glog" "github.com/golang/glog"
@ -269,7 +269,7 @@ func (r *Request) Watch() (watch.Interface, error) {
if response.StatusCode != http.StatusOK { if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Got status: %v", response.StatusCode) return nil, fmt.Errorf("Got status: %v", response.StatusCode)
} }
return watch.NewStreamWatcher(tools.NewAPIEventDecoder(response.Body)), nil return watch.NewStreamWatcher(cwatch.NewAPIEventDecoder(response.Body)), nil
} }
// Do formats and executes the request. Returns the API object received, or an error. // Do formats and executes the request. Returns the API object received, or an error.

View File

@ -29,6 +29,7 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@ -401,7 +402,13 @@ func TestWatch(t *testing.T) {
encoder := json.NewEncoder(w) encoder := json.NewEncoder(w)
for _, item := range table { for _, item := range table {
encoder.Encode(&api.WatchEvent{item.t, runtime.EmbeddedObject{item.obj}}) data, err := api.NewJSONWatchEvent(latest.Codec, watch.Event{item.t, item.obj})
if err != nil {
panic(err)
}
if err := encoder.Encode(data); err != nil {
panic(err)
}
flusher.Flush() flusher.Flush()
} }
})) }))

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tools package watch
import ( import (
"encoding/json" "encoding/json"
@ -28,6 +28,8 @@ import (
// APIEventDecoder implements the watch.Decoder interface for io.ReadClosers that // APIEventDecoder implements the watch.Decoder interface for io.ReadClosers that
// have contents which consist of a series of api.WatchEvent objects encoded via JSON. // have contents which consist of a series of api.WatchEvent objects encoded via JSON.
// It will decode any object which is registered to convert to api.WatchEvent via
// api.Scheme
type APIEventDecoder struct { type APIEventDecoder struct {
stream io.ReadCloser stream io.ReadCloser
decoder *json.Decoder decoder *json.Decoder

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package tools package watch
import ( import (
"encoding/json" "encoding/json"
@ -24,29 +24,37 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
) )
type watchSerialization struct {
Type watch.EventType
Object json.RawMessage
}
func TestDecoder(t *testing.T) { func TestDecoder(t *testing.T) {
out, in := io.Pipe() out, in := io.Pipe()
encoder := json.NewEncoder(in)
decoder := NewAPIEventDecoder(out) decoder := NewAPIEventDecoder(out)
expect := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} expect := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
encoder := json.NewEncoder(in)
go func() { go func() {
err := encoder.Encode(api.WatchEvent{watch.Added, runtime.EmbeddedObject{expect}}) data, err := v1beta1.Codec.Encode(expect)
if err != nil { if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if err := encoder.Encode(&watchSerialization{watch.Added, json.RawMessage(data)}); err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
in.Close()
}() }()
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
action, got, err := decoder.Decode() action, got, err := decoder.Decode()
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Fatalf("Unexpected error %v", err)
} }
if e, a := watch.Added, action; e != a { if e, a := watch.Added, action; e != a {
t.Errorf("Expected %v, got %v", e, a) t.Errorf("Expected %v, got %v", e, a)
@ -54,17 +62,12 @@ func TestDecoder(t *testing.T) {
if e, a := expect, got; !reflect.DeepEqual(e, a) { if e, a := expect, got; !reflect.DeepEqual(e, a) {
t.Errorf("Expected %v, got %v", e, a) t.Errorf("Expected %v, got %v", e, a)
} }
t.Logf("Exited read")
close(done) close(done)
}() }()
select { <-done
case <-done:
break
case <-time.After(10 * time.Second):
t.Error("Timeout")
}
done = make(chan struct{}) done = make(chan struct{})
go func() { go func() {
_, _, err := decoder.Decode() _, _, err := decoder.Decode()
if err == nil { if err == nil {
@ -72,15 +75,9 @@ func TestDecoder(t *testing.T) {
} }
close(done) close(done)
}() }()
<-done
decoder.Close() decoder.Close()
select {
case <-done:
break
case <-time.After(10 * time.Second):
t.Error("Timeout")
}
} }
func TestDecoder_SourceClose(t *testing.T) { func TestDecoder_SourceClose(t *testing.T) {

View File

@ -27,6 +27,8 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -169,7 +171,7 @@ func TestSyncReplicationControllerCreates(t *testing.T) {
} }
func TestCreateReplica(t *testing.T) { func TestCreateReplica(t *testing.T) {
body, _ := latest.Codec.Encode(&api.Pod{}) body, _ := v1beta1.Codec.Encode(&api.Pod{})
fakeHandler := util.FakeHandler{ fakeHandler := util.FakeHandler{
StatusCode: 200, StatusCode: 200,
ResponseBody: string(body), ResponseBody: string(body),
@ -209,7 +211,7 @@ func TestCreateReplica(t *testing.T) {
expectedPod := api.Pod{ expectedPod := api.Pod{
JSONBase: api.JSONBase{ JSONBase: api.JSONBase{
Kind: "Pod", Kind: "Pod",
APIVersion: "v1beta1", APIVersion: latest.Version,
}, },
Labels: controllerSpec.DesiredState.PodTemplate.Labels, Labels: controllerSpec.DesiredState.PodTemplate.Labels,
DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState, DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState,
@ -287,12 +289,12 @@ func TestSyncronize(t *testing.T) {
fakePodHandler := util.FakeHandler{ fakePodHandler := util.FakeHandler{
StatusCode: 200, StatusCode: 200,
ResponseBody: "{\"apiVersion\": \"v1beta1\", \"kind\": \"PodList\"}", ResponseBody: "{\"apiVersion\": \"" + latest.Version + "\", \"kind\": \"PodList\"}",
T: t, T: t,
} }
fakeControllerHandler := util.FakeHandler{ fakeControllerHandler := util.FakeHandler{
StatusCode: 200, StatusCode: 200,
ResponseBody: latest.Codec.EncodeOrDie(&api.ReplicationControllerList{ ResponseBody: runtime.EncodeOrDie(latest.Codec, &api.ReplicationControllerList{
Items: []api.ReplicationController{ Items: []api.ReplicationController{
controllerSpec1, controllerSpec1,
controllerSpec2, controllerSpec2,

View File

@ -25,14 +25,14 @@ import (
// Decode converts a YAML or JSON string back into a pointer to an api object. // Decode converts a YAML or JSON string back into a pointer to an api object.
// Deduces the type based upon the fields added by the MetaInsertionFactory // Deduces the type based upon the fields added by the MetaInsertionFactory
// technique. The object will be converted, if necessary, into the // technique. The object will be converted, if necessary, into the
// s.InternalVersion type before being returned. Decode will refuse to decode // s.InternalVersion type before being returned. Decode will not decode
// objects without a version, because that's probably an error. // objects without version set unless InternalVersion is also "".
func (s *Scheme) Decode(data []byte) (interface{}, error) { func (s *Scheme) Decode(data []byte) (interface{}, error) {
version, kind, err := s.DataVersionAndKind(data) version, kind, err := s.DataVersionAndKind(data)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if version == "" { if version == "" && s.InternalVersion != "" {
return nil, fmt.Errorf("version not set in '%s'", string(data)) return nil, fmt.Errorf("version not set in '%s'", string(data))
} }
obj, err := s.NewObject(version, kind) obj, err := s.NewObject(version, kind)

View File

@ -21,16 +21,7 @@ import (
"fmt" "fmt"
) )
// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests. // EncodeToVersion turns the given api object into an appropriate JSON string.
func (s *Scheme) EncodeOrDie(obj interface{}) string {
bytes, err := s.Encode(obj)
if err != nil {
panic(err)
}
return string(bytes)
}
// Encode turns the given api object into an appropriate JSON string.
// Obj may be a pointer to a struct, or a struct. If a struct, a copy // Obj may be a pointer to a struct, or a struct. If a struct, a copy
// will be made, therefore it's recommended to pass a pointer to a // will be made, therefore it's recommended to pass a pointer to a
// struct. The type must have been registered. // struct. The type must have been registered.
@ -58,11 +49,6 @@ func (s *Scheme) EncodeOrDie(obj interface{}) string {
// objects, whether they be in our storage layer (e.g., etcd), or in user's // objects, whether they be in our storage layer (e.g., etcd), or in user's
// config files. // config files.
// //
func (s *Scheme) Encode(obj interface{}) (data []byte, err error) {
return s.EncodeToVersion(obj, s.ExternalVersion)
}
// EncodeToVersion is like Encode, but you may choose the version.
func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []byte, err error) { func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []byte, err error) {
obj = maybeCopy(obj) obj = maybeCopy(obj)
v, _ := enforcePtr(obj) // maybeCopy guarantees a pointer v, _ := enforcePtr(obj) // maybeCopy guarantees a pointer

View File

@ -66,9 +66,6 @@ type Scheme struct {
// you use "" for the internal version. // you use "" for the internal version.
InternalVersion string InternalVersion string
// ExternalVersion is the default external version.
ExternalVersion string
// MetaInsertionFactory is used to create an object to store and retrieve // MetaInsertionFactory is used to create an object to store and retrieve
// the version and kind information for all objects. The default uses the // the version and kind information for all objects. The default uses the
// keys "version" and "kind" respectively. // keys "version" and "kind" respectively.
@ -83,7 +80,6 @@ func NewScheme() *Scheme {
typeToKind: map[reflect.Type]string{}, typeToKind: map[reflect.Type]string{},
converter: NewConverter(), converter: NewConverter(),
InternalVersion: "", InternalVersion: "",
ExternalVersion: "v1",
MetaInsertionFactory: metaInsertion{}, MetaInsertionFactory: metaInsertion{},
} }
s.converter.NameFunc = s.nameFunc s.converter.NameFunc = s.nameFunc
@ -146,6 +142,19 @@ func (s *Scheme) AddKnownTypeWithName(version, kind string, obj interface{}) {
s.typeToKind[t] = kind s.typeToKind[t] = kind
} }
// KnownTypes returns an array of the types that are known for a particular version.
func (s *Scheme) KnownTypes(version string) map[string]reflect.Type {
all, ok := s.versionMap[version]
if !ok {
return map[string]reflect.Type{}
}
types := make(map[string]reflect.Type)
for k, v := range all {
types[k] = v
}
return types
}
// NewObject returns a new object of the given version and name, // NewObject returns a new object of the given version and name,
// or an error if it hasn't been registered. // or an error if it hasn't been registered.
func (s *Scheme) NewObject(versionName, typeName string) (interface{}, error) { func (s *Scheme) NewObject(versionName, typeName string) (interface{}, error) {

View File

@ -125,7 +125,6 @@ func GetTestScheme() *Scheme {
s.AddKnownTypes("v1", &ExternalInternalSame{}) s.AddKnownTypes("v1", &ExternalInternalSame{})
s.AddKnownTypeWithName("v1", "TestType1", &ExternalTestType1{}) s.AddKnownTypeWithName("v1", "TestType1", &ExternalTestType1{})
s.AddKnownTypeWithName("v1", "TestType2", &ExternalTestType2{}) s.AddKnownTypeWithName("v1", "TestType2", &ExternalTestType2{})
s.ExternalVersion = "v1"
s.InternalVersion = "" s.InternalVersion = ""
s.MetaInsertionFactory = testMetaInsertionFactory{} s.MetaInsertionFactory = testMetaInsertionFactory{}
return s return s
@ -178,7 +177,7 @@ func runTest(t *testing.T, source interface{}) {
TestObjectFuzzer.Fuzz(source) TestObjectFuzzer.Fuzz(source)
s := GetTestScheme() s := GetTestScheme()
data, err := s.Encode(source) data, err := s.EncodeToVersion(source, "v1")
if err != nil { if err != nil {
t.Errorf("%v: %v (%#v)", name, err, source) t.Errorf("%v: %v (%#v)", name, err, source)
return return
@ -221,7 +220,7 @@ func TestEncode_NonPtr(t *testing.T) {
s := GetTestScheme() s := GetTestScheme()
tt := TestType1{A: "I'm not a pointer object"} tt := TestType1{A: "I'm not a pointer object"}
obj := interface{}(tt) obj := interface{}(tt)
data, err := s.Encode(obj) data, err := s.EncodeToVersion(obj, "v1")
obj2, err2 := s.Decode(data) obj2, err2 := s.Decode(data)
if err != nil || err2 != nil { if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v'", err, err2) t.Fatalf("Failure: '%v' '%v'", err, err2)
@ -238,7 +237,7 @@ func TestEncode_Ptr(t *testing.T) {
s := GetTestScheme() s := GetTestScheme()
tt := &TestType1{A: "I am a pointer object"} tt := &TestType1{A: "I am a pointer object"}
obj := interface{}(tt) obj := interface{}(tt)
data, err := s.Encode(obj) data, err := s.EncodeToVersion(obj, "v1")
obj2, err2 := s.Decode(data) obj2, err2 := s.Decode(data)
if err != nil || err2 != nil { if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v'", err, err2) t.Fatalf("Failure: '%v' '%v'", err, err2)
@ -255,7 +254,6 @@ func TestBadJSONRejection(t *testing.T) {
s := GetTestScheme() s := GetTestScheme()
badJSONs := [][]byte{ badJSONs := [][]byte{
[]byte(`{"myVersionKey":"v1"}`), // Missing kind []byte(`{"myVersionKey":"v1"}`), // Missing kind
[]byte(`{"myKindKey":"TestType1"}`), // Missing version
[]byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind []byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind
[]byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version []byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version
} }
@ -270,6 +268,23 @@ func TestBadJSONRejection(t *testing.T) {
} }
} }
func TestBadJSONRejectionForSetInternalVersion(t *testing.T) {
s := GetTestScheme()
s.InternalVersion = "v1"
badJSONs := [][]byte{
[]byte(`{"myKindKey":"TestType1"}`), // Missing version
}
for _, b := range badJSONs {
if _, err := s.Decode(b); err == nil {
t.Errorf("Did not reject bad json: %s", string(b))
}
}
badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`)
if err := s.DecodeInto(badJSONKindMismatch, &TestType1{}); err == nil {
t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
}
}
func TestMetaValues(t *testing.T) { func TestMetaValues(t *testing.T) {
type InternalSimple struct { type InternalSimple struct {
Version string `json:"version,omitempty" yaml:"version,omitempty"` Version string `json:"version,omitempty" yaml:"version,omitempty"`
@ -283,7 +298,6 @@ func TestMetaValues(t *testing.T) {
} }
s := NewScheme() s := NewScheme()
s.InternalVersion = "" s.InternalVersion = ""
s.ExternalVersion = "externalVersion"
s.AddKnownTypeWithName("", "Simple", &InternalSimple{}) s.AddKnownTypeWithName("", "Simple", &InternalSimple{})
s.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{}) s.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{})
@ -373,7 +387,6 @@ func TestMetaValuesUnregisteredConvert(t *testing.T) {
} }
s := NewScheme() s := NewScheme()
s.InternalVersion = "" s.InternalVersion = ""
s.ExternalVersion = "externalVersion"
// We deliberately don't register the types. // We deliberately don't register the types.
internalToExternalCalls := 0 internalToExternalCalls := 0

View File

@ -26,7 +26,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
func validateAction(expectedAction, actualAction client.FakeAction, t *testing.T) { func validateAction(expectedAction, actualAction client.FakeAction, t *testing.T) {
@ -94,7 +93,7 @@ func TestUpdateWithNewImage(t *testing.T) {
} }
validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t) validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t)
newCtrl := runtime.DefaultScheme.CopyOrDie(&fakeClient.Ctrl).(*api.ReplicationController) newCtrl := api.Scheme.CopyOrDie(&fakeClient.Ctrl).(*api.ReplicationController)
newCtrl.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image = "fooImage:2" newCtrl.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image = "fooImage:2"
validateAction(client.FakeAction{Action: "update-controller", Value: newCtrl}, fakeClient.Actions[1], t) validateAction(client.FakeAction{Action: "update-controller", Value: newCtrl}, fakeClient.Actions[1], t)

View File

@ -21,6 +21,8 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"gopkg.in/v1/yaml" "gopkg.in/v1/yaml"
) )
@ -125,8 +127,9 @@ type TestParseType struct {
func (*TestParseType) IsAnAPIObject() {} func (*TestParseType) IsAnAPIObject() {}
func TestParseCustomType(t *testing.T) { func TestParseCustomType(t *testing.T) {
latest.Codec.AddKnownTypes("", &TestParseType{}) api.Scheme.AddKnownTypes("", &TestParseType{})
latest.Codec.AddKnownTypes("v1beta1", &TestParseType{}) api.Scheme.AddKnownTypes("v1beta1", &TestParseType{})
api.Scheme.AddKnownTypes("v1beta2", &TestParseType{})
parser := NewParser(map[string]runtime.Object{ parser := NewParser(map[string]runtime.Object{
"custom": &TestParseType{}, "custom": &TestParseType{},
}) })

View File

@ -21,8 +21,8 @@ import (
"net/http" "net/http"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
// ProxyServer is a http.Handler which proxies Kubernetes APIs to remote API server. // ProxyServer is a http.Handler which proxies Kubernetes APIs to remote API server.

View File

@ -26,6 +26,7 @@ import (
"text/template" "text/template"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/golang/glog" "github.com/golang/glog"

View File

@ -25,7 +25,7 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"gopkg.in/v1/yaml" "gopkg.in/v1/yaml"
) )

View File

@ -24,9 +24,8 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@ -48,7 +47,7 @@ func NewSourceEtcd(key string, client tools.EtcdClient, updates chan<- interface
helper := tools.EtcdHelper{ helper := tools.EtcdHelper{
client, client,
latest.Codec, latest.Codec,
runtime.DefaultResourceVersioner, latest.ResourceVersioner,
} }
source := &SourceEtcd{ source := &SourceEtcd{
key: key, key: key,

View File

@ -20,7 +20,7 @@ import (
"net/http" "net/http"
"time" "time"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
@ -136,5 +136,5 @@ func (m *Master) API_v1beta1() (map[string]apiserver.RESTStorage, runtime.Codec)
for k, v := range m.storage { for k, v := range m.storage {
storage[k] = v storage[k] = v
} }
return storage, runtime.DefaultCodec return storage, v1beta1.Codec
} }

View File

@ -39,7 +39,7 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/coreos/go-etcd/etcd" "github.com/coreos/go-etcd/etcd"

View File

@ -23,9 +23,8 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
func TestNewREST(t *testing.T) { func TestNewREST(t *testing.T) {

View File

@ -26,10 +26,10 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
func TestListControllersError(t *testing.T) { func TestListControllersError(t *testing.T) {

View File

@ -21,6 +21,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd" etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/constraint" "github.com/GoogleCloudPlatform/kubernetes/pkg/constraint"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -46,7 +47,7 @@ func NewRegistry(client tools.EtcdClient) *Registry {
EtcdHelper: tools.EtcdHelper{ EtcdHelper: tools.EtcdHelper{
client, client,
latest.Codec, latest.Codec,
runtime.DefaultResourceVersioner, latest.ResourceVersioner,
}, },
} }
registry.manifestFactory = &BasicManifestFactory{ registry.manifestFactory = &BasicManifestFactory{

View File

@ -22,7 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -41,7 +41,7 @@ func NewTestEtcdRegistry(client tools.EtcdClient) *Registry {
func TestEtcdGetPod(t *testing.T) { func TestEtcdGetPod(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t) fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Set("/registry/pods/foo", latest.Codec.EncodeOrDie(&api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0) fakeClient.Set("/registry/pods/foo", runtime.EncodeOrDie(latest.Codec, &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
pod, err := registry.GetPod("foo") pod, err := registry.GetPod("foo")
if err != nil { if err != nil {
@ -77,7 +77,7 @@ func TestEtcdCreatePod(t *testing.T) {
}, },
E: tools.EtcdErrorNotFound, E: tools.EtcdErrorNotFound,
} }
fakeClient.Set("/registry/hosts/machine/kubelet", latest.Codec.EncodeOrDie(&api.ContainerManifestList{}), 0) fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(latest.Codec, &api.ContainerManifestList{}), 0)
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
err := registry.CreatePod(&api.Pod{ err := registry.CreatePod(&api.Pod{
JSONBase: api.JSONBase{ JSONBase: api.JSONBase{
@ -133,7 +133,7 @@ func TestEtcdCreatePodAlreadyExisting(t *testing.T) {
fakeClient.Data["/registry/pods/foo"] = tools.EtcdResponseWithError{ fakeClient.Data["/registry/pods/foo"] = tools.EtcdResponseWithError{
R: &etcd.Response{ R: &etcd.Response{
Node: &etcd.Node{ Node: &etcd.Node{
Value: latest.Codec.EncodeOrDie(&api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}),
}, },
}, },
E: nil, E: nil,
@ -264,7 +264,7 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) {
}, },
E: tools.EtcdErrorNotFound, E: tools.EtcdErrorNotFound,
} }
fakeClient.Set("/registry/hosts/machine/kubelet", latest.Codec.EncodeOrDie(&api.ContainerManifestList{ fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(latest.Codec, &api.ContainerManifestList{
Items: []api.ContainerManifest{ Items: []api.ContainerManifest{
{ID: "bar"}, {ID: "bar"},
}, },
@ -325,11 +325,11 @@ func TestEtcdDeletePod(t *testing.T) {
fakeClient.TestIndex = true fakeClient.TestIndex = true
key := "/registry/pods/foo" key := "/registry/pods/foo"
fakeClient.Set(key, latest.Codec.EncodeOrDie(&api.Pod{ fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &api.Pod{
JSONBase: api.JSONBase{ID: "foo"}, JSONBase: api.JSONBase{ID: "foo"},
DesiredState: api.PodState{Host: "machine"}, DesiredState: api.PodState{Host: "machine"},
}), 0) }), 0)
fakeClient.Set("/registry/hosts/machine/kubelet", latest.Codec.EncodeOrDie(&api.ContainerManifestList{ fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(latest.Codec, &api.ContainerManifestList{
Items: []api.ContainerManifest{ Items: []api.ContainerManifest{
{ID: "foo"}, {ID: "foo"},
}, },
@ -361,11 +361,11 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) {
fakeClient.TestIndex = true fakeClient.TestIndex = true
key := "/registry/pods/foo" key := "/registry/pods/foo"
fakeClient.Set(key, latest.Codec.EncodeOrDie(&api.Pod{ fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &api.Pod{
JSONBase: api.JSONBase{ID: "foo"}, JSONBase: api.JSONBase{ID: "foo"},
DesiredState: api.PodState{Host: "machine"}, DesiredState: api.PodState{Host: "machine"},
}), 0) }), 0)
fakeClient.Set("/registry/hosts/machine/kubelet", latest.Codec.EncodeOrDie(&api.ContainerManifestList{ fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(latest.Codec, &api.ContainerManifestList{
Items: []api.ContainerManifest{ Items: []api.ContainerManifest{
{ID: "foo"}, {ID: "foo"},
{ID: "bar"}, {ID: "bar"},
@ -445,13 +445,13 @@ func TestEtcdListPods(t *testing.T) {
Node: &etcd.Node{ Node: &etcd.Node{
Nodes: []*etcd.Node{ Nodes: []*etcd.Node{
{ {
Value: latest.Codec.EncodeOrDie(&api.Pod{ Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{
JSONBase: api.JSONBase{ID: "foo"}, JSONBase: api.JSONBase{ID: "foo"},
DesiredState: api.PodState{Host: "machine"}, DesiredState: api.PodState{Host: "machine"},
}), }),
}, },
{ {
Value: latest.Codec.EncodeOrDie(&api.Pod{ Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{
JSONBase: api.JSONBase{ID: "bar"}, JSONBase: api.JSONBase{ID: "bar"},
DesiredState: api.PodState{Host: "machine"}, DesiredState: api.PodState{Host: "machine"},
}), }),
@ -520,10 +520,10 @@ func TestEtcdListControllers(t *testing.T) {
Node: &etcd.Node{ Node: &etcd.Node{
Nodes: []*etcd.Node{ Nodes: []*etcd.Node{
{ {
Value: latest.Codec.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), Value: runtime.EncodeOrDie(latest.Codec, &api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}),
}, },
{ {
Value: latest.Codec.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "bar"}}), Value: runtime.EncodeOrDie(latest.Codec, &api.ReplicationController{JSONBase: api.JSONBase{ID: "bar"}}),
}, },
}, },
}, },
@ -543,7 +543,7 @@ func TestEtcdListControllers(t *testing.T) {
func TestEtcdGetController(t *testing.T) { func TestEtcdGetController(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t) fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Set("/registry/controllers/foo", latest.Codec.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) fakeClient.Set("/registry/controllers/foo", runtime.EncodeOrDie(latest.Codec, &api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
ctrl, err := registry.GetController("foo") ctrl, err := registry.GetController("foo")
if err != nil { if err != nil {
@ -619,7 +619,7 @@ func TestEtcdCreateController(t *testing.T) {
func TestEtcdCreateControllerAlreadyExisting(t *testing.T) { func TestEtcdCreateControllerAlreadyExisting(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t) fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Set("/registry/controllers/foo", latest.Codec.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) fakeClient.Set("/registry/controllers/foo", runtime.EncodeOrDie(latest.Codec, &api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
err := registry.CreateController(&api.ReplicationController{ err := registry.CreateController(&api.ReplicationController{
@ -636,7 +636,7 @@ func TestEtcdUpdateController(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t) fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.TestIndex = true fakeClient.TestIndex = true
resp, _ := fakeClient.Set("/registry/controllers/foo", latest.Codec.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) resp, _ := fakeClient.Set("/registry/controllers/foo", runtime.EncodeOrDie(latest.Codec, &api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
err := registry.UpdateController(&api.ReplicationController{ err := registry.UpdateController(&api.ReplicationController{
JSONBase: api.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex}, JSONBase: api.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex},
@ -662,10 +662,10 @@ func TestEtcdListServices(t *testing.T) {
Node: &etcd.Node{ Node: &etcd.Node{
Nodes: []*etcd.Node{ Nodes: []*etcd.Node{
{ {
Value: latest.Codec.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}), Value: runtime.EncodeOrDie(latest.Codec, &api.Service{JSONBase: api.JSONBase{ID: "foo"}}),
}, },
{ {
Value: latest.Codec.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "bar"}}), Value: runtime.EncodeOrDie(latest.Codec, &api.Service{JSONBase: api.JSONBase{ID: "bar"}}),
}, },
}, },
}, },
@ -711,7 +711,7 @@ func TestEtcdCreateService(t *testing.T) {
func TestEtcdCreateServiceAlreadyExisting(t *testing.T) { func TestEtcdCreateServiceAlreadyExisting(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t) fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Set("/registry/services/specs/foo", latest.Codec.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) fakeClient.Set("/registry/services/specs/foo", runtime.EncodeOrDie(latest.Codec, &api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
err := registry.CreateService(&api.Service{ err := registry.CreateService(&api.Service{
JSONBase: api.JSONBase{ID: "foo"}, JSONBase: api.JSONBase{ID: "foo"},
@ -723,7 +723,7 @@ func TestEtcdCreateServiceAlreadyExisting(t *testing.T) {
func TestEtcdGetService(t *testing.T) { func TestEtcdGetService(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t) fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Set("/registry/services/specs/foo", latest.Codec.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) fakeClient.Set("/registry/services/specs/foo", runtime.EncodeOrDie(latest.Codec, &api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
service, err := registry.GetService("foo") service, err := registry.GetService("foo")
if err != nil { if err != nil {
@ -775,7 +775,7 @@ func TestEtcdUpdateService(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t) fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.TestIndex = true fakeClient.TestIndex = true
resp, _ := fakeClient.Set("/registry/services/specs/foo", latest.Codec.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) resp, _ := fakeClient.Set("/registry/services/specs/foo", runtime.EncodeOrDie(latest.Codec, &api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
testService := api.Service{ testService := api.Service{
JSONBase: api.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex}, JSONBase: api.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex},
@ -812,10 +812,10 @@ func TestEtcdListEndpoints(t *testing.T) {
Node: &etcd.Node{ Node: &etcd.Node{
Nodes: []*etcd.Node{ Nodes: []*etcd.Node{
{ {
Value: latest.Codec.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:8345"}}), Value: runtime.EncodeOrDie(latest.Codec, &api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:8345"}}),
}, },
{ {
Value: latest.Codec.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "bar"}}), Value: runtime.EncodeOrDie(latest.Codec, &api.Endpoints{JSONBase: api.JSONBase{ID: "bar"}}),
}, },
}, },
}, },
@ -841,7 +841,7 @@ func TestEtcdGetEndpoints(t *testing.T) {
Endpoints: []string{"127.0.0.1:34855"}, Endpoints: []string{"127.0.0.1:34855"},
} }
fakeClient.Set("/registry/services/endpoints/foo", latest.Codec.EncodeOrDie(endpoints), 0) fakeClient.Set("/registry/services/endpoints/foo", runtime.EncodeOrDie(latest.Codec, endpoints), 0)
got, err := registry.GetEndpoints("foo") got, err := registry.GetEndpoints("foo")
if err != nil { if err != nil {
@ -862,7 +862,7 @@ func TestEtcdUpdateEndpoints(t *testing.T) {
Endpoints: []string{"baz", "bar"}, Endpoints: []string{"baz", "bar"},
} }
fakeClient.Set("/registry/services/endpoints/foo", latest.Codec.EncodeOrDie(&api.Endpoints{}), 0) fakeClient.Set("/registry/services/endpoints/foo", runtime.EncodeOrDie(latest.Codec, &api.Endpoints{}), 0)
err := registry.UpdateEndpoints(&endpoints) err := registry.UpdateEndpoints(&endpoints)
if err != nil { if err != nil {

View File

@ -24,6 +24,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/fake" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/fake"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"

View File

@ -20,42 +20,80 @@ import (
"gopkg.in/v1/yaml" "gopkg.in/v1/yaml"
) )
// EmbeddedObject must have an appropriate encoder and decoder functions, such that on the
// wire, it's stored as a []byte, but in memory, the contained object is accessable as an
// Object via the Get() function. Only valid API objects may be stored via EmbeddedObject.
// The purpose of this is to allow an API object of type known only at runtime to be
// embedded within other API objects.
//
// Define a Codec variable in your package and import the runtime package and
// then use the commented section below
/*
// EmbeddedObject implements a Codec specific version of an
// embedded object.
type EmbeddedObject struct {
runtime.Object
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (a *EmbeddedObject) UnmarshalJSON(b []byte) error {
obj, err := runtime.CodecUnmarshalJSON(Codec, b)
a.Object = obj
return err
}
// MarshalJSON implements the json.Marshaler interface.
func (a EmbeddedObject) MarshalJSON() ([]byte, error) {
return runtime.CodecMarshalJSON(Codec, a.Object)
}
// SetYAML implements the yaml.Setter interface.
func (a *EmbeddedObject) SetYAML(tag string, value interface{}) bool {
obj, ok := runtime.CodecSetYAML(Codec, tag, value)
a.Object = obj
return ok
}
// GetYAML implements the yaml.Getter interface.
func (a EmbeddedObject) GetYAML() (tag string, value interface{}) {
return runtime.CodecGetYAML(Codec, a.Object)
}
*/
// Encode()/Decode() are the canonical way of converting an API object to/from // Encode()/Decode() are the canonical way of converting an API object to/from
// wire format. This file provides utility functions which permit doing so // wire format. This file provides utility functions which permit doing so
// recursively, such that API objects of types known only at run time can be // recursively, such that API objects of types known only at run time can be
// embedded within other API types. // embedded within other API types.
// UnmarshalJSON implements the json.Unmarshaler interface. // UnmarshalJSON implements the json.Unmarshaler interface.
func (a *EmbeddedObject) UnmarshalJSON(b []byte) error { func CodecUnmarshalJSON(codec Codec, b []byte) (Object, error) {
// Handle JSON's "null": Decode() doesn't expect it. // Handle JSON's "null": Decode() doesn't expect it.
if len(b) == 4 && string(b) == "null" { if len(b) == 4 && string(b) == "null" {
a.Object = nil return nil, nil
return nil
} }
obj, err := DefaultCodec.Decode(b) obj, err := codec.Decode(b)
if err != nil { if err != nil {
return err return nil, err
} }
a.Object = obj return obj, nil
return nil
} }
// MarshalJSON implements the json.Marshaler interface. // MarshalJSON implements the json.Marshaler interface.
func (a EmbeddedObject) MarshalJSON() ([]byte, error) { func CodecMarshalJSON(codec Codec, obj Object) ([]byte, error) {
if a.Object == nil { if obj == nil {
// Encode unset/nil objects as JSON's "null". // Encode unset/nil objects as JSON's "null".
return []byte("null"), nil return []byte("null"), nil
} }
return DefaultCodec.Encode(a.Object) return codec.Encode(obj)
} }
// SetYAML implements the yaml.Setter interface. // SetYAML implements the yaml.Setter interface.
func (a *EmbeddedObject) SetYAML(tag string, value interface{}) bool { func CodecSetYAML(codec Codec, tag string, value interface{}) (Object, bool) {
if value == nil { if value == nil {
a.Object = nil return nil, true
return true
} }
// Why does the yaml package send value as a map[interface{}]interface{}? // Why does the yaml package send value as a map[interface{}]interface{}?
// It's especially frustrating because encoding/json does the right thing // It's especially frustrating because encoding/json does the right thing
@ -67,22 +105,21 @@ func (a *EmbeddedObject) SetYAML(tag string, value interface{}) bool {
if err != nil { if err != nil {
panic("yaml can't reverse its own object") panic("yaml can't reverse its own object")
} }
obj, err := DefaultCodec.Decode(b) obj, err := codec.Decode(b)
if err != nil { if err != nil {
return false return nil, false
} }
a.Object = obj return obj, true
return true
} }
// GetYAML implements the yaml.Getter interface. // GetYAML implements the yaml.Getter interface.
func (a EmbeddedObject) GetYAML() (tag string, value interface{}) { func CodecGetYAML(codec Codec, obj Object) (tag string, value interface{}) {
if a.Object == nil { if obj == nil {
value = "null" value = "null"
return return
} }
// Encode returns JSON, which is conveniently a subset of YAML. // Encode returns JSON, which is conveniently a subset of YAML.
v, err := DefaultCodec.Encode(a.Object) v, err := codec.Encode(obj)
if err != nil { if err != nil {
panic("impossible to encode API object!") panic("impossible to encode API object!")
} }

View File

@ -14,16 +14,51 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package runtime package runtime_test
import ( import (
"encoding/json" "encoding/json"
"reflect" "reflect"
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
var scheme = runtime.NewScheme()
var Codec = runtime.CodecFor(scheme, "v1test")
// EmbeddedObject implements a Codec specific version of an
// embedded object.
type EmbeddedObject struct {
runtime.Object
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (a *EmbeddedObject) UnmarshalJSON(b []byte) error {
obj, err := runtime.CodecUnmarshalJSON(Codec, b)
a.Object = obj
return err
}
// MarshalJSON implements the json.Marshaler interface.
func (a EmbeddedObject) MarshalJSON() ([]byte, error) {
return runtime.CodecMarshalJSON(Codec, a.Object)
}
// SetYAML implements the yaml.Setter interface.
func (a *EmbeddedObject) SetYAML(tag string, value interface{}) bool {
obj, ok := runtime.CodecSetYAML(Codec, tag, value)
a.Object = obj
return ok
}
// GetYAML implements the yaml.Getter interface.
func (a EmbeddedObject) GetYAML() (tag string, value interface{}) {
return runtime.CodecGetYAML(Codec, a.Object)
}
type EmbeddedTest struct { type EmbeddedTest struct {
JSONBase `yaml:",inline" json:",inline"` runtime.JSONBase `yaml:",inline" json:",inline"`
Object EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"` Object EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"`
EmptyObject EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"` EmptyObject EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"`
} }
@ -31,21 +66,20 @@ type EmbeddedTest struct {
func (*EmbeddedTest) IsAnAPIObject() {} func (*EmbeddedTest) IsAnAPIObject() {}
func TestEmbeddedObject(t *testing.T) { func TestEmbeddedObject(t *testing.T) {
// TODO(dbsmith) fix EmbeddedObject to not use DefaultScheme. s := scheme
s := DefaultScheme
s.AddKnownTypes("", &EmbeddedTest{}) s.AddKnownTypes("", &EmbeddedTest{})
s.AddKnownTypes("v1beta1", &EmbeddedTest{}) s.AddKnownTypes("v1test", &EmbeddedTest{})
outer := &EmbeddedTest{ outer := &EmbeddedTest{
JSONBase: JSONBase{ID: "outer"}, JSONBase: runtime.JSONBase{ID: "outer"},
Object: EmbeddedObject{ Object: EmbeddedObject{
&EmbeddedTest{ &EmbeddedTest{
JSONBase: JSONBase{ID: "inner"}, JSONBase: runtime.JSONBase{ID: "inner"},
}, },
}, },
} }
wire, err := s.Encode(outer) wire, err := s.EncodeToVersion(outer, "v1test")
if err != nil { if err != nil {
t.Fatalf("Unexpected encode error '%v'", err) t.Fatalf("Unexpected encode error '%v'", err)
} }

View File

@ -16,13 +16,23 @@ limitations under the License.
package runtime package runtime
// Codec defines methods for serializing and deserializing API objects. // Decoder defines methods for deserializing API objects into a given type
type Codec interface { type Decoder interface {
Encode(obj Object) (data []byte, err error)
Decode(data []byte) (Object, error) Decode(data []byte) (Object, error)
DecodeInto(data []byte, obj Object) error DecodeInto(data []byte, obj Object) error
} }
// Encoder defines methods for serializing API objects into bytes
type Encoder interface {
Encode(obj Object) (data []byte, err error)
}
// Codec defines methods for serializing and deserializing API objects.
type Codec interface {
Decoder
Encoder
}
// ResourceVersioner provides methods for setting and retrieving // ResourceVersioner provides methods for setting and retrieving
// the resource version from an API object. // the resource version from an API object.
type ResourceVersioner interface { type ResourceVersioner interface {

View File

@ -17,16 +17,40 @@ limitations under the License.
package runtime package runtime
import ( import (
"encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"gopkg.in/v1/yaml" "gopkg.in/v1/yaml"
) )
var DefaultResourceVersioner ResourceVersioner = NewJSONBaseResourceVersioner() // codecWrapper implements encoding to an alternative
var DefaultScheme = NewScheme("", "v1beta1") // default version for a scheme.
var DefaultCodec Codec = DefaultScheme type codecWrapper struct {
*Scheme
version string
}
// Encode implements Codec
func (c *codecWrapper) Encode(obj Object) ([]byte, error) {
return c.Scheme.EncodeToVersion(obj, c.version)
}
// CodecFor returns a Codec that invokes Encode with the provided version.
func CodecFor(scheme *Scheme, version string) Codec {
return &codecWrapper{scheme, version}
}
// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
func EncodeOrDie(codec Codec, obj Object) string {
bytes, err := codec.Encode(obj)
if err != nil {
panic(err)
}
return string(bytes)
}
// Scheme defines methods for serializing and deserializing API objects. It // Scheme defines methods for serializing and deserializing API objects. It
// is an adaptation of conversion's Scheme for our API objects. // is an adaptation of conversion's Scheme for our API objects.
@ -36,21 +60,13 @@ type Scheme struct {
// fromScope gets the input version, desired output version, and desired Scheme // fromScope gets the input version, desired output version, and desired Scheme
// from a conversion.Scope. // from a conversion.Scope.
func fromScope(s conversion.Scope) (inVersion, outVersion string, scheme *Scheme) { func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
scheme = DefaultScheme scheme = self
inVersion = s.Meta().SrcVersion inVersion = s.Meta().SrcVersion
outVersion = s.Meta().DestVersion outVersion = s.Meta().DestVersion
return inVersion, outVersion, scheme return inVersion, outVersion, scheme
} }
func init() {
// Set up a generic mapping between RawExtension and EmbeddedObject.
DefaultScheme.AddConversionFuncs(
embeddedObjectToRawExtension,
rawExtensionToEmbeddedObject,
)
}
// emptyPlugin is used to copy the Kind field to and from plugin objects. // emptyPlugin is used to copy the Kind field to and from plugin objects.
type emptyPlugin struct { type emptyPlugin struct {
PluginBase `json:",inline" yaml:",inline"` PluginBase `json:",inline" yaml:",inline"`
@ -59,14 +75,14 @@ type emptyPlugin struct {
// embeddedObjectToRawExtension does the conversion you would expect from the name, using the information // embeddedObjectToRawExtension does the conversion you would expect from the name, using the information
// given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins; // given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins;
// see the comment for RawExtension. // see the comment for RawExtension.
func embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExtension, s conversion.Scope) error { func (self *Scheme) embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExtension, s conversion.Scope) error {
if in.Object == nil { if in.Object == nil {
out.RawJSON = []byte("null") out.RawJSON = []byte("null")
return nil return nil
} }
// Figure out the type and kind of the output object. // Figure out the type and kind of the output object.
_, outVersion, scheme := fromScope(s) _, outVersion, scheme := self.fromScope(s)
_, kind, err := scheme.raw.ObjectVersionAndKind(in.Object) _, kind, err := scheme.raw.ObjectVersionAndKind(in.Object)
if err != nil { if err != nil {
return err return err
@ -107,13 +123,13 @@ func embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExtension, s conve
// rawExtensionToEmbeddedObject does the conversion you would expect from the name, using the information // rawExtensionToEmbeddedObject does the conversion you would expect from the name, using the information
// given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins; // given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins;
// see the comment for RawExtension. // see the comment for RawExtension.
func rawExtensionToEmbeddedObject(in *RawExtension, out *EmbeddedObject, s conversion.Scope) error { func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *EmbeddedObject, s conversion.Scope) error {
if len(in.RawJSON) == 4 && string(in.RawJSON) == "null" { if len(in.RawJSON) == 4 && string(in.RawJSON) == "null" {
out.Object = nil out.Object = nil
return nil return nil
} }
// Figure out the type and kind of the output object. // Figure out the type and kind of the output object.
inVersion, outVersion, scheme := fromScope(s) inVersion, outVersion, scheme := self.fromScope(s)
_, kind, err := scheme.raw.DataVersionAndKind(in.RawJSON) _, kind, err := scheme.raw.DataVersionAndKind(in.RawJSON)
if err != nil { if err != nil {
return err return err
@ -153,13 +169,15 @@ func rawExtensionToEmbeddedObject(in *RawExtension, out *EmbeddedObject, s conve
return nil return nil
} }
// NewScheme creates a new Scheme. A default scheme is provided and accessible // NewScheme creates a new Scheme. This scheme is pluggable by default.
// as the "DefaultScheme" variable. func NewScheme() *Scheme {
func NewScheme(internalVersion, externalVersion string) *Scheme {
s := &Scheme{conversion.NewScheme()} s := &Scheme{conversion.NewScheme()}
s.raw.InternalVersion = internalVersion s.raw.InternalVersion = ""
s.raw.ExternalVersion = externalVersion
s.raw.MetaInsertionFactory = metaInsertion{} s.raw.MetaInsertionFactory = metaInsertion{}
s.raw.AddConversionFuncs(
s.embeddedObjectToRawExtension,
s.rawExtensionToEmbeddedObject,
)
return s return s
} }
@ -180,6 +198,10 @@ func (s *Scheme) AddKnownTypeWithName(version, kind string, obj Object) {
s.raw.AddKnownTypeWithName(version, kind, obj) s.raw.AddKnownTypeWithName(version, kind, obj)
} }
func (s *Scheme) KnownTypes(version string) map[string]reflect.Type {
return s.raw.KnownTypes(version)
}
// New returns a new API object of the given version ("" for internal // New returns a new API object of the given version ("" for internal
// representation) and name, or an error if it hasn't been registered. // representation) and name, or an error if it hasn't been registered.
func (s *Scheme) New(versionName, typeName string) (Object, error) { func (s *Scheme) New(versionName, typeName string) (Object, error) {
@ -236,12 +258,7 @@ func FindJSONBase(obj Object) (JSONBaseInterface, error) {
return g, nil return g, nil
} }
// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests. // EncodeToVersion turns the given api object into an appropriate JSON string.
func (s *Scheme) EncodeOrDie(obj Object) string {
return s.raw.EncodeOrDie(obj)
}
// Encode turns the given api object into an appropriate JSON string.
// Will return an error if the object doesn't have an embedded JSONBase. // Will return an error if the object doesn't have an embedded JSONBase.
// Obj may be a pointer to a struct, or a struct. If a struct, a copy // Obj may be a pointer to a struct, or a struct. If a struct, a copy
// must be made. If a pointer, the object may be modified before encoding, // must be made. If a pointer, the object may be modified before encoding,
@ -269,17 +286,6 @@ func (s *Scheme) EncodeOrDie(obj Object) string {
// change the memory format yet not break compatibility with any stored // change the memory format yet not break compatibility with any stored
// objects, whether they be in our storage layer (e.g., etcd), or in user's // objects, whether they be in our storage layer (e.g., etcd), or in user's
// config files. // config files.
//
// TODO/next steps: When we add our second versioned type, this package will
// need a version of Encode that lets you choose the wire version. A configurable
// default will be needed, to allow operating in clusters that haven't yet
// upgraded.
//
func (s *Scheme) Encode(obj Object) (data []byte, err error) {
return s.raw.Encode(obj)
}
// EncodeToVersion is like Encode, but lets you specify the destination version.
func (s *Scheme) EncodeToVersion(obj Object, destVersion string) (data []byte, err error) { func (s *Scheme) EncodeToVersion(obj Object, destVersion string) (data []byte, err error) {
return s.raw.EncodeToVersion(obj, destVersion) return s.raw.EncodeToVersion(obj, destVersion)
} }
@ -332,10 +338,10 @@ func (s *Scheme) DecodeInto(data []byte, obj Object) error {
return s.raw.DecodeInto(data, obj) return s.raw.DecodeInto(data, obj)
} }
// Does a deep copy of an API object. Useful mostly for tests. // Copy does a deep copy of an API object. Useful mostly for tests.
// TODO(dbsmith): implement directly instead of via Encode/Decode // TODO(dbsmith): implement directly instead of via Encode/Decode
func (s *Scheme) Copy(obj Object) (Object, error) { func (s *Scheme) Copy(obj Object) (Object, error) {
data, err := s.Encode(obj) data, err := s.EncodeToVersion(obj, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -350,6 +356,26 @@ func (s *Scheme) CopyOrDie(obj Object) Object {
return newObj return newObj
} }
func ObjectDiff(a, b Object) string {
ab, err := json.Marshal(a)
if err != nil {
panic(fmt.Sprintf("a: %v", err))
}
bb, err := json.Marshal(b)
if err != nil {
panic(fmt.Sprintf("b: %v", err))
}
return util.StringDiff(string(ab), string(bb))
// An alternate diff attempt, in case json isn't showing you
// the difference. (reflect.DeepEqual makes a distinction between
// nil and empty slices, for example.)
return util.StringDiff(
fmt.Sprintf("%#v", a),
fmt.Sprintf("%#v", b),
)
}
// metaInsertion implements conversion.MetaInsertionFactory, which lets the conversion // metaInsertion implements conversion.MetaInsertionFactory, which lets the conversion
// package figure out how to encode our object's types and versions. These fields are // package figure out how to encode our object's types and versions. These fields are
// located in our JSONBase. // located in our JSONBase.

View File

@ -43,14 +43,15 @@ func (*InternalSimple) IsAnAPIObject() {}
func (*ExternalSimple) IsAnAPIObject() {} func (*ExternalSimple) IsAnAPIObject() {}
func TestScheme(t *testing.T) { func TestScheme(t *testing.T) {
runtime.DefaultScheme.AddKnownTypeWithName("", "Simple", &InternalSimple{}) scheme := runtime.NewScheme()
runtime.DefaultScheme.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{}) scheme.AddKnownTypeWithName("", "Simple", &InternalSimple{})
scheme.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{})
internalToExternalCalls := 0 internalToExternalCalls := 0
externalToInternalCalls := 0 externalToInternalCalls := 0
// Register functions to verify that scope.Meta() gets set correctly. // Register functions to verify that scope.Meta() gets set correctly.
err := runtime.DefaultScheme.AddConversionFuncs( err := scheme.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
if e, a := "", scope.Meta().SrcVersion; e != a { if e, a := "", scope.Meta().SrcVersion; e != a {
t.Errorf("Expected '%v', got '%v'", e, a) t.Errorf("Expected '%v', got '%v'", e, a)
@ -85,10 +86,10 @@ func TestScheme(t *testing.T) {
// Test Encode, Decode, and DecodeInto // Test Encode, Decode, and DecodeInto
obj := runtime.Object(simple) obj := runtime.Object(simple)
data, err := runtime.DefaultScheme.EncodeToVersion(obj, "externalVersion") data, err := scheme.EncodeToVersion(obj, "externalVersion")
obj2, err2 := runtime.DefaultScheme.Decode(data) obj2, err2 := scheme.Decode(data)
obj3 := &InternalSimple{} obj3 := &InternalSimple{}
err3 := runtime.DefaultScheme.DecodeInto(data, obj3) err3 := scheme.DecodeInto(data, obj3)
if err != nil || err2 != nil { if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v' '%v'", err, err2, err3) t.Fatalf("Failure: '%v' '%v' '%v'", err, err2, err3)
} }
@ -104,7 +105,7 @@ func TestScheme(t *testing.T) {
// Test Convert // Test Convert
external := &ExternalSimple{} external := &ExternalSimple{}
err = runtime.DefaultScheme.Convert(simple, external) err = scheme.Convert(simple, external)
if err != nil { if err != nil {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
} }
@ -123,12 +124,13 @@ func TestScheme(t *testing.T) {
} }
func TestBadJSONRejection(t *testing.T) { func TestBadJSONRejection(t *testing.T) {
scheme := runtime.NewScheme()
badJSONMissingKind := []byte(`{ }`) badJSONMissingKind := []byte(`{ }`)
if _, err := runtime.DefaultScheme.Decode(badJSONMissingKind); err == nil { if _, err := scheme.Decode(badJSONMissingKind); err == nil {
t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
} }
badJSONUnknownType := []byte(`{"kind": "bar"}`) badJSONUnknownType := []byte(`{"kind": "bar"}`)
if _, err1 := runtime.DefaultScheme.Decode(badJSONUnknownType); err1 == nil { if _, err1 := scheme.Decode(badJSONUnknownType); err1 == nil {
t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
} }
/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
@ -163,12 +165,13 @@ func (*ExternalExtensionType) IsAnAPIObject() {}
func (*InternalExtensionType) IsAnAPIObject() {} func (*InternalExtensionType) IsAnAPIObject() {}
func TestExtensionMapping(t *testing.T) { func TestExtensionMapping(t *testing.T) {
runtime.DefaultScheme.AddKnownTypeWithName("", "ExtensionType", &InternalExtensionType{}) scheme := runtime.NewScheme()
runtime.DefaultScheme.AddKnownTypeWithName("", "A", &ExtensionA{}) scheme.AddKnownTypeWithName("", "ExtensionType", &InternalExtensionType{})
runtime.DefaultScheme.AddKnownTypeWithName("", "B", &ExtensionB{}) scheme.AddKnownTypeWithName("", "A", &ExtensionA{})
runtime.DefaultScheme.AddKnownTypeWithName("testExternal", "ExtensionType", &ExternalExtensionType{}) scheme.AddKnownTypeWithName("", "B", &ExtensionB{})
runtime.DefaultScheme.AddKnownTypeWithName("testExternal", "A", &ExtensionA{}) scheme.AddKnownTypeWithName("testExternal", "ExtensionType", &ExternalExtensionType{})
runtime.DefaultScheme.AddKnownTypeWithName("testExternal", "B", &ExtensionB{}) scheme.AddKnownTypeWithName("testExternal", "A", &ExtensionA{})
scheme.AddKnownTypeWithName("testExternal", "B", &ExtensionB{})
table := []struct { table := []struct {
obj runtime.Object obj runtime.Object
@ -187,14 +190,14 @@ func TestExtensionMapping(t *testing.T) {
} }
for _, item := range table { for _, item := range table {
gotEncoded, err := runtime.DefaultScheme.EncodeToVersion(item.obj, "testExternal") gotEncoded, err := scheme.EncodeToVersion(item.obj, "testExternal")
if err != nil { if err != nil {
t.Errorf("unexpected error '%v' (%#v)", err, item.obj) t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
} else if e, a := item.encoded, string(gotEncoded); e != a { } else if e, a := item.encoded, string(gotEncoded); e != a {
t.Errorf("expected %v, got %v", e, a) t.Errorf("expected %v, got %v", e, a)
} }
gotDecoded, err := runtime.DefaultScheme.Decode([]byte(item.encoded)) gotDecoded, err := scheme.Decode([]byte(item.encoded))
if err != nil { if err != nil {
t.Errorf("unexpected error '%v' (%v)", err, item.encoded) t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
} else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) {
@ -209,3 +212,25 @@ func TestExtensionMapping(t *testing.T) {
} }
} }
} }
func TestEncode(t *testing.T) {
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "Simple", &InternalSimple{})
scheme.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{})
codec := runtime.CodecFor(scheme, "externalVersion")
test := &InternalSimple{
TestString: "I'm the same",
}
obj := runtime.Object(test)
data, err := codec.Encode(obj)
obj2, err2 := codec.Decode(data)
if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v'", err, err2)
}
if _, ok := obj2.(*InternalSimple); !ok {
t.Fatalf("Got wrong type")
}
if !reflect.DeepEqual(obj2, test) {
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &test, obj2)
}
}

View File

@ -23,6 +23,7 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"

View File

@ -24,6 +24,7 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/coreos/go-etcd/etcd" "github.com/coreos/go-etcd/etcd"
@ -42,13 +43,14 @@ type TestResource struct {
func (*TestResource) IsAnAPIObject() {} func (*TestResource) IsAnAPIObject() {}
var scheme *runtime.Scheme var scheme *runtime.Scheme
var codec = latest.Codec var codec runtime.Codec
var versioner = runtime.DefaultResourceVersioner var versioner = runtime.NewJSONBaseResourceVersioner()
func init() { func init() {
scheme = runtime.NewScheme("", "v1beta1") scheme = runtime.NewScheme()
scheme.AddKnownTypes("", &TestResource{}) scheme.AddKnownTypes("", &TestResource{})
scheme.AddKnownTypes("v1beta1", &TestResource{}) scheme.AddKnownTypes("v1beta1", &TestResource{})
codec = runtime.CodecFor(scheme, "v1beta1")
} }
func TestIsEtcdNotFound(t *testing.T) { func TestIsEtcdNotFound(t *testing.T) {
@ -93,7 +95,7 @@ func TestExtractList(t *testing.T) {
} }
var got []api.Pod var got []api.Pod
helper := EtcdHelper{fakeClient, codec, versioner} helper := EtcdHelper{fakeClient, latest.Codec, versioner}
resourceVersion := uint64(0) resourceVersion := uint64(0)
err := helper.ExtractList("/some/key", &got, &resourceVersion) err := helper.ExtractList("/some/key", &got, &resourceVersion)
if err != nil { if err != nil {
@ -114,7 +116,7 @@ func TestExtractObj(t *testing.T) {
fakeClient := NewFakeEtcdClient(t) fakeClient := NewFakeEtcdClient(t)
expect := api.Pod{JSONBase: api.JSONBase{ID: "foo"}} expect := api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
fakeClient.Set("/some/key", util.EncodeJSON(expect), 0) fakeClient.Set("/some/key", util.EncodeJSON(expect), 0)
helper := EtcdHelper{fakeClient, codec, versioner} helper := EtcdHelper{fakeClient, latest.Codec, versioner}
var got api.Pod var got api.Pod
err := helper.ExtractObj("/some/key", &got, false) err := helper.ExtractObj("/some/key", &got, false)
if err != nil { if err != nil {
@ -168,12 +170,12 @@ func TestExtractObjNotFoundErr(t *testing.T) {
func TestSetObj(t *testing.T) { func TestSetObj(t *testing.T) {
obj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} obj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
fakeClient := NewFakeEtcdClient(t) fakeClient := NewFakeEtcdClient(t)
helper := EtcdHelper{fakeClient, codec, versioner} helper := EtcdHelper{fakeClient, latest.Codec, versioner}
err := helper.SetObj("/some/key", obj) err := helper.SetObj("/some/key", obj)
if err != nil { if err != nil {
t.Errorf("Unexpected error %#v", err) t.Errorf("Unexpected error %#v", err)
} }
data, err := codec.Encode(obj) data, err := latest.Codec.Encode(obj)
if err != nil { if err != nil {
t.Errorf("Unexpected error %#v", err) t.Errorf("Unexpected error %#v", err)
} }
@ -191,18 +193,18 @@ func TestSetObjWithVersion(t *testing.T) {
fakeClient.Data["/some/key"] = EtcdResponseWithError{ fakeClient.Data["/some/key"] = EtcdResponseWithError{
R: &etcd.Response{ R: &etcd.Response{
Node: &etcd.Node{ Node: &etcd.Node{
Value: latest.Codec.EncodeOrDie(obj), Value: runtime.EncodeOrDie(latest.Codec, obj),
ModifiedIndex: 1, ModifiedIndex: 1,
}, },
}, },
} }
helper := EtcdHelper{fakeClient, codec, versioner} helper := EtcdHelper{fakeClient, latest.Codec, versioner}
err := helper.SetObj("/some/key", obj) err := helper.SetObj("/some/key", obj)
if err != nil { if err != nil {
t.Fatalf("Unexpected error %#v", err) t.Fatalf("Unexpected error %#v", err)
} }
data, err := codec.Encode(obj) data, err := latest.Codec.Encode(obj)
if err != nil { if err != nil {
t.Fatalf("Unexpected error %#v", err) t.Fatalf("Unexpected error %#v", err)
} }
@ -216,12 +218,12 @@ func TestSetObjWithVersion(t *testing.T) {
func TestSetObjWithoutResourceVersioner(t *testing.T) { func TestSetObjWithoutResourceVersioner(t *testing.T) {
obj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} obj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
fakeClient := NewFakeEtcdClient(t) fakeClient := NewFakeEtcdClient(t)
helper := EtcdHelper{fakeClient, codec, nil} helper := EtcdHelper{fakeClient, latest.Codec, nil}
err := helper.SetObj("/some/key", obj) err := helper.SetObj("/some/key", obj)
if err != nil { if err != nil {
t.Errorf("Unexpected error %#v", err) t.Errorf("Unexpected error %#v", err)
} }
data, err := codec.Encode(obj) data, err := latest.Codec.Encode(obj)
if err != nil { if err != nil {
t.Errorf("Unexpected error %#v", err) t.Errorf("Unexpected error %#v", err)
} }
@ -235,7 +237,6 @@ func TestSetObjWithoutResourceVersioner(t *testing.T) {
func TestAtomicUpdate(t *testing.T) { func TestAtomicUpdate(t *testing.T) {
fakeClient := NewFakeEtcdClient(t) fakeClient := NewFakeEtcdClient(t)
fakeClient.TestIndex = true fakeClient.TestIndex = true
codec := scheme
helper := EtcdHelper{fakeClient, codec, runtime.NewJSONBaseResourceVersioner()} helper := EtcdHelper{fakeClient, codec, runtime.NewJSONBaseResourceVersioner()}
// Create a new node. // Create a new node.
@ -290,7 +291,7 @@ func TestAtomicUpdate(t *testing.T) {
func TestAtomicUpdateNoChange(t *testing.T) { func TestAtomicUpdateNoChange(t *testing.T) {
fakeClient := NewFakeEtcdClient(t) fakeClient := NewFakeEtcdClient(t)
fakeClient.TestIndex = true fakeClient.TestIndex = true
helper := EtcdHelper{fakeClient, scheme, runtime.NewJSONBaseResourceVersioner()} helper := EtcdHelper{fakeClient, codec, runtime.NewJSONBaseResourceVersioner()}
// Create a new node. // Create a new node.
fakeClient.ExpectNotFoundGet("/some/key") fakeClient.ExpectNotFoundGet("/some/key")
@ -321,7 +322,6 @@ func TestAtomicUpdateNoChange(t *testing.T) {
func TestAtomicUpdate_CreateCollision(t *testing.T) { func TestAtomicUpdate_CreateCollision(t *testing.T) {
fakeClient := NewFakeEtcdClient(t) fakeClient := NewFakeEtcdClient(t)
fakeClient.TestIndex = true fakeClient.TestIndex = true
codec := scheme
helper := EtcdHelper{fakeClient, codec, runtime.NewJSONBaseResourceVersioner()} helper := EtcdHelper{fakeClient, codec, runtime.NewJSONBaseResourceVersioner()}
fakeClient.ExpectNotFoundGet("/some/key") fakeClient.ExpectNotFoundGet("/some/key")

View File

@ -23,12 +23,14 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/coreos/go-etcd/etcd" "github.com/coreos/go-etcd/etcd"
) )
func TestWatchInterpretations(t *testing.T) { func TestWatchInterpretations(t *testing.T) {
codec := latest.Codec
// Declare some pods to make the test cases compact. // Declare some pods to make the test cases compact.
podFoo := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} podFoo := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
podBar := &api.Pod{JSONBase: api.JSONBase{ID: "bar"}} podBar := &api.Pod{JSONBase: api.JSONBase{ID: "bar"}}
@ -48,62 +50,62 @@ func TestWatchInterpretations(t *testing.T) {
}{ }{
"create": { "create": {
actions: []string{"create", "get"}, actions: []string{"create", "get"},
nodeValue: latest.Codec.EncodeOrDie(podBar), nodeValue: runtime.EncodeOrDie(codec, podBar),
expectEmit: true, expectEmit: true,
expectType: watch.Added, expectType: watch.Added,
expectObject: podBar, expectObject: podBar,
}, },
"create but filter blocks": { "create but filter blocks": {
actions: []string{"create", "get"}, actions: []string{"create", "get"},
nodeValue: latest.Codec.EncodeOrDie(podFoo), nodeValue: runtime.EncodeOrDie(codec, podFoo),
expectEmit: false, expectEmit: false,
}, },
"delete": { "delete": {
actions: []string{"delete"}, actions: []string{"delete"},
prevNodeValue: latest.Codec.EncodeOrDie(podBar), prevNodeValue: runtime.EncodeOrDie(codec, podBar),
expectEmit: true, expectEmit: true,
expectType: watch.Deleted, expectType: watch.Deleted,
expectObject: podBar, expectObject: podBar,
}, },
"delete but filter blocks": { "delete but filter blocks": {
actions: []string{"delete"}, actions: []string{"delete"},
nodeValue: latest.Codec.EncodeOrDie(podFoo), nodeValue: runtime.EncodeOrDie(codec, podFoo),
expectEmit: false, expectEmit: false,
}, },
"modify appears to create 1": { "modify appears to create 1": {
actions: []string{"set", "compareAndSwap"}, actions: []string{"set", "compareAndSwap"},
nodeValue: latest.Codec.EncodeOrDie(podBar), nodeValue: runtime.EncodeOrDie(codec, podBar),
expectEmit: true, expectEmit: true,
expectType: watch.Added, expectType: watch.Added,
expectObject: podBar, expectObject: podBar,
}, },
"modify appears to create 2": { "modify appears to create 2": {
actions: []string{"set", "compareAndSwap"}, actions: []string{"set", "compareAndSwap"},
prevNodeValue: latest.Codec.EncodeOrDie(podFoo), prevNodeValue: runtime.EncodeOrDie(codec, podFoo),
nodeValue: latest.Codec.EncodeOrDie(podBar), nodeValue: runtime.EncodeOrDie(codec, podBar),
expectEmit: true, expectEmit: true,
expectType: watch.Added, expectType: watch.Added,
expectObject: podBar, expectObject: podBar,
}, },
"modify appears to delete": { "modify appears to delete": {
actions: []string{"set", "compareAndSwap"}, actions: []string{"set", "compareAndSwap"},
prevNodeValue: latest.Codec.EncodeOrDie(podBar), prevNodeValue: runtime.EncodeOrDie(codec, podBar),
nodeValue: latest.Codec.EncodeOrDie(podFoo), nodeValue: runtime.EncodeOrDie(codec, podFoo),
expectEmit: true, expectEmit: true,
expectType: watch.Deleted, expectType: watch.Deleted,
expectObject: podBar, // Should return last state that passed the filter! expectObject: podBar, // Should return last state that passed the filter!
}, },
"modify modifies": { "modify modifies": {
actions: []string{"set", "compareAndSwap"}, actions: []string{"set", "compareAndSwap"},
prevNodeValue: latest.Codec.EncodeOrDie(podBar), prevNodeValue: runtime.EncodeOrDie(codec, podBar),
nodeValue: latest.Codec.EncodeOrDie(podBaz), nodeValue: runtime.EncodeOrDie(codec, podBaz),
expectEmit: true, expectEmit: true,
expectType: watch.Modified, expectType: watch.Modified,
expectObject: podBaz, expectObject: podBaz,
}, },
"modify ignores": { "modify ignores": {
actions: []string{"set", "compareAndSwap"}, actions: []string{"set", "compareAndSwap"},
nodeValue: latest.Codec.EncodeOrDie(podFoo), nodeValue: runtime.EncodeOrDie(codec, podFoo),
expectEmit: false, expectEmit: false,
}, },
} }
@ -197,6 +199,7 @@ func TestWatchInterpretation_ResponseBadData(t *testing.T) {
} }
func TestWatch(t *testing.T) { func TestWatch(t *testing.T) {
codec := latest.Codec
fakeClient := NewFakeEtcdClient(t) fakeClient := NewFakeEtcdClient(t)
fakeClient.expectNotFoundGetSet["/some/key"] = struct{}{} fakeClient.expectNotFoundGetSet["/some/key"] = struct{}{}
h := EtcdHelper{fakeClient, codec, versioner} h := EtcdHelper{fakeClient, codec, versioner}
@ -243,6 +246,7 @@ func TestWatch(t *testing.T) {
} }
func TestWatchEtcdState(t *testing.T) { func TestWatchEtcdState(t *testing.T) {
codec := latest.Codec
type T struct { type T struct {
Type watch.EventType Type watch.EventType
Endpoints []string Endpoints []string
@ -259,7 +263,7 @@ func TestWatchEtcdState(t *testing.T) {
{ {
Action: "create", Action: "create",
Node: &etcd.Node{ Node: &etcd.Node{
Value: string(latest.Codec.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), Value: string(runtime.EncodeOrDie(codec, &api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})),
}, },
}, },
}, },
@ -273,12 +277,12 @@ func TestWatchEtcdState(t *testing.T) {
{ {
Action: "compareAndSwap", Action: "compareAndSwap",
Node: &etcd.Node{ Node: &etcd.Node{
Value: string(latest.Codec.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:9000"}})), Value: string(runtime.EncodeOrDie(codec, &api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:9000"}})),
CreatedIndex: 1, CreatedIndex: 1,
ModifiedIndex: 2, ModifiedIndex: 2,
}, },
PrevNode: &etcd.Node{ PrevNode: &etcd.Node{
Value: string(latest.Codec.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), Value: string(runtime.EncodeOrDie(codec, &api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})),
CreatedIndex: 1, CreatedIndex: 1,
ModifiedIndex: 1, ModifiedIndex: 1,
}, },
@ -295,7 +299,7 @@ func TestWatchEtcdState(t *testing.T) {
R: &etcd.Response{ R: &etcd.Response{
Action: "get", Action: "get",
Node: &etcd.Node{ Node: &etcd.Node{
Value: string(latest.Codec.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), Value: string(runtime.EncodeOrDie(codec, &api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})),
CreatedIndex: 1, CreatedIndex: 1,
ModifiedIndex: 1, ModifiedIndex: 1,
}, },
@ -308,12 +312,12 @@ func TestWatchEtcdState(t *testing.T) {
{ {
Action: "compareAndSwap", Action: "compareAndSwap",
Node: &etcd.Node{ Node: &etcd.Node{
Value: string(latest.Codec.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:9000"}})), Value: string(runtime.EncodeOrDie(codec, &api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:9000"}})),
CreatedIndex: 1, CreatedIndex: 1,
ModifiedIndex: 2, ModifiedIndex: 2,
}, },
PrevNode: &etcd.Node{ PrevNode: &etcd.Node{
Value: string(latest.Codec.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), Value: string(runtime.EncodeOrDie(codec, &api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})),
CreatedIndex: 1, CreatedIndex: 1,
ModifiedIndex: 1, ModifiedIndex: 1,
}, },
@ -359,6 +363,7 @@ func TestWatchEtcdState(t *testing.T) {
} }
func TestWatchFromZeroIndex(t *testing.T) { func TestWatchFromZeroIndex(t *testing.T) {
codec := latest.Codec
pod := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} pod := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
testCases := map[string]struct { testCases := map[string]struct {
@ -370,7 +375,7 @@ func TestWatchFromZeroIndex(t *testing.T) {
EtcdResponseWithError{ EtcdResponseWithError{
R: &etcd.Response{ R: &etcd.Response{
Node: &etcd.Node{ Node: &etcd.Node{
Value: latest.Codec.EncodeOrDie(pod), Value: runtime.EncodeOrDie(codec, pod),
CreatedIndex: 1, CreatedIndex: 1,
ModifiedIndex: 1, ModifiedIndex: 1,
}, },
@ -385,7 +390,7 @@ func TestWatchFromZeroIndex(t *testing.T) {
EtcdResponseWithError{ EtcdResponseWithError{
R: &etcd.Response{ R: &etcd.Response{
Node: &etcd.Node{ Node: &etcd.Node{
Value: latest.Codec.EncodeOrDie(pod), Value: runtime.EncodeOrDie(codec, pod),
CreatedIndex: 1, CreatedIndex: 1,
ModifiedIndex: 2, ModifiedIndex: 2,
}, },
@ -434,6 +439,7 @@ func TestWatchFromZeroIndex(t *testing.T) {
} }
func TestWatchListFromZeroIndex(t *testing.T) { func TestWatchListFromZeroIndex(t *testing.T) {
codec := latest.Codec
pod := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} pod := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
fakeClient := NewFakeEtcdClient(t) fakeClient := NewFakeEtcdClient(t)
@ -443,13 +449,13 @@ func TestWatchListFromZeroIndex(t *testing.T) {
Dir: true, Dir: true,
Nodes: etcd.Nodes{ Nodes: etcd.Nodes{
&etcd.Node{ &etcd.Node{
Value: latest.Codec.EncodeOrDie(pod), Value: runtime.EncodeOrDie(codec, pod),
CreatedIndex: 1, CreatedIndex: 1,
ModifiedIndex: 1, ModifiedIndex: 1,
Nodes: etcd.Nodes{}, Nodes: etcd.Nodes{},
}, },
&etcd.Node{ &etcd.Node{
Value: latest.Codec.EncodeOrDie(pod), Value: runtime.EncodeOrDie(codec, pod),
CreatedIndex: 2, CreatedIndex: 2,
ModifiedIndex: 2, ModifiedIndex: 2,
Nodes: etcd.Nodes{}, Nodes: etcd.Nodes{},

View File

@ -24,6 +24,7 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
@ -113,7 +114,7 @@ func TestPollMinions(t *testing.T) {
ml := &api.MinionList{Items: item.minions} ml := &api.MinionList{Items: item.minions}
handler := util.FakeHandler{ handler := util.FakeHandler{
StatusCode: 200, StatusCode: 200,
ResponseBody: latest.Codec.EncodeOrDie(ml), ResponseBody: runtime.EncodeOrDie(latest.Codec, ml),
T: t, T: t,
} }
mux := http.NewServeMux() mux := http.NewServeMux()
@ -140,7 +141,7 @@ func TestDefaultErrorFunc(t *testing.T) {
testPod := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} testPod := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
handler := util.FakeHandler{ handler := util.FakeHandler{
StatusCode: 200, StatusCode: 200,
ResponseBody: latest.Codec.EncodeOrDie(testPod), ResponseBody: runtime.EncodeOrDie(latest.Codec, testPod),
T: t, T: t,
} }
mux := http.NewServeMux() mux := http.NewServeMux()
@ -259,7 +260,7 @@ func TestBind(t *testing.T) {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
continue continue
} }
expectedBody := latest.Codec.EncodeOrDie(item.binding) expectedBody := runtime.EncodeOrDie(latest.Codec, item.binding)
handler.ValidateRequest(t, "/api/v1beta1/bindings", "POST", &expectedBody) handler.ValidateRequest(t, "/api/v1beta1/bindings", "POST", &expectedBody)
} }
} }

View File

@ -22,6 +22,8 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@ -90,9 +92,9 @@ func TestExtractObj(t *testing.T) {
func TestWatch(t *testing.T) { func TestWatch(t *testing.T) {
client := newEtcdClient() client := newEtcdClient()
helper := tools.EtcdHelper{Client: client, Codec: latest.Codec, ResourceVersioner: runtime.DefaultResourceVersioner} helper := tools.EtcdHelper{Client: client, Codec: latest.Codec, ResourceVersioner: latest.ResourceVersioner}
withEtcdKey(func(key string) { withEtcdKey(func(key string) {
resp, err := client.Set(key, latest.Codec.EncodeOrDie(&api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0) resp, err := client.Set(key, runtime.EncodeOrDie(v1beta1.Codec, &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }