Skip to content

Commit

Permalink
Make runtime less global for Codec
Browse files Browse the repository at this point in the history
* Make Codec separate from Scheme
* Move EncodeOrDie off Scheme to take a Codec
* Make Copy work without a Codec
* Create a "latest" package that imports all versions and
  sets global defaults for "most recent encoding"
  * v1beta1 is the current "latest", v1beta2 exists
  * Kill DefaultCodec, replace it with "latest.Codec"
  * This updates the client and etcd to store the latest known version
* EmbeddedObject is per schema and per package now
* Move runtime.DefaultScheme to api.Scheme
* Split out WatchEvent since it's not an API object today, treat it
like a special object in api
* Kill DefaultResourceVersioner, instead place it on "latest" (as the
  package that understands all packages)
* Move objDiff to runtime.ObjectDiff
  • Loading branch information
smarterclayton committed Sep 16, 2014
1 parent 154a91c commit 61e3ce7
Show file tree
Hide file tree
Showing 58 changed files with 944 additions and 389 deletions.
3 changes: 2 additions & 1 deletion cmd/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions cmd/kubecfg/kubecfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion examples/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
55 changes: 55 additions & 0 deletions pkg/api/conversion.go
Original file line number Diff line number Diff line change
@@ -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 changes: 20 additions & 0 deletions pkg/api/latest/doc.go
Original file line number Diff line number Diff line change
@@ -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 changes: 37 additions & 0 deletions pkg/api/latest/latest.go
Original file line number Diff line number Diff line change
@@ -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()
146 changes: 146 additions & 0 deletions pkg/api/latest/latest_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
4 changes: 3 additions & 1 deletion pkg/api/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{},
Expand Down
Loading

0 comments on commit 61e3ce7

Please sign in to comment.