Merge pull request #1275 from smarterclayton/v1beta2

Create v1beta2 and allow multiple Codec encodings to exist
This commit is contained in:
Daniel Smith
2014-09-16 14:52:04 -07:00
61 changed files with 1754 additions and 425 deletions

View File

@@ -14,7 +14,7 @@ install:
- ./hack/build-go.sh
script:
- ./hack/test-go.sh
- KUBE_TIMEOUT='-timeout 60s' ./hack/test-go.sh
- PATH=$HOME/gopath/bin:./third_party/etcd/bin:$PATH ./hack/test-cmd.sh
- PATH=$HOME/gopath/bin:./third_party/etcd/bin:$PATH ./hack/test-integration.sh

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"
@@ -129,7 +130,7 @@ func readConfig(storage string) []byte {
glog.Fatal("Need config file (-c)")
}
data, err := parser.ToWireFormat(readConfigData(), storage, runtime.DefaultCodec)
data, err := parser.ToWireFormat(readConfigData(), storage, latest.Codec)
if err != nil {
glog.Fatalf("Error parsing %v as an object for %v: %v\n", *config, storage, err)
@@ -296,7 +297,7 @@ func executeAPIRequest(method string, c *client.Client) bool {
if setBody {
if version != 0 {
data := readConfig(storage)
obj, err := runtime.DefaultCodec.Decode(data)
obj, err := latest.Codec.Decode(data)
if err != nil {
glog.Fatalf("error setting resource version: %v", err)
}
@@ -305,7 +306,7 @@ func executeAPIRequest(method string, c *client.Client) bool {
glog.Fatalf("error setting resource version: %v", err)
}
jsonBase.SetResourceVersion(version)
data, err = runtime.DefaultCodec.Encode(obj)
data, err = latest.Codec.Encode(obj)
if err != nil {
glog.Fatalf("error setting resource version: %v", err)
}

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"
@@ -103,7 +103,7 @@ func TestApiExamples(t *testing.T) {
return
}
tested += 1
if err := runtime.DefaultCodec.DecodeInto(data, expectedType); err != nil {
if err := latest.Codec.DecodeInto(data, expectedType); err != nil {
t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data))
return
}
@@ -137,7 +137,7 @@ func TestExamples(t *testing.T) {
return
}
tested += 1
if err := runtime.DefaultCodec.DecodeInto(data, expectedType); err != nil {
if err := latest.Codec.DecodeInto(data, expectedType); err != nil {
t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data))
return
}
@@ -168,14 +168,14 @@ func TestReadme(t *testing.T) {
}
for _, json := range match[1:] {
expectedType := &api.Pod{}
if err := runtime.DefaultCodec.DecodeInto([]byte(json), expectedType); err != nil {
if err := latest.Codec.DecodeInto([]byte(json), expectedType); err != nil {
t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data))
return
}
if errors := validateObject(expectedType); len(errors) > 0 {
t.Errorf("%s did not validate correctly: %v", path, errors)
}
encoded, err := runtime.DefaultCodec.Encode(expectedType)
encoded, err := latest.Codec.Encode(expectedType)
if err != nil {
t.Errorf("Could not encode object: %v", err)
continue

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,21 +17,21 @@ 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"
"github.com/google/gofuzz"
)
var fuzzIters = flag.Int("fuzz_iters", 50, "How many fuzzing iterations to do.")
var fuzzIters = flag.Int("fuzz_iters", 40, "How many fuzzing iterations to do.")
// apiObjectFuzzer can randomly populate api objects.
var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
@@ -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 := runtime.DefaultCodec.Encode(source)
data, err := codec.Encode(source)
if err != nil {
t.Errorf("%v: %v (%#v)", name, err, source)
return
}
obj2, err := runtime.DefaultCodec.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 = runtime.DefaultCodec.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,9 @@ 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)
runTest(t, api.Codec, item)
}
}
}
@@ -194,8 +176,8 @@ func TestEncode_Ptr(t *testing.T) {
Labels: map[string]string{"name": "foo"},
}
obj := runtime.Object(pod)
data, err := runtime.DefaultCodec.Encode(obj)
obj2, err2 := runtime.DefaultCodec.Decode(data)
data, err := latest.Codec.Encode(obj)
obj2, err2 := latest.Codec.Decode(data)
if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v'", err, err2)
}
@@ -209,11 +191,11 @@ func TestEncode_Ptr(t *testing.T) {
func TestBadJSONRejection(t *testing.T) {
badJSONMissingKind := []byte(`{ }`)
if _, err := runtime.DefaultCodec.Decode(badJSONMissingKind); err == nil {
if _, err := latest.Codec.Decode(badJSONMissingKind); err == nil {
t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
}
badJSONUnknownType := []byte(`{"kind": "bar"}`)
if _, err1 := runtime.DefaultCodec.Decode(badJSONUnknownType); err1 == nil {
if _, err1 := latest.Codec.Decode(badJSONUnknownType); err1 == nil {
t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
}
/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)

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

@@ -0,0 +1,111 @@
/*
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 v1beta2
import (
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
func init() {
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
// 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

@@ -0,0 +1,19 @@
/*
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 v1beta2_test
import ()

18
pkg/api/v1beta2/doc.go Normal file
View File

@@ -0,0 +1,18 @@
/*
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 v1beta2 is the v1beta2 version of the API.
package v1beta2

View File

@@ -0,0 +1,45 @@
/*
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 v1beta2
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
// Codec encodes internal objects to the v1beta2 scheme
var Codec = runtime.CodecFor(api.Scheme, "v1beta2")
func init() {
api.Scheme.AddKnownTypes("v1beta2",
&PodList{},
&Pod{},
&ReplicationControllerList{},
&ReplicationController{},
&ServiceList{},
&Service{},
&MinionList{},
&Minion{},
&Status{},
&ServerOpList{},
&ServerOp{},
&ContainerManifestList{},
&Endpoints{},
&EndpointsList{},
&Binding{},
)
}

635
pkg/api/v1beta2/types.go Normal file
View File

@@ -0,0 +1,635 @@
/*
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 v1beta2
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/fsouza/go-dockerclient"
)
// Common string formats
// ---------------------
// Many fields in this API have formatting requirements. The commonly used
// formats are defined here.
//
// C_IDENTIFIER: This is a string that conforms the definition of an "identifier"
// in the C language. This is captured by the following regex:
// [A-Za-z_][A-Za-z0-9_]*
// This defines the format, but not the length restriction, which should be
// specified at the definition of any field of this type.
//
// DNS_LABEL: This is a string, no more than 63 characters long, that conforms
// to the definition of a "label" in RFCs 1035 and 1123. This is captured
// by the following regex:
// [a-z0-9]([-a-z0-9]*[a-z0-9])?
//
// DNS_SUBDOMAIN: This is a string, no more than 253 characters long, that conforms
// to the definition of a "subdomain" in RFCs 1035 and 1123. This is captured
// by the following regex:
// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
// or more simply:
// DNS_LABEL(\.DNS_LABEL)*
// ContainerManifest corresponds to the Container Manifest format, documented at:
// https://developers.google.com/compute/docs/containers/container_vms#container_manifest
// This is used as the representation of Kubernetes workloads.
type ContainerManifest struct {
// Required: This must be a supported version string, such as "v1beta1".
Version string `yaml:"version" json:"version"`
// 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 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"`
Containers []Container `yaml:"containers" json:"containers"`
RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"`
}
// ContainerManifestList is used to communicate container manifests to kubelet.
type ContainerManifestList struct {
JSONBase `json:",inline" yaml:",inline"`
Items []ContainerManifest `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*ContainerManifestList) IsAnAPIObject() {}
// Volume represents a named volume in a pod that may be accessed by any containers in the pod.
type Volume struct {
// Required: This must be a DNS_LABEL. Each volume in a pod must have
// a unique name.
Name string `yaml:"name" json:"name"`
// Source represents the location and type of a volume to mount.
// This is optional for now. If not specified, the Volume is implied to be an EmptyDir.
// This implied behavior is deprecated and will be removed in a future version.
Source *VolumeSource `yaml:"source" json:"source"`
}
type VolumeSource struct {
// Only one of the following sources may be specified
// HostDirectory represents a pre-existing directory on the host machine that is directly
// exposed to the container. This is generally used for system agents or other privileged
// things that are allowed to see the host machine. Most containers will NOT need this.
// TODO(jonesdl) We need to restrict who can use host directory mounts and
// who can/can not mount host directories as read/write.
HostDirectory *HostDirectory `yaml:"hostDir" json:"hostDir"`
// EmptyDirectory represents a temporary directory that shares a pod's lifetime.
EmptyDirectory *EmptyDirectory `yaml:"emptyDir" json:"emptyDir"`
}
// HostDirectory represents bare host directory volume.
type HostDirectory struct {
Path string `yaml:"path" json:"path"`
}
type EmptyDirectory struct{}
// Port represents a network port in a single container.
type Port struct {
// Optional: If specified, this must be a DNS_LABEL. Each named port
// in a pod must have a unique name.
Name string `yaml:"name,omitempty" json:"name,omitempty"`
// Optional: If specified, this must be a valid port number, 0 < x < 65536.
HostPort int `yaml:"hostPort,omitempty" json:"hostPort,omitempty"`
// Required: This must be a valid port number, 0 < x < 65536.
ContainerPort int `yaml:"containerPort" json:"containerPort"`
// Optional: Supports "TCP" and "UDP". Defaults to "TCP".
Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"`
// Optional: What host IP to bind the external port to.
HostIP string `yaml:"hostIP,omitempty" json:"hostIP,omitempty"`
}
// VolumeMount describes a mounting of a Volume within a container.
type VolumeMount struct {
// Required: This must match the Name of a Volume [above].
Name string `yaml:"name" json:"name"`
// 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"`
}
// HTTPGetAction describes an action based on HTTP Get requests.
type HTTPGetAction struct {
// Optional: Path to access on the HTTP server.
Path string `yaml:"path,omitempty" json:"path,omitempty"`
// Required: Name or number of the port to access on the container.
Port util.IntOrString `yaml:"port,omitempty" json:"port,omitempty"`
// Optional: Host name to connect to, defaults to the pod IP.
Host string `yaml:"host,omitempty" json:"host,omitempty"`
}
// TCPSocketAction describes an action based on opening a socket
type TCPSocketAction struct {
// Required: Port to connect to.
Port util.IntOrString `yaml:"port,omitempty" json:"port,omitempty"`
}
// ExecAction describes a "run in container" action.
type ExecAction struct {
// Command is the command line to execute inside the container, the working directory for the
// 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.
Command []string `yaml:"command,omitempty" json:"command,omitempty"`
}
// LivenessProbe describes a liveness probe to be examined to the container.
// TODO: pass structured data to the actions, and document that data here.
type LivenessProbe struct {
// Type of liveness probe. Current legal values "http", "tcp"
Type string `yaml:"type,omitempty" json:"type,omitempty"`
// HTTPGetProbe parameters, required if Type == 'http'
HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
// TCPSocketProbe parameter, required if Type == 'tcp'
TCPSocket *TCPSocketAction `yaml:"tcpSocket,omitempty" json:"tcpSocket,omitempty"`
// ExecProbe parameter, required if Type == 'exec'
Exec *ExecAction `yaml:"exec,omitempty" json:"exec,omitempty"`
// Length of time before health checking is activated. In seconds.
InitialDelaySeconds int64 `yaml:"initialDelaySeconds,omitempty" json:"initialDelaySeconds,omitempty"`
}
// Container represents a single container that is expected to be run on the host.
type Container struct {
// Required: This must be a DNS_LABEL. Each container in a pod must
// have a unique name.
Name string `yaml:"name" json:"name"`
// Required.
Image string `yaml:"image" json:"image"`
// Optional: Defaults to whatever is defined in the image.
Command []string `yaml:"command,omitempty" json:"command,omitempty"`
// Optional: Defaults to Docker's default.
WorkingDir string `yaml:"workingDir,omitempty" json:"workingDir,omitempty"`
Ports []Port `yaml:"ports,omitempty" json:"ports,omitempty"`
Env []EnvVar `yaml:"env,omitempty" json:"env,omitempty"`
// Optional: Defaults to unlimited.
Memory int `yaml:"memory,omitempty" json:"memory,omitempty"`
// Optional: Defaults to unlimited.
CPU int `yaml:"cpu,omitempty" json:"cpu,omitempty"`
VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty" json:"volumeMounts,omitempty"`
LivenessProbe *LivenessProbe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"`
Lifecycle *Lifecycle `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
// Optional: Default to false.
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"`
}
// Handler defines a specific action that should be taken
// 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.
// Exec specifies the action to take.
Exec *ExecAction `yaml:"exec,omitempty" json:"exec,omitempty"`
// HTTPGet specifies the http request to perform.
HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
}
// Lifecycle describes actions that the management system should take in response to container lifecycle
// events. For the PostStart and PreStop lifecycle handlers, management of the container blocks
// until the action is complete, unless the container process fails, in which case the handler is aborted.
type Lifecycle struct {
// PostStart is called immediately after a container is created. If the handler fails, the container
// is terminated and restarted.
PostStart *Handler `yaml:"postStart,omitempty" json:"postStart,omitempty"`
// PreStop is called immediately before a container is terminated. The reason for termination is
// passed to the handler. Regardless of the outcome of the handler, the container is eventually terminated.
PreStop *Handler `yaml:"preStop,omitempty" json:"preStop,omitempty"`
}
// Event is the representation of an event logged to etcd backends.
type Event struct {
Event string `json:"event,omitempty"`
Manifest *ContainerManifest `json:"manifest,omitempty"`
Container *Container `json:"container,omitempty"`
Timestamp int64 `json:"timestamp"`
}
// The below types are used by kube_client and api_server.
// JSONBase is shared by all objects sent to, or returned from the client.
type JSONBase struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
CreationTimestamp util.Time `json:"creationTimestamp,omitempty" yaml:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"`
ResourceVersion uint64 `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
}
// PodStatus represents a status of a pod.
type PodStatus string
// These are the valid statuses of pods.
const (
// PodWaiting means that we're waiting for the pod to begin running.
PodWaiting PodStatus = "Waiting"
// PodRunning means that the pod is up and running.
PodRunning PodStatus = "Running"
// PodTerminated means that the pod has stopped.
PodTerminated PodStatus = "Terminated"
)
type ContainerStateWaiting struct {
// Reason could be pulling image,
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`
}
type ContainerStateRunning struct {
}
type ContainerStateTerminated struct {
ExitCode int `json:"exitCode,omitempty" yaml:"exitCode,omitempty"`
Signal int `json:"signal,omitempty" yaml:"signal,omitempty"`
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`
}
type ContainerState struct {
// Only one of the following ContainerState may be specified.
// If none of them is specified, the default one is ContainerStateWaiting.
Waiting *ContainerStateWaiting `json:"waiting,omitempty" yaml:"waiting,omitempty"`
Running *ContainerStateRunning `json:"running,omitempty" yaml:"running,omitempty"`
Termination *ContainerStateTerminated `json:"termination,omitempty" yaml:"termination,omitempty"`
}
type ContainerStatus struct {
// TODO(dchen1107): Should we rename PodStatus to a more generic name or have a separate states
// defined for container?
State ContainerState `json:"state,omitempty" yaml:"state,omitempty"`
RestartCount int `json:"restartCount" yaml:"restartCount"`
// TODO(dchen1107): Introduce our own NetworkSettings struct here?
// TODO(dchen1107): Once we have done with integration with cadvisor, resource
// usage should be included.
// TODO(dchen1107): In long run, I think we should replace this with our own struct to remove
// the dependency on docker.
DetailInfo docker.Container `json:"detailInfo,omitempty" yaml:"detailInfo,omitempty"`
}
// 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): 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 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"`
OnFailure *RestartPolicyOnFailure `json:"onFailure,omitempty" yaml:"onFailure,omitempty"`
Never *RestartPolicyNever `json:"never,omitempty" yaml:"never,omitempty"`
}
// PodState is the state of a pod, used as either input (desired state) or output (current state).
type PodState struct {
Manifest ContainerManifest `json:"manifest,omitempty" yaml:"manifest,omitempty"`
Status PodStatus `json:"status,omitempty" yaml:"status,omitempty"`
Host string `json:"host,omitempty" yaml:"host,omitempty"`
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
PodIP string `json:"podIP,omitempty" yaml:"podIP,omitempty"`
// 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.
// 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"`
}
// PodList is a list of Pods.
type PodList struct {
JSONBase `json:",inline" yaml:",inline"`
Items []Pod `json:"items" yaml:"items,omitempty"`
}
func (*PodList) IsAnAPIObject() {}
// Pod is a collection of containers, used as either input (create, update) or as output (list, get).
type Pod struct {
JSONBase `json:",inline" yaml:",inline"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"`
CurrentState PodState `json:"currentState,omitempty" yaml:"currentState,omitempty"`
}
func (*Pod) IsAnAPIObject() {}
// ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get).
type ReplicationControllerState struct {
Replicas int `json:"replicas" yaml:"replicas"`
ReplicaSelector map[string]string `json:"replicaSelector,omitempty" yaml:"replicaSelector,omitempty"`
PodTemplate PodTemplate `json:"podTemplate,omitempty" yaml:"podTemplate,omitempty"`
}
// ReplicationControllerList is a collection of replication controllers.
type ReplicationControllerList struct {
JSONBase `json:",inline" yaml:",inline"`
Items []ReplicationController `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*ReplicationControllerList) IsAnAPIObject() {}
// ReplicationController represents the configuration of a replication controller.
type ReplicationController struct {
JSONBase `json:",inline" yaml:",inline"`
DesiredState ReplicationControllerState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"`
CurrentState ReplicationControllerState `json:"currentState,omitempty" yaml:"currentState,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
func (*ReplicationController) IsAnAPIObject() {}
// PodTemplate holds the information used for creating pods.
type PodTemplate struct {
DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
// ServiceList holds a list of services.
type ServiceList struct {
JSONBase `json:",inline" yaml:",inline"`
Items []Service `json:"items" yaml:"items"`
}
func (*ServiceList) IsAnAPIObject() {}
// Service is a named abstraction of software service (for example, mysql) consisting of local port
// (for example 3306) that the proxy listens on, and the selector that determines which pods
// will answer requests sent through the proxy.
type Service struct {
JSONBase `json:",inline" yaml:",inline"`
// Required.
Port int `json:"port" yaml:"port"`
// Optional: Supports "TCP" and "UDP". Defaults to "TCP".
Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"`
// This service's labels.
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// This service will route traffic to pods having labels matching this selector.
Selector map[string]string `json:"selector,omitempty" yaml:"selector,omitempty"`
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" yaml:"createExternalLoadBalancer,omitempty"`
// ContainerPort is the name of the port on the container to direct traffic to.
// Optional, if unspecified use the first port on the container.
ContainerPort util.IntOrString `json:"containerPort,omitempty" yaml:"containerPort,omitempty"`
}
func (*Service) IsAnAPIObject() {}
// Endpoints is a collection of endpoints that implement the actual service, for example:
// Name: "mysql", Endpoints: ["10.10.1.1:1909", "10.10.2.2:8834"]
type Endpoints struct {
JSONBase `json:",inline" yaml:",inline"`
Endpoints []string `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
}
func (*Endpoints) IsAnAPIObject() {}
// EndpointsList is a list of endpoints.
type EndpointsList struct {
JSONBase `json:",inline" yaml:",inline"`
Items []Endpoints `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*EndpointsList) IsAnAPIObject() {}
// Minion is a worker node in Kubernetenes.
// The name of the minion according to etcd is in JSONBase.ID.
type Minion struct {
JSONBase `json:",inline" yaml:",inline"`
// Queried from cloud provider, if available.
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
}
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"`
}
func (*MinionList) IsAnAPIObject() {}
// Binding is written by a scheduler to cause a pod to be bound to a host.
type Binding struct {
JSONBase `json:",inline" yaml:",inline"`
PodID string `json:"podID" yaml:"podID"`
Host string `json:"host" yaml:"host"`
}
func (*Binding) IsAnAPIObject() {}
// Status is a return value for calls that don't return other objects.
// TODO: this could go in apiserver, but I'm including it here so clients needn't
// import both.
type Status struct {
JSONBase `json:",inline" yaml:",inline"`
// One of: "success", "failure", "working" (for operations not yet completed)
Status string `json:"status,omitempty" yaml:"status,omitempty"`
// A human-readable description of the status of this operation.
Message string `json:"message,omitempty" yaml:"message,omitempty"`
// A machine-readable description of why this operation is in the
// "failure" or "working" status. If this value is empty there
// is no information available. A Reason clarifies an HTTP status
// code but does not override it.
Reason StatusReason `json:"reason,omitempty" yaml:"reason,omitempty"`
// Extended data associated with the reason. Each reason may define its
// own extended details. This field is optional and the data returned
// is not guaranteed to conform to any schema except that defined by
// the reason type.
Details *StatusDetails `json:"details,omitempty" yaml:"details,omitempty"`
// Suggested HTTP return code for this status, 0 if not set.
Code int `json:"code,omitempty" yaml:"code,omitempty"`
}
func (*Status) IsAnAPIObject() {}
// StatusDetails is a set of additional properties that MAY be set by the
// server to provide additional information about a response. The Reason
// field of a Status object defines what attributes will be set. Clients
// must ignore fields that do not match the defined type of each attribute,
// and should assume that any attribute may be empty, invalid, or under
// defined.
type StatusDetails struct {
// The ID attribute of the resource associated with the status StatusReason
// (when there is a single ID which can be described).
ID string `json:"id,omitempty" yaml:"id,omitempty"`
// The kind attribute of the resource associated with the status StatusReason.
// On some operations may differ from the requested resource Kind.
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
// The Causes array includes more details associated with the StatusReason
// failure. Not all StatusReasons may provide detailed causes.
Causes []StatusCause `json:"causes,omitempty" yaml:"causes,omitempty"`
}
// Values of Status.Status
const (
StatusSuccess = "success"
StatusFailure = "failure"
StatusWorking = "working"
)
// StatusReason is an enumeration of possible failure causes. Each StatusReason
// must map to a single HTTP status code, but multiple reasons may map
// to the same HTTP status code.
// TODO: move to apiserver
type StatusReason string
const (
// StatusReasonUnknown means the server has declined to indicate a specific reason.
// The details field may contain other information about this error.
// Status code 500.
StatusReasonUnknown StatusReason = ""
// StatusReasonWorking means the server is processing this request and will complete
// at a future time.
// Details (optional):
// "kind" string - the name of the resource being referenced ("operation" today)
// "id" string - the identifier of the Operation resource where updates
// will be returned
// Headers (optional):
// "Location" - HTTP header populated with a URL that can retrieved the final
// status of this operation.
// Status code 202
StatusReasonWorking StatusReason = "working"
// StatusReasonNotFound means one or more resources required for this operation
// could not be found.
// Details (optional):
// "kind" string - the kind attribute of the missing resource
// on some operations may differ from the requested
// resource.
// "id" string - the identifier of the missing resource
// Status code 404
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 = "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.
// Each resource may define custom details that indicate the nature of the
// 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
// cases when multiple errors are encountered.
type StatusCause struct {
// A machine-readable description of the cause of the error. If this value is
// empty there is no information available.
Type CauseType `json:"reason,omitempty" yaml:"reason,omitempty"`
// A human-readable description of the cause of the error. This field may be
// presented as-is to a reader.
Message string `json:"message,omitempty" yaml:"message,omitempty"`
// The field of the resource that has caused this error, as named by its JSON
// serialization. May include dot and postfix notation for nested attributes.
// Arrays are zero-indexed. Fields may appear more than once in an array of
// causes due to fields having multiple errors.
// Optional.
//
// Examples:
// "name" - the field "name" on the current resource
// "items[0].name" - the field "name" on the first array entry in "items"
Field string `json:"field,omitempty" yaml:"field,omitempty"`
}
// CauseType is a machine readable value providing more detail about what
// occured in a status response. An operation may have multiple causes for a
// status (whether failure, success, or working).
type CauseType string
const (
// CauseTypeFieldValueNotFound is used to report failure to find a requested value
// (e.g. looking up an ID).
CauseTypeFieldValueNotFound CauseType = "fieldValueNotFound"
// CauseTypeFieldValueInvalid is used to report required values that are not
// provided (e.g. empty strings, null values, or empty arrays).
CauseTypeFieldValueRequired CauseType = "fieldValueRequired"
// CauseTypeFieldValueDuplicate is used to report collisions of values that must be
// unique (e.g. unique IDs).
CauseTypeFieldValueDuplicate CauseType = "fieldValueDuplicate"
// CauseTypeFieldValueInvalid is used to report malformed values (e.g. failed regex
// match).
CauseTypeFieldValueInvalid CauseType = "fieldValueInvalid"
// CauseTypeFieldValueNotSupported is used to report valid (as per formatting rules)
// values that can not be handled (e.g. an enumerated string).
CauseTypeFieldValueNotSupported CauseType = "fieldValueNotSupported"
)
// ServerOp is an operation delivered to API clients.
type ServerOp struct {
JSONBase `yaml:",inline" json:",inline"`
}
func (*ServerOp) IsAnAPIObject() {}
// ServerOpList is a list of operations, as delivered to API clients.
type ServerOpList struct {
JSONBase `yaml:",inline" json:",inline"`
Items []ServerOp `yaml:"items,omitempty" json:"items,omitempty"`
}
func (*ServerOpList) IsAnAPIObject() {}

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,10 +437,7 @@ 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"`
Items []Minion `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*MinionList) IsAnAPIObject() {}
@@ -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"
@@ -43,11 +44,11 @@ func convert(obj runtime.Object) (runtime.Object, error) {
return obj, nil
}
var codec = runtime.DefaultCodec
var codec = latest.Codec
func init() {
runtime.DefaultScheme.AddKnownTypes("", &Simple{}, &SimpleList{})
runtime.DefaultScheme.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 runtime.DefaultScheme.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) {
@@ -676,7 +677,7 @@ func (*UnregisteredAPIObject) IsAnAPIObject() {}
func TestWriteJSONDecodeError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
writeJSON(http.StatusOK, runtime.DefaultCodec, &UnregisteredAPIObject{"Undecodable"}, w)
writeJSON(http.StatusOK, latest.Codec, &UnregisteredAPIObject{"Undecodable"}, w)
}))
status := expectApiStatus(t, "GET", server.URL, nil, http.StatusInternalServerError)
if status.Reason != api.StatusReasonUnknown {

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"
@@ -99,7 +99,7 @@ type Client struct {
// to a URL will prepend the server path. Returns an error if host cannot be converted to a
// valid URL.
func New(host string, auth *AuthInfo) (*Client, error) {
restClient, err := NewRESTClient(host, auth, "/api/v1beta1/", runtime.DefaultCodec)
restClient, err := NewRESTClient(host, auth, "/api/v1beta1/", latest.Codec)
if err != nil {
return nil, err
}
@@ -224,7 +224,7 @@ func (c *RESTClient) doRequest(request *http.Request) ([]byte, error) {
// Did the server give us a status response?
isStatusResponse := false
var status api.Status
if err := runtime.DefaultCodec.DecodeInto(body, &status); err == nil && status.Status != "" {
if err := latest.Codec.DecodeInto(body, &status); err == nil && status.Status != "" {
isStatusResponse = true
}

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"
@@ -48,7 +49,7 @@ func TestValidatesHostParameter(t *testing.T) {
"host/server": {"", "", true},
}
for k, expected := range testCases {
c, err := NewRESTClient(k, nil, "/api/v1beta1/", runtime.DefaultCodec)
c, err := NewRESTClient(k, nil, "/api/v1beta1/", latest.Codec)
switch {
case err == nil && expected.Err:
t.Errorf("expected error but was nil")
@@ -309,7 +310,7 @@ func TestCreateController(t *testing.T) {
func body(obj runtime.Object, raw *string) *string {
if obj != nil {
bs, _ := runtime.DefaultCodec.Encode(obj)
bs, _ := latest.Codec.Encode(obj)
body := string(bs)
return &body
}
@@ -533,7 +534,7 @@ func TestDoRequest(t *testing.T) {
func TestDoRequestAccepted(t *testing.T) {
status := &api.Status{Status: api.StatusWorking}
expectedBody, _ := runtime.DefaultCodec.Encode(status)
expectedBody, _ := latest.Codec.Encode(status)
fakeHandler := util.FakeHandler{
StatusCode: 202,
ResponseBody: string(expectedBody),
@@ -570,7 +571,7 @@ func TestDoRequestAccepted(t *testing.T) {
func TestDoRequestAcceptedSuccess(t *testing.T) {
status := &api.Status{Status: api.StatusSuccess}
expectedBody, _ := runtime.DefaultCodec.Encode(status)
expectedBody, _ := latest.Codec.Encode(status)
fakeHandler := util.FakeHandler{
StatusCode: 202,
ResponseBody: string(expectedBody),
@@ -590,7 +591,7 @@ func TestDoRequestAcceptedSuccess(t *testing.T) {
if err != nil {
t.Errorf("Unexpected error %#v", err)
}
statusOut, err := runtime.DefaultCodec.Decode(body)
statusOut, err := latest.Codec.Decode(body)
if err != nil {
t.Errorf("Unexpected error %#v", err)
}

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"
@@ -38,7 +39,7 @@ import (
func TestDoRequestNewWay(t *testing.T) {
reqBody := "request body"
expectedObj := &api.Service{Port: 12345}
expectedBody, _ := runtime.DefaultCodec.Encode(expectedObj)
expectedBody, _ := latest.Codec.Encode(expectedObj)
fakeHandler := util.FakeHandler{
StatusCode: 200,
ResponseBody: string(expectedBody),
@@ -71,9 +72,9 @@ func TestDoRequestNewWay(t *testing.T) {
func TestDoRequestNewWayReader(t *testing.T) {
reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
reqBodyExpected, _ := runtime.DefaultCodec.Encode(reqObj)
reqBodyExpected, _ := latest.Codec.Encode(reqObj)
expectedObj := &api.Service{Port: 12345}
expectedBody, _ := runtime.DefaultCodec.Encode(expectedObj)
expectedBody, _ := latest.Codec.Encode(expectedObj)
fakeHandler := util.FakeHandler{
StatusCode: 200,
ResponseBody: string(expectedBody),
@@ -108,9 +109,9 @@ func TestDoRequestNewWayReader(t *testing.T) {
func TestDoRequestNewWayObj(t *testing.T) {
reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
reqBodyExpected, _ := runtime.DefaultCodec.Encode(reqObj)
reqBodyExpected, _ := latest.Codec.Encode(reqObj)
expectedObj := &api.Service{Port: 12345}
expectedBody, _ := runtime.DefaultCodec.Encode(expectedObj)
expectedBody, _ := latest.Codec.Encode(expectedObj)
fakeHandler := util.FakeHandler{
StatusCode: 200,
ResponseBody: string(expectedBody),
@@ -144,7 +145,7 @@ func TestDoRequestNewWayObj(t *testing.T) {
func TestDoRequestNewWayFile(t *testing.T) {
reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
reqBodyExpected, err := runtime.DefaultCodec.Encode(reqObj)
reqBodyExpected, err := latest.Codec.Encode(reqObj)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -160,7 +161,7 @@ func TestDoRequestNewWayFile(t *testing.T) {
}
expectedObj := &api.Service{Port: 12345}
expectedBody, _ := runtime.DefaultCodec.Encode(expectedObj)
expectedBody, _ := latest.Codec.Encode(expectedObj)
fakeHandler := util.FakeHandler{
StatusCode: 200,
ResponseBody: string(expectedBody),
@@ -295,7 +296,7 @@ func TestPolling(t *testing.T) {
callNumber := 0
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
data, err := runtime.DefaultCodec.Encode(objects[callNumber])
data, err := latest.Codec.Encode(objects[callNumber])
if err != nil {
t.Errorf("Unexpected encode error")
}
@@ -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"
@@ -109,7 +111,7 @@ func validateSyncReplication(t *testing.T, fakePodControl *FakePodControl, expec
}
func TestSyncReplicationControllerDoesNothing(t *testing.T) {
body, _ := runtime.DefaultCodec.Encode(newPodList(2))
body, _ := latest.Codec.Encode(newPodList(2))
fakeHandler := util.FakeHandler{
StatusCode: 200,
ResponseBody: string(body),
@@ -129,7 +131,7 @@ func TestSyncReplicationControllerDoesNothing(t *testing.T) {
}
func TestSyncReplicationControllerDeletes(t *testing.T) {
body, _ := runtime.DefaultCodec.Encode(newPodList(2))
body, _ := latest.Codec.Encode(newPodList(2))
fakeHandler := util.FakeHandler{
StatusCode: 200,
ResponseBody: string(body),
@@ -149,7 +151,7 @@ func TestSyncReplicationControllerDeletes(t *testing.T) {
}
func TestSyncReplicationControllerCreates(t *testing.T) {
body, _ := runtime.DefaultCodec.Encode(newPodList(0))
body, _ := latest.Codec.Encode(newPodList(0))
fakeHandler := util.FakeHandler{
StatusCode: 200,
ResponseBody: string(body),
@@ -169,7 +171,7 @@ func TestSyncReplicationControllerCreates(t *testing.T) {
}
func TestCreateReplica(t *testing.T) {
body, _ := runtime.DefaultCodec.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: runtime.DefaultScheme.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,27 +21,29 @@ 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"
)
func TestParseBadStorage(t *testing.T) {
p := NewParser(map[string]runtime.Object{})
_, err := p.ToWireFormat([]byte("{}"), "badstorage", runtime.DefaultCodec)
_, err := p.ToWireFormat([]byte("{}"), "badstorage", latest.Codec)
if err == nil {
t.Errorf("Expected error, received none")
}
}
func DoParseTest(t *testing.T, storage string, obj runtime.Object, p *Parser) {
jsonData, _ := runtime.DefaultCodec.Encode(obj)
jsonData, _ := latest.Codec.Encode(obj)
var tmp map[string]interface{}
json.Unmarshal(jsonData, &tmp)
yamlData, _ := yaml.Marshal(tmp)
t.Logf("Intermediate yaml:\n%v\n", string(yamlData))
t.Logf("Intermediate json:\n%v\n", string(jsonData))
jsonGot, jsonErr := p.ToWireFormat(jsonData, storage, runtime.DefaultCodec)
yamlGot, yamlErr := p.ToWireFormat(yamlData, storage, runtime.DefaultCodec)
jsonGot, jsonErr := p.ToWireFormat(jsonData, storage, latest.Codec)
yamlGot, yamlErr := p.ToWireFormat(yamlData, storage, latest.Codec)
if jsonErr != nil {
t.Errorf("json err: %#v", jsonErr)
@@ -125,8 +127,9 @@ type TestParseType struct {
func (*TestParseType) IsAnAPIObject() {}
func TestParseCustomType(t *testing.T) {
runtime.DefaultScheme.AddKnownTypes("", &TestParseType{})
runtime.DefaultScheme.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.
@@ -53,7 +53,7 @@ func (s *ProxyServer) Serve() error {
func (s *ProxyServer) doError(w http.ResponseWriter, err error) {
w.WriteHeader(http.StatusInternalServerError)
w.Header().Add("Content-type", "application/json")
data, _ := runtime.DefaultCodec.Encode(&api.Status{
data, _ := latest.Codec.Encode(&api.Status{
Status: api.StatusFailure,
Message: fmt.Sprintf("internal error: %#v", err),
})

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"
@@ -50,7 +51,7 @@ func (i *IdentityPrinter) Print(data []byte, w io.Writer) error {
// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer.
func (i *IdentityPrinter) PrintObj(obj runtime.Object, output io.Writer) error {
data, err := runtime.DefaultCodec.Encode(obj)
data, err := latest.Codec.Encode(obj)
if err != nil {
return err
}
@@ -260,7 +261,7 @@ func (h *HumanReadablePrinter) Print(data []byte, output io.Writer) error {
return fmt.Errorf("unexpected object with no 'kind' field: %s", data)
}
obj, err := runtime.DefaultCodec.Decode(data)
obj, err := latest.Codec.Decode(data)
if err != nil {
return err
}
@@ -292,7 +293,7 @@ type TemplatePrinter struct {
// Print parses the data as JSON, and re-formats it with the Go Template.
func (t *TemplatePrinter) Print(data []byte, w io.Writer) error {
obj, err := runtime.DefaultCodec.Decode(data)
obj, err := latest.Codec.Decode(data)
if err != nil {
return err
}

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"
)
@@ -96,7 +96,7 @@ func TestIdentityPrinter(t *testing.T) {
}
buff.Reset()
printer.PrintObj(obj, buff)
objOut, err := runtime.DefaultCodec.Decode([]byte(buff.String()))
objOut, err := latest.Codec.Decode([]byte(buff.String()))
if err != nil {
t.Errorf("Unexpeted error: %#v", err)
}

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"
@@ -47,8 +46,8 @@ type SourceEtcd struct {
func NewSourceEtcd(key string, client tools.EtcdClient, updates chan<- interface{}) *SourceEtcd {
helper := tools.EtcdHelper{
client,
runtime.DefaultCodec,
runtime.DefaultResourceVersioner,
latest.Codec,
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"
@@ -135,7 +135,7 @@ func (s ConfigSourceEtcd) GetServices() ([]api.Service, []api.Endpoints, error)
// and create a Service entry for it.
for i, node := range response.Node.Nodes {
var svc api.Service
err = runtime.DefaultCodec.DecodeInto([]byte(node.Value), &svc)
err = latest.Codec.DecodeInto([]byte(node.Value), &svc)
if err != nil {
glog.Errorf("Failed to load Service: %s (%#v)", node.Value, err)
continue
@@ -168,7 +168,7 @@ func (s ConfigSourceEtcd) GetEndpoints(service string) (api.Endpoints, error) {
}
// Parse all the endpoint specifications in this value.
var e api.Endpoints
err = runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &e)
err = latest.Codec.DecodeInto([]byte(response.Node.Value), &e)
return e, err
}
@@ -178,7 +178,7 @@ func etcdResponseToService(response *etcd.Response) (*api.Service, error) {
return nil, fmt.Errorf("invalid response from etcd: %#v", response)
}
var svc api.Service
err := runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &svc)
err := latest.Codec.DecodeInto([]byte(response.Node.Value), &svc)
if err != nil {
return nil, err
}
@@ -232,7 +232,7 @@ func (s ConfigSourceEtcd) ProcessChange(response *etcd.Response) {
func (s ConfigSourceEtcd) ProcessEndpointResponse(response *etcd.Response) {
glog.Infof("Processing a change in endpoint configuration... %s", *response)
var endpoints api.Endpoints
err := runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &endpoints)
err := latest.Codec.DecodeInto([]byte(response.Node.Value), &endpoints)
if err != nil {
glog.Errorf("Failed to parse service out of etcd key: %v : %+v", response.Node.Value, err)
return

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) {
@@ -38,12 +37,12 @@ func TestNewREST(t *testing.T) {
PodID: "foo",
Host: "bar",
}
body, err := runtime.DefaultCodec.Encode(binding)
body, err := latest.Codec.Encode(binding)
if err != nil {
t.Fatalf("Unexpected encode error %v", err)
}
obj := b.New()
err = runtime.DefaultCodec.DecodeInto(body, obj)
err = latest.Codec.DecodeInto(body, obj)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}

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) {
@@ -113,13 +113,13 @@ func TestControllerDecode(t *testing.T) {
ID: "foo",
},
}
body, err := runtime.DefaultCodec.Encode(controller)
body, err := latest.Codec.Encode(controller)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
controllerOut := storage.New()
if err := runtime.DefaultCodec.DecodeInto(body, controllerOut); err != nil {
if err := latest.Codec.DecodeInto(body, controllerOut); err != nil {
t.Errorf("unexpected error: %v", err)
}

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"
@@ -45,8 +46,8 @@ func NewRegistry(client tools.EtcdClient) *Registry {
registry := &Registry{
EtcdHelper: tools.EtcdHelper{
client,
runtime.DefaultCodec,
runtime.DefaultResourceVersioner,
latest.Codec,
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", runtime.DefaultScheme.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", runtime.DefaultScheme.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{
@@ -108,7 +108,7 @@ func TestEtcdCreatePod(t *testing.T) {
t.Fatalf("Unexpected error %v", err)
}
var pod api.Pod
err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &pod)
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &pod)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -122,7 +122,7 @@ func TestEtcdCreatePod(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &manifests)
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &manifests)
if len(manifests.Items) != 1 || manifests.Items[0].ID != "foo" {
t.Errorf("Unexpected manifest list: %#v", manifests)
}
@@ -133,7 +133,7 @@ func TestEtcdCreatePodAlreadyExisting(t *testing.T) {
fakeClient.Data["/registry/pods/foo"] = tools.EtcdResponseWithError{
R: &etcd.Response{
Node: &etcd.Node{
Value: runtime.DefaultScheme.EncodeOrDie(&api.Pod{JSONBase: api.JSONBase{ID: "foo"}}),
Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{JSONBase: api.JSONBase{ID: "foo"}}),
},
},
E: nil,
@@ -235,7 +235,7 @@ func TestEtcdCreatePodWithContainersNotFound(t *testing.T) {
t.Fatalf("Unexpected error %v", err)
}
var pod api.Pod
err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &pod)
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &pod)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -249,7 +249,7 @@ func TestEtcdCreatePodWithContainersNotFound(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &manifests)
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &manifests)
if len(manifests.Items) != 1 || manifests.Items[0].ID != "foo" {
t.Errorf("Unexpected manifest list: %#v", manifests)
}
@@ -264,7 +264,7 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) {
},
E: tools.EtcdErrorNotFound,
}
fakeClient.Set("/registry/hosts/machine/kubelet", runtime.DefaultScheme.EncodeOrDie(&api.ContainerManifestList{
fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(latest.Codec, &api.ContainerManifestList{
Items: []api.ContainerManifest{
{ID: "bar"},
},
@@ -300,7 +300,7 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) {
t.Fatalf("Unexpected error %v", err)
}
var pod api.Pod
err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &pod)
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &pod)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -314,7 +314,7 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &manifests)
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &manifests)
if len(manifests.Items) != 2 || manifests.Items[1].ID != "foo" {
t.Errorf("Unexpected manifest list: %#v", manifests)
}
@@ -325,11 +325,11 @@ func TestEtcdDeletePod(t *testing.T) {
fakeClient.TestIndex = true
key := "/registry/pods/foo"
fakeClient.Set(key, runtime.DefaultScheme.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", runtime.DefaultScheme.EncodeOrDie(&api.ContainerManifestList{
fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(latest.Codec, &api.ContainerManifestList{
Items: []api.ContainerManifest{
{ID: "foo"},
},
@@ -350,7 +350,7 @@ func TestEtcdDeletePod(t *testing.T) {
t.Fatalf("Unexpected error %v", err)
}
var manifests api.ContainerManifestList
runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &manifests)
latest.Codec.DecodeInto([]byte(response.Node.Value), &manifests)
if len(manifests.Items) != 0 {
t.Errorf("Unexpected container set: %s, expected empty", response.Node.Value)
}
@@ -361,11 +361,11 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) {
fakeClient.TestIndex = true
key := "/registry/pods/foo"
fakeClient.Set(key, runtime.DefaultScheme.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", runtime.DefaultScheme.EncodeOrDie(&api.ContainerManifestList{
fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(latest.Codec, &api.ContainerManifestList{
Items: []api.ContainerManifest{
{ID: "foo"},
{ID: "bar"},
@@ -388,7 +388,7 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) {
t.Fatalf("Unexpected error %v", err)
}
var manifests api.ContainerManifestList
runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &manifests)
latest.Codec.DecodeInto([]byte(response.Node.Value), &manifests)
if len(manifests.Items) != 1 {
t.Fatalf("Unexpected manifest set: %#v, expected empty", manifests)
}
@@ -445,13 +445,13 @@ func TestEtcdListPods(t *testing.T) {
Node: &etcd.Node{
Nodes: []*etcd.Node{
{
Value: runtime.DefaultScheme.EncodeOrDie(&api.Pod{
Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{
JSONBase: api.JSONBase{ID: "foo"},
DesiredState: api.PodState{Host: "machine"},
}),
},
{
Value: runtime.DefaultScheme.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: runtime.DefaultScheme.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}),
Value: runtime.EncodeOrDie(latest.Codec, &api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}),
},
{
Value: runtime.DefaultScheme.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", runtime.DefaultScheme.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 {
@@ -607,7 +607,7 @@ func TestEtcdCreateController(t *testing.T) {
t.Fatalf("Unexpected error %v", err)
}
var ctrl api.ReplicationController
err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &ctrl)
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &ctrl)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -619,7 +619,7 @@ func TestEtcdCreateController(t *testing.T) {
func TestEtcdCreateControllerAlreadyExisting(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Set("/registry/controllers/foo", runtime.DefaultScheme.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", runtime.DefaultScheme.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: runtime.DefaultScheme.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}),
Value: runtime.EncodeOrDie(latest.Codec, &api.Service{JSONBase: api.JSONBase{ID: "foo"}}),
},
{
Value: runtime.DefaultScheme.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "bar"}}),
Value: runtime.EncodeOrDie(latest.Codec, &api.Service{JSONBase: api.JSONBase{ID: "bar"}}),
},
},
},
@@ -699,7 +699,7 @@ func TestEtcdCreateService(t *testing.T) {
}
var service api.Service
err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &service)
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &service)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -711,7 +711,7 @@ func TestEtcdCreateService(t *testing.T) {
func TestEtcdCreateServiceAlreadyExisting(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Set("/registry/services/specs/foo", runtime.DefaultScheme.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", runtime.DefaultScheme.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", runtime.DefaultScheme.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: runtime.DefaultScheme.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: runtime.DefaultScheme.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", runtime.DefaultScheme.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", runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{}), 0)
fakeClient.Set("/registry/services/endpoints/foo", runtime.EncodeOrDie(latest.Codec, &api.Endpoints{}), 0)
err := registry.UpdateEndpoints(&endpoints)
if err != nil {
@@ -874,7 +874,7 @@ func TestEtcdUpdateEndpoints(t *testing.T) {
t.Fatalf("Unexpected error %v", err)
}
var endpointsOut api.Endpoints
err = runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &endpointsOut)
err = latest.Codec.DecodeInto([]byte(response.Node.Value), &endpointsOut)
if !reflect.DeepEqual(endpoints, endpointsOut) {
t.Errorf("Unexpected endpoints: %#v, expected %#v", endpointsOut, endpoints)
}

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"
@@ -201,13 +202,13 @@ func TestPodDecode(t *testing.T) {
ID: "foo",
},
}
body, err := runtime.DefaultCodec.Encode(expected)
body, err := latest.Codec.Encode(expected)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
actual := storage.New()
if err := runtime.DefaultCodec.DecodeInto(body, actual); err != nil {
if err := latest.Codec.DecodeInto(body, actual); err != nil {
t.Errorf("unexpected error: %v", err)
}

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,38 +14,72 @@ 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"`
Object EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"`
EmptyObject EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"`
runtime.JSONBase `yaml:",inline" json:",inline"`
Object EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"`
EmptyObject EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"`
}
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 = runtime.DefaultCodec
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: runtime.DefaultScheme.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: runtime.DefaultScheme.EncodeOrDie(podBar),
nodeValue: runtime.EncodeOrDie(codec, podBar),
expectEmit: true,
expectType: watch.Added,
expectObject: podBar,
},
"create but filter blocks": {
actions: []string{"create", "get"},
nodeValue: runtime.DefaultScheme.EncodeOrDie(podFoo),
nodeValue: runtime.EncodeOrDie(codec, podFoo),
expectEmit: false,
},
"delete": {
actions: []string{"delete"},
prevNodeValue: runtime.DefaultScheme.EncodeOrDie(podBar),
prevNodeValue: runtime.EncodeOrDie(codec, podBar),
expectEmit: true,
expectType: watch.Deleted,
expectObject: podBar,
},
"delete but filter blocks": {
actions: []string{"delete"},
nodeValue: runtime.DefaultScheme.EncodeOrDie(podFoo),
nodeValue: runtime.EncodeOrDie(codec, podFoo),
expectEmit: false,
},
"modify appears to create 1": {
actions: []string{"set", "compareAndSwap"},
nodeValue: runtime.DefaultScheme.EncodeOrDie(podBar),
nodeValue: runtime.EncodeOrDie(codec, podBar),
expectEmit: true,
expectType: watch.Added,
expectObject: podBar,
},
"modify appears to create 2": {
actions: []string{"set", "compareAndSwap"},
prevNodeValue: runtime.DefaultScheme.EncodeOrDie(podFoo),
nodeValue: runtime.DefaultScheme.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: runtime.DefaultScheme.EncodeOrDie(podBar),
nodeValue: runtime.DefaultScheme.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: runtime.DefaultScheme.EncodeOrDie(podBar),
nodeValue: runtime.DefaultScheme.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: runtime.DefaultScheme.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(runtime.DefaultScheme.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(runtime.DefaultScheme.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(runtime.DefaultScheme.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(runtime.DefaultScheme.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(runtime.DefaultScheme.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(runtime.DefaultScheme.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: runtime.DefaultScheme.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: runtime.DefaultScheme.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: runtime.DefaultScheme.EncodeOrDie(pod),
Value: runtime.EncodeOrDie(codec, pod),
CreatedIndex: 1,
ModifiedIndex: 1,
Nodes: etcd.Nodes{},
},
&etcd.Node{
Value: runtime.DefaultScheme.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: runtime.DefaultScheme.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: runtime.DefaultScheme.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 := runtime.DefaultScheme.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: runtime.DefaultCodec, ResourceVersioner: runtime.DefaultResourceVersioner}
helper := tools.EtcdHelper{Client: client, Codec: latest.Codec, ResourceVersioner: latest.ResourceVersioner}
withEtcdKey(func(key string) {
resp, err := client.Set(key, runtime.DefaultScheme.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)
}