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

View File

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

View File

@ -25,7 +25,7 @@ import (
"testing"
"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/runtime"
"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"
)
var Scheme = runtime.NewScheme()
func init() {
runtime.DefaultScheme.AddKnownTypes("",
Scheme.AddKnownTypes("",
&PodList{},
&Pod{},
&ReplicationControllerList{},

View File

@ -17,14 +17,14 @@ limitations under the License.
package api_test
import (
"encoding/json"
"flag"
"fmt"
"reflect"
"testing"
"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/util"
"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 {
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) {
func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
name := reflect.TypeOf(source).Elem().Name()
apiObjectFuzzer.Fuzz(source)
j, err := runtime.FindJSONBase(source)
@ -135,30 +115,30 @@ func runTest(t *testing.T, source runtime.Object) {
j.SetKind("")
j.SetAPIVersion("")
data, err := latest.Codec.Encode(source)
data, err := codec.Encode(source)
if err != nil {
t.Errorf("%v: %v (%#v)", name, err, source)
return
}
obj2, err := latest.Codec.Decode(data)
obj2, err := codec.Decode(data)
if err != nil {
t.Errorf("%v: %v", name, err)
return
} else {
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
}
}
obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface().(runtime.Object)
err = latest.Codec.DecodeInto(data, obj3)
err = codec.DecodeInto(data, obj3)
if err != nil {
t.Errorf("2: %v: %v", name, err)
return
} else {
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
}
}
@ -184,7 +164,8 @@ func TestTypes(t *testing.T) {
for _, item := range table {
// Try a few times, since runTest uses random values.
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
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/fsouza/go-dockerclient"
)
@ -623,13 +621,3 @@ type ServerOpList struct {
}
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
import (
// Alias this so it can be easily changed when we cut the next version.
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
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
@ -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"
"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) {
nonCanonical := []v1beta1.EnvVar{

View File

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

View File

@ -17,9 +17,7 @@ limitations under the License.
package v1beta1
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/fsouza/go-dockerclient"
)
@ -625,13 +623,3 @@ type ServerOpList struct {
}
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
import (
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
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

View File

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

View File

@ -17,9 +17,7 @@ limitations under the License.
package v1beta2
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/fsouza/go-dockerclient"
)
@ -55,8 +53,8 @@ type ContainerManifest struct {
// Required: This must be a DNS_SUBDOMAIN.
// TODO: ID on Manifest is deprecated and will be removed in the future.
ID string `yaml:"id" json:"id"`
// TODO: UUID on Manifext is deprecated in the future once we are done
// with the API refactory. It is required for now to determine the instance
// TODO: UUID on Manifest is deprecated in the future once we are done
// with the API refactoring. It is required for now to determine the instance
// of a Pod.
UUID string `yaml:"uuid,omitempty" json:"uuid,omitempty"`
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
// 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 return code of zero is treated as 'Healthy', non-zero is 'Unhealthy'
Command []string `yaml:"command,omitempty" json:"command,omitempty"`
}
@ -210,7 +207,6 @@ type Container struct {
}
// 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.
type Handler struct {
// 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"`
}
func (*JSONBase) IsAnAPIObject() {}
// PodStatus represents a status of a pod.
type PodStatus string
@ -303,18 +297,19 @@ type ContainerStatus struct {
}
// 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 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.
type RestartPolicyOnFailure struct{}
type RestartPolicyNever 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
// is RestartPolicyAlways.
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
// 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
// upon. To allow marshalling/unmarshalling, we copied the client's structs and added
// json/yaml tags.
// TODO: Make real decisions about what our info should look like.
// upon.
// TODO: Make real decisions about what our info should look like. Re-enable fuzz test
// when we have done this.
Info PodInfo `json:"info,omitempty" yaml:"info,omitempty"`
}
@ -550,14 +545,14 @@ const (
// resource.
// "id" string - the identifier of the missing resource
// Status code 404
StatusReasonNotFound StatusReason = "notFound"
StatusReasonNotFound StatusReason = "not_found"
// StatusReasonAlreadyExists means the resource you are creating already exists.
// Details (optional):
// "kind" string - the kind attribute of the conflicting resource
// "id" string - the identifier of the conflicting resource
// Status code 409
StatusReasonAlreadyExists StatusReason = "alreadyExists"
StatusReasonAlreadyExists StatusReason = "already_exists"
// StatusReasonConflict means the requested update operation cannot be completed
// due to a conflict in the operation. The client may need to alter the request.
@ -565,6 +560,19 @@ const (
// conflict.
// Status code 409
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
@ -625,13 +633,3 @@ type ServerOpList struct {
}
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.
*/
package v1beta1
package v1beta3
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/fsouza/go-dockerclient"
)
@ -55,8 +53,8 @@ type ContainerManifest struct {
// Required: This must be a DNS_SUBDOMAIN.
// TODO: ID on Manifest is deprecated and will be removed in the future.
ID string `yaml:"id" json:"id"`
// TODO: UUID on Manifext is deprecated in the future once we are done
// with the API refactory. It is required for now to determine the instance
// TODO: UUID on Manifest is deprecated in the future once we are done
// with the API refactoring. It is required for now to determine the instance
// of a Pod.
UUID string `yaml:"uuid,omitempty" json:"uuid,omitempty"`
Volumes []Volume `yaml:"volumes" json:"volumes"`
@ -124,22 +122,13 @@ type VolumeMount struct {
// Optional: Defaults to false (read-write).
ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"`
// 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"`
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.
type EnvVar struct {
// 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"`
Key string `yaml:"key,omitempty" json:"key,omitempty"`
// Optional: defaults to "".
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
// 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 return code of zero is treated as 'Healthy', non-zero is 'Unhealthy'
Command []string `yaml:"command,omitempty" json:"command,omitempty"`
}
@ -210,7 +198,6 @@ type Container struct {
}
// 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.
type Handler struct {
// 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"`
}
func (*JSONBase) IsAnAPIObject() {}
// PodStatus represents a status of a pod.
type PodStatus string
@ -303,18 +288,19 @@ type ContainerStatus struct {
}
// 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 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.
type RestartPolicyOnFailure struct{}
type RestartPolicyNever 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
// is RestartPolicyAlways.
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
// 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
// upon. To allow marshalling/unmarshalling, we copied the client's structs and added
// json/yaml tags.
// TODO: Make real decisions about what our info should look like.
// upon.
// TODO: Make real decisions about what our info should look like. Re-enable fuzz test
// when we have done this.
Info PodInfo `json:"info,omitempty" yaml:"info,omitempty"`
}
@ -451,9 +437,6 @@ func (*Minion) IsAnAPIObject() {}
// MinionList is a list of minions.
type MinionList struct {
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"`
}
@ -550,14 +533,14 @@ const (
// resource.
// "id" string - the identifier of the missing resource
// Status code 404
StatusReasonNotFound StatusReason = "notFound"
StatusReasonNotFound StatusReason = "not_found"
// StatusReasonAlreadyExists means the resource you are creating already exists.
// Details (optional):
// "kind" string - the kind attribute of the conflicting resource
// "id" string - the identifier of the conflicting resource
// Status code 409
StatusReasonAlreadyExists StatusReason = "alreadyExists"
StatusReasonAlreadyExists StatusReason = "already_exists"
// StatusReasonConflict means the requested update operation cannot be completed
// due to a conflict in the operation. The client may need to alter the request.
@ -565,6 +548,19 @@ const (
// conflict.
// Status code 409
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
@ -625,13 +621,3 @@ type ServerOpList struct {
}
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"
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/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@ -46,8 +47,8 @@ func convert(obj runtime.Object) (runtime.Object, error) {
var codec = latest.Codec
func init() {
latest.Codec.AddKnownTypes("", &Simple{}, &SimpleList{})
latest.Codec.AddKnownTypes("v1beta1", &Simple{}, &SimpleList{})
api.Scheme.AddKnownTypes("", &Simple{}, &SimpleList{})
api.Scheme.AddKnownTypes(latest.Version, &Simple{}, &SimpleList{})
}
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) {
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) {

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
// 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" {
websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(w), req)
} 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.
type WatchServer struct {
watching watch.Interface
codec runtime.Codec
}
// HandleWS implements a websocket handler.
@ -111,15 +112,17 @@ func (w *WatchServer) HandleWS(ws *websocket.Conn) {
// End of results.
return
}
err := websocket.JSON.Send(ws, &api.WatchEvent{
Type: event.Type,
Object: runtime.EmbeddedObject{event.Object},
})
obj, err := api.NewJSONWatchEvent(w.codec, event)
if err != nil {
// Client disconnect.
w.watching.Stop()
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.
return
}
err := encoder.Encode(&api.WatchEvent{
Type: event.Type,
Object: runtime.EmbeddedObject{event.Object},
})
obj, err := api.NewJSONWatchEvent(self.codec, event)
if err != nil {
// Client disconnect.
self.watching.Stop()
return
}
if err := encoder.Encode(obj); err != nil {
// Client disconnect.
self.watching.Stop()
return
}
flusher.Flush()
}
}

View File

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

View File

@ -26,7 +26,7 @@ import (
"time"
"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/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"

View File

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

View File

@ -19,7 +19,6 @@ package client
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
@ -44,7 +43,7 @@ type Fake struct {
func (c *Fake) ListPods(selector labels.Selector) (*api.PodList, error) {
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) {
@ -74,7 +73,7 @@ func (c *Fake) ListReplicationControllers(selector labels.Selector) (*api.Replic
func (c *Fake) GetReplicationController(name string) (*api.ReplicationController, error) {
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) {
@ -129,7 +128,7 @@ func (c *Fake) WatchServices(label, field labels.Selector, resourceVersion uint6
func (c *Fake) ListEndpoints(selector labels.Selector) (*api.EndpointsList, error) {
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) {

View File

@ -28,9 +28,9 @@ import (
"time"
"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/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/golang/glog"
@ -269,7 +269,7 @@ func (r *Request) Watch() (watch.Interface, error) {
if response.StatusCode != http.StatusOK {
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.

View File

@ -29,6 +29,7 @@ import (
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@ -401,7 +402,13 @@ func TestWatch(t *testing.T) {
encoder := json.NewEncoder(w)
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()
}
}))

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package tools
package watch
import (
"encoding/json"
@ -28,6 +28,8 @@ import (
// APIEventDecoder implements the watch.Decoder interface for io.ReadClosers that
// 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 {
stream io.ReadCloser
decoder *json.Decoder

View File

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

View File

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

View File

@ -25,14 +25,14 @@ import (
// 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
// technique. The object will be converted, if necessary, into the
// s.InternalVersion type before being returned. Decode will refuse to decode
// objects without a version, because that's probably an error.
// s.InternalVersion type before being returned. Decode will not decode
// objects without version set unless InternalVersion is also "".
func (s *Scheme) Decode(data []byte) (interface{}, error) {
version, kind, err := s.DataVersionAndKind(data)
if err != nil {
return nil, err
}
if version == "" {
if version == "" && s.InternalVersion != "" {
return nil, fmt.Errorf("version not set in '%s'", string(data))
}
obj, err := s.NewObject(version, kind)

View File

@ -21,16 +21,7 @@ import (
"fmt"
)
// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
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.
// EncodeToVersion 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
// will be made, therefore it's recommended to pass a pointer to a
// 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
// 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) {
obj = maybeCopy(obj)
v, _ := enforcePtr(obj) // maybeCopy guarantees a pointer

View File

@ -66,9 +66,6 @@ type Scheme struct {
// you use "" for the internal version.
InternalVersion string
// ExternalVersion is the default external version.
ExternalVersion string
// MetaInsertionFactory is used to create an object to store and retrieve
// the version and kind information for all objects. The default uses the
// keys "version" and "kind" respectively.
@ -83,7 +80,6 @@ func NewScheme() *Scheme {
typeToKind: map[reflect.Type]string{},
converter: NewConverter(),
InternalVersion: "",
ExternalVersion: "v1",
MetaInsertionFactory: metaInsertion{},
}
s.converter.NameFunc = s.nameFunc
@ -146,6 +142,19 @@ func (s *Scheme) AddKnownTypeWithName(version, kind string, obj interface{}) {
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,
// or an error if it hasn't been registered.
func (s *Scheme) NewObject(versionName, typeName string) (interface{}, error) {

View File

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

View File

@ -26,7 +26,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
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)
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"
validateAction(client.FakeAction{Action: "update-controller", Value: newCtrl}, fakeClient.Actions[1], t)

View File

@ -21,6 +21,8 @@ import (
"testing"
"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"
"gopkg.in/v1/yaml"
)
@ -125,8 +127,9 @@ type TestParseType struct {
func (*TestParseType) IsAnAPIObject() {}
func TestParseCustomType(t *testing.T) {
latest.Codec.AddKnownTypes("", &TestParseType{})
latest.Codec.AddKnownTypes("v1beta1", &TestParseType{})
api.Scheme.AddKnownTypes("", &TestParseType{})
api.Scheme.AddKnownTypes("v1beta1", &TestParseType{})
api.Scheme.AddKnownTypes("v1beta2", &TestParseType{})
parser := NewParser(map[string]runtime.Object{
"custom": &TestParseType{},
})

View File

@ -21,8 +21,8 @@ import (
"net/http"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"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.

View File

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

View File

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

View File

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

View File

@ -20,7 +20,7 @@ import (
"net/http"
"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/client"
"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 {
storage[k] = v
}
return storage, runtime.DefaultCodec
return storage, v1beta1.Codec
}

View File

@ -39,7 +39,7 @@ import (
"time"
"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/util"
"github.com/coreos/go-etcd/etcd"

View File

@ -23,9 +23,8 @@ import (
"testing"
"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/runtime"
)
func TestNewREST(t *testing.T) {

View File

@ -26,10 +26,10 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"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/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
func TestListControllersError(t *testing.T) {

View File

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

View File

@ -22,7 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"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/registry/registrytest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -41,7 +41,7 @@ func NewTestEtcdRegistry(client tools.EtcdClient) *Registry {
func TestEtcdGetPod(t *testing.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)
pod, err := registry.GetPod("foo")
if err != nil {
@ -77,7 +77,7 @@ func TestEtcdCreatePod(t *testing.T) {
},
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)
err := registry.CreatePod(&api.Pod{
JSONBase: api.JSONBase{
@ -133,7 +133,7 @@ func TestEtcdCreatePodAlreadyExisting(t *testing.T) {
fakeClient.Data["/registry/pods/foo"] = tools.EtcdResponseWithError{
R: &etcd.Response{
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,
@ -264,7 +264,7 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) {
},
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{
{ID: "bar"},
},
@ -325,11 +325,11 @@ func TestEtcdDeletePod(t *testing.T) {
fakeClient.TestIndex = true
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"},
DesiredState: api.PodState{Host: "machine"},
}), 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{
{ID: "foo"},
},
@ -361,11 +361,11 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) {
fakeClient.TestIndex = true
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"},
DesiredState: api.PodState{Host: "machine"},
}), 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{
{ID: "foo"},
{ID: "bar"},
@ -445,13 +445,13 @@ func TestEtcdListPods(t *testing.T) {
Node: &etcd.Node{
Nodes: []*etcd.Node{
{
Value: latest.Codec.EncodeOrDie(&api.Pod{
Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{
JSONBase: api.JSONBase{ID: "foo"},
DesiredState: api.PodState{Host: "machine"},
}),
},
{
Value: latest.Codec.EncodeOrDie(&api.Pod{
Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{
JSONBase: api.JSONBase{ID: "bar"},
DesiredState: api.PodState{Host: "machine"},
}),
@ -520,10 +520,10 @@ func TestEtcdListControllers(t *testing.T) {
Node: &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) {
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)
ctrl, err := registry.GetController("foo")
if err != nil {
@ -619,7 +619,7 @@ func TestEtcdCreateController(t *testing.T) {
func TestEtcdCreateControllerAlreadyExisting(t *testing.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)
err := registry.CreateController(&api.ReplicationController{
@ -636,7 +636,7 @@ func TestEtcdUpdateController(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t)
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)
err := registry.UpdateController(&api.ReplicationController{
JSONBase: api.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex},
@ -662,10 +662,10 @@ func TestEtcdListServices(t *testing.T) {
Node: &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) {
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)
err := registry.CreateService(&api.Service{
JSONBase: api.JSONBase{ID: "foo"},
@ -723,7 +723,7 @@ func TestEtcdCreateServiceAlreadyExisting(t *testing.T) {
func TestEtcdGetService(t *testing.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)
service, err := registry.GetService("foo")
if err != nil {
@ -775,7 +775,7 @@ func TestEtcdUpdateService(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t)
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)
testService := api.Service{
JSONBase: api.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex},
@ -812,10 +812,10 @@ func TestEtcdListEndpoints(t *testing.T) {
Node: &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"},
}
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")
if err != nil {
@ -862,7 +862,7 @@ func TestEtcdUpdateEndpoints(t *testing.T) {
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)
if err != nil {

View File

@ -24,6 +24,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"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/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"

View File

@ -20,42 +20,80 @@ import (
"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
// 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
// embedded within other API types.
// 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.
if len(b) == 4 && string(b) == "null" {
a.Object = nil
return nil
return nil, nil
}
obj, err := DefaultCodec.Decode(b)
obj, err := codec.Decode(b)
if err != nil {
return err
return nil, err
}
a.Object = obj
return nil
return obj, nil
}
// MarshalJSON implements the json.Marshaler interface.
func (a EmbeddedObject) MarshalJSON() ([]byte, error) {
if a.Object == nil {
func CodecMarshalJSON(codec Codec, obj Object) ([]byte, error) {
if obj == nil {
// Encode unset/nil objects as JSON's "null".
return []byte("null"), nil
}
return DefaultCodec.Encode(a.Object)
return codec.Encode(obj)
}
// 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 {
a.Object = nil
return true
return nil, true
}
// Why does the yaml package send value as a map[interface{}]interface{}?
// 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 {
panic("yaml can't reverse its own object")
}
obj, err := DefaultCodec.Decode(b)
obj, err := codec.Decode(b)
if err != nil {
return false
return nil, false
}
a.Object = obj
return true
return obj, true
}
// GetYAML implements the yaml.Getter interface.
func (a EmbeddedObject) GetYAML() (tag string, value interface{}) {
if a.Object == nil {
func CodecGetYAML(codec Codec, obj Object) (tag string, value interface{}) {
if obj == nil {
value = "null"
return
}
// Encode returns JSON, which is conveniently a subset of YAML.
v, err := DefaultCodec.Encode(a.Object)
v, err := codec.Encode(obj)
if err != nil {
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.
*/
package runtime
package runtime_test
import (
"encoding/json"
"reflect"
"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 {
JSONBase `yaml:",inline" json:",inline"`
runtime.JSONBase `yaml:",inline" json:",inline"`
Object EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"`
EmptyObject EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"`
}
@ -31,21 +66,20 @@ type EmbeddedTest struct {
func (*EmbeddedTest) IsAnAPIObject() {}
func TestEmbeddedObject(t *testing.T) {
// TODO(dbsmith) fix EmbeddedObject to not use DefaultScheme.
s := DefaultScheme
s := scheme
s.AddKnownTypes("", &EmbeddedTest{})
s.AddKnownTypes("v1beta1", &EmbeddedTest{})
s.AddKnownTypes("v1test", &EmbeddedTest{})
outer := &EmbeddedTest{
JSONBase: JSONBase{ID: "outer"},
JSONBase: runtime.JSONBase{ID: "outer"},
Object: EmbeddedObject{
&EmbeddedTest{
JSONBase: JSONBase{ID: "inner"},
JSONBase: runtime.JSONBase{ID: "inner"},
},
},
}
wire, err := s.Encode(outer)
wire, err := s.EncodeToVersion(outer, "v1test")
if err != nil {
t.Fatalf("Unexpected encode error '%v'", err)
}

View File

@ -16,13 +16,23 @@ limitations under the License.
package runtime
// Codec defines methods for serializing and deserializing API objects.
type Codec interface {
Encode(obj Object) (data []byte, err error)
// Decoder defines methods for deserializing API objects into a given type
type Decoder interface {
Decode(data []byte) (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
// the resource version from an API object.
type ResourceVersioner interface {

View File

@ -17,16 +17,40 @@ limitations under the License.
package runtime
import (
"encoding/json"
"fmt"
"reflect"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"gopkg.in/v1/yaml"
)
var DefaultResourceVersioner ResourceVersioner = NewJSONBaseResourceVersioner()
var DefaultScheme = NewScheme("", "v1beta1")
var DefaultCodec Codec = DefaultScheme
// codecWrapper implements encoding to an alternative
// default version for a scheme.
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
// 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
// from a conversion.Scope.
func fromScope(s conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
scheme = DefaultScheme
func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
scheme = self
inVersion = s.Meta().SrcVersion
outVersion = s.Meta().DestVersion
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.
type emptyPlugin struct {
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
// given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins;
// 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 {
out.RawJSON = []byte("null")
return nil
}
// 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)
if err != nil {
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
// given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins;
// 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" {
out.Object = nil
return nil
}
// 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)
if err != nil {
return err
@ -153,13 +169,15 @@ func rawExtensionToEmbeddedObject(in *RawExtension, out *EmbeddedObject, s conve
return nil
}
// NewScheme creates a new Scheme. A default scheme is provided and accessible
// as the "DefaultScheme" variable.
func NewScheme(internalVersion, externalVersion string) *Scheme {
// NewScheme creates a new Scheme. This scheme is pluggable by default.
func NewScheme() *Scheme {
s := &Scheme{conversion.NewScheme()}
s.raw.InternalVersion = internalVersion
s.raw.ExternalVersion = externalVersion
s.raw.InternalVersion = ""
s.raw.MetaInsertionFactory = metaInsertion{}
s.raw.AddConversionFuncs(
s.embeddedObjectToRawExtension,
s.rawExtensionToEmbeddedObject,
)
return s
}
@ -180,6 +198,10 @@ func (s *Scheme) AddKnownTypeWithName(version, kind string, obj Object) {
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
// representation) and name, or an error if it hasn't been registered.
func (s *Scheme) New(versionName, typeName string) (Object, error) {
@ -236,12 +258,7 @@ func FindJSONBase(obj Object) (JSONBaseInterface, error) {
return g, nil
}
// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
func (s *Scheme) EncodeOrDie(obj Object) string {
return s.raw.EncodeOrDie(obj)
}
// Encode turns the given api object into an appropriate JSON string.
// EncodeToVersion turns the given api object into an appropriate JSON string.
// 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
// 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
// objects, whether they be in our storage layer (e.g., etcd), or in user's
// 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) {
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)
}
// 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
func (s *Scheme) Copy(obj Object) (Object, error) {
data, err := s.Encode(obj)
data, err := s.EncodeToVersion(obj, "")
if err != nil {
return nil, err
}
@ -350,6 +356,26 @@ func (s *Scheme) CopyOrDie(obj Object) Object {
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
// package figure out how to encode our object's types and versions. These fields are
// located in our JSONBase.

View File

@ -43,14 +43,15 @@ func (*InternalSimple) IsAnAPIObject() {}
func (*ExternalSimple) IsAnAPIObject() {}
func TestScheme(t *testing.T) {
runtime.DefaultScheme.AddKnownTypeWithName("", "Simple", &InternalSimple{})
runtime.DefaultScheme.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{})
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "Simple", &InternalSimple{})
scheme.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{})
internalToExternalCalls := 0
externalToInternalCalls := 0
// 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 {
if e, a := "", scope.Meta().SrcVersion; e != a {
t.Errorf("Expected '%v', got '%v'", e, a)
@ -85,10 +86,10 @@ func TestScheme(t *testing.T) {
// Test Encode, Decode, and DecodeInto
obj := runtime.Object(simple)
data, err := runtime.DefaultScheme.EncodeToVersion(obj, "externalVersion")
obj2, err2 := runtime.DefaultScheme.Decode(data)
data, err := scheme.EncodeToVersion(obj, "externalVersion")
obj2, err2 := scheme.Decode(data)
obj3 := &InternalSimple{}
err3 := runtime.DefaultScheme.DecodeInto(data, obj3)
err3 := scheme.DecodeInto(data, obj3)
if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v' '%v'", err, err2, err3)
}
@ -104,7 +105,7 @@ func TestScheme(t *testing.T) {
// Test Convert
external := &ExternalSimple{}
err = runtime.DefaultScheme.Convert(simple, external)
err = scheme.Convert(simple, external)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
@ -123,12 +124,13 @@ func TestScheme(t *testing.T) {
}
func TestBadJSONRejection(t *testing.T) {
scheme := runtime.NewScheme()
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)
}
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)
}
/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
@ -163,12 +165,13 @@ func (*ExternalExtensionType) IsAnAPIObject() {}
func (*InternalExtensionType) IsAnAPIObject() {}
func TestExtensionMapping(t *testing.T) {
runtime.DefaultScheme.AddKnownTypeWithName("", "ExtensionType", &InternalExtensionType{})
runtime.DefaultScheme.AddKnownTypeWithName("", "A", &ExtensionA{})
runtime.DefaultScheme.AddKnownTypeWithName("", "B", &ExtensionB{})
runtime.DefaultScheme.AddKnownTypeWithName("testExternal", "ExtensionType", &ExternalExtensionType{})
runtime.DefaultScheme.AddKnownTypeWithName("testExternal", "A", &ExtensionA{})
runtime.DefaultScheme.AddKnownTypeWithName("testExternal", "B", &ExtensionB{})
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "ExtensionType", &InternalExtensionType{})
scheme.AddKnownTypeWithName("", "A", &ExtensionA{})
scheme.AddKnownTypeWithName("", "B", &ExtensionB{})
scheme.AddKnownTypeWithName("testExternal", "ExtensionType", &ExternalExtensionType{})
scheme.AddKnownTypeWithName("testExternal", "A", &ExtensionA{})
scheme.AddKnownTypeWithName("testExternal", "B", &ExtensionB{})
table := []struct {
obj runtime.Object
@ -187,14 +190,14 @@ func TestExtensionMapping(t *testing.T) {
}
for _, item := range table {
gotEncoded, err := runtime.DefaultScheme.EncodeToVersion(item.obj, "testExternal")
gotEncoded, err := scheme.EncodeToVersion(item.obj, "testExternal")
if err != nil {
t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
} else if e, a := item.encoded, string(gotEncoded); 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 {
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
} 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"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

View File

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

View File

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

View File

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

View File

@ -22,6 +22,8 @@ import (
"testing"
"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/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@ -90,9 +92,9 @@ func TestExtractObj(t *testing.T) {
func TestWatch(t *testing.T) {
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) {
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 {
t.Fatalf("unexpected error: %v", err)
}