Skip to content

Commit

Permalink
Merge pull request kubernetes#856 from pmorie/workaround-timestamp
Browse files Browse the repository at this point in the history
Set CreationTimestamp in each storage implementation
  • Loading branch information
smarterclayton committed Aug 14, 2014
2 parents f521fff + c691600 commit 8725612
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 22 deletions.
7 changes: 6 additions & 1 deletion pkg/api/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
// only when all 8 bytes are set.
j.ResourceVersion = c.RandUint64() >> 8
j.SelfLink = c.RandString()
j.CreationTimestamp = 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.
Expand Down Expand Up @@ -113,6 +117,7 @@ func runTest(t *testing.T, source interface{}) {
t.Errorf("%v: %v (%#v)", name, err, source)
return
}

obj2, err := Decode(data)
if err != nil {
t.Errorf("%v: %v", name, err)
Expand Down
12 changes: 6 additions & 6 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,12 @@ type Event struct {

// 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 string `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"`
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.
Expand Down
12 changes: 6 additions & 6 deletions pkg/api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,12 @@ type Event struct {

// 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 string `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"`
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.
Expand Down
2 changes: 1 addition & 1 deletion pkg/conversion/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (c *Converter) Register(conversionFunc interface{}) error {
type FieldMatchingFlags int

const (
// Loop through destiation fields, search for matching source
// Loop through destination fields, search for matching source
// field to copy it from. Source fields with no corresponding
// destination field will be ignored. If SourceToDest is
// specified, this flag is ignored. If niether is specified,
Expand Down
4 changes: 4 additions & 0 deletions pkg/registry/controller/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"

"code.google.com/p/go-uuid/uuid"
Expand Down Expand Up @@ -61,6 +62,9 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) {
if errs := api.ValidateReplicationController(controller); len(errs) > 0 {
return nil, fmt.Errorf("Validation errors: %v", errs)
}

controller.CreationTimestamp = util.Now()

return apiserver.MakeAsync(func() (interface{}, error) {
err := rs.registry.CreateController(*controller)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions pkg/registry/minion/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

// RegistryStorage implements the RESTStorage interface, backed by a MinionRegistry.
Expand All @@ -44,6 +45,9 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) {
if minion.ID == "" {
return nil, fmt.Errorf("ID should not be empty: %#v", minion)
}

minion.CreationTimestamp = util.Now()

return apiserver.MakeAsync(func() (interface{}, error) {
err := rs.registry.Insert(minion.ID)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions pkg/registry/pod/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

"code.google.com/p/go-uuid/uuid"
"github.com/golang/glog"
Expand Down Expand Up @@ -76,6 +77,9 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) {
if errs := api.ValidatePod(pod); len(errs) > 0 {
return nil, fmt.Errorf("Validation errors: %v", errs)
}

pod.CreationTimestamp = util.Now()

return apiserver.MakeAsync(func() (interface{}, error) {
if err := rs.scheduleAndCreatePod(*pod); err != nil {
return nil, err
Expand Down
97 changes: 97 additions & 0 deletions pkg/util/time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package util

import (
"encoding/json"
"time"
)

// Time is a wrapper around time.Time which supports correct
// marshaling to YAML and JSON. Wrappers are provided for many
// of the factory methods that the time package offers.
type Time struct {
time.Time
}

// Date returns the Time corresponding to the supplied parameters
// by wrapping time.Date.
func Date(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) Time {
return Time{time.Date(year, month, day, hour, min, sec, nsec, loc)}
}

// Now returns the current local time.
func Now() Time {
return Time{time.Now()}
}

// Unix returns the local time corresponding to the given Unix time
// by wrapping time.Unix.
func Unix(sec int64, nsec int64) Time {
return Time{time.Unix(sec, nsec)}
}

// Rfc3339Copy returns a copy of the Time at second-level precision.
func (t Time) Rfc3339Copy() Time {
copied, _ := time.Parse(time.RFC3339, t.Format(time.RFC3339))
return Time{copied}
}

// UnmarshalJSON implements the json.Unmarshaller interface.
func (t *Time) UnmarshalJSON(b []byte) error {
if len(b) == 4 && string(b) == "null" {
t.Time = time.Time{}
return nil
}

var str string
json.Unmarshal(b, &str)

pt, err := time.Parse(time.RFC3339, str)
if err != nil {
return err
}

t.Time = pt
return nil
}

// MarshalJSON implements the json.Marshaler interface.
func (t Time) MarshalJSON() ([]byte, error) {
if t.IsZero() {
// Encode unset/nil objects as JSON's "null".
return []byte("null"), nil
}

return json.Marshal(t.Format(time.RFC3339))
}

// SetYAML implements the yaml.Setter interface.
func (t *Time) SetYAML(tag string, value interface{}) bool {
if value == nil {
t.Time = time.Time{}
return true
}

str, ok := value.(string)
if !ok {
return false
}

pt, err := time.Parse(time.RFC3339, str)
if err != nil {
return false
}

t.Time = pt
return true
}

// GetYAML implements the yaml.Getter interface.
func (t Time) GetYAML() (tag string, value interface{}) {
if t.IsZero() {
value = "null"
return
}

value = t.Format(time.RFC3339)
return tag, value
}
126 changes: 126 additions & 0 deletions pkg/util/time_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package util

import (
"encoding/json"
"reflect"
"testing"
"time"

"gopkg.in/v1/yaml"
)

type TimeHolder struct {
T Time `json:"t" yaml:"t"`
}

func TestTimeMarshalYAML(t *testing.T) {
cases := []struct {
input Time
result string
}{
{Time{}, "t: \"null\"\n"},
{Date(1998, time.May, 5, 5, 5, 5, 50, time.UTC), "t: 1998-05-05T05:05:05Z\n"},
{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), "t: 1998-05-05T05:05:05Z\n"},
}

for _, c := range cases {
input := TimeHolder{c.input}
result, err := yaml.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input: '%v': %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input: '%v': expected %+v, got %q", input, c.result, string(result))
}
}
}

func TestTimeUnmarshalYAML(t *testing.T) {
cases := []struct {
input string
result Time
}{
{"t: \"null\"\n", Time{}},
{"t: 1998-05-05T05:05:05Z\n", Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC)},
}

for _, c := range cases {
var result TimeHolder
if err := yaml.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if result.T != c.result {
t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result)
}
}
}

func TestTimeMarshalJSON(t *testing.T) {
cases := []struct {
input Time
result string
}{
{Time{}, "{\"t\":null}"},
{Date(1998, time.May, 5, 5, 5, 5, 50, time.UTC), "{\"t\":\"1998-05-05T05:05:05Z\"}"},
{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), "{\"t\":\"1998-05-05T05:05:05Z\"}"},
}

for _, c := range cases {
input := TimeHolder{c.input}
result, err := json.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input: '%v': %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input: '%v': expected %+v, got %q", input, c.result, string(result))
}
}
}

func TestTimeUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result Time
}{
{"{\"t\":null}", Time{}},
{"{\"t\":\"1998-05-05T05:05:05Z\"}", Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC)},
}

for _, c := range cases {
var result TimeHolder
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if result.T != c.result {
t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result)
}
}
}

func TestTimeMarshalJSONUnmarshalYAML(t *testing.T) {
cases := []struct {
input Time
}{
{Time{}},
{Date(1998, time.May, 5, 5, 5, 5, 50, time.UTC).Rfc3339Copy()},
{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Rfc3339Copy()},
}

for _, c := range cases {
input := TimeHolder{c.input}
jsonMarshalled, err := json.Marshal(&input)
if err != nil {
t.Errorf("1: Failed to marshal input: '%v': %v", input, err)
}

var result TimeHolder
err = yaml.Unmarshal(jsonMarshalled, &result)
if err != nil {
t.Errorf("2: Failed to unmarshall '%+v': %v", string(jsonMarshalled), err)
}

if !reflect.DeepEqual(input, result) {
t.Errorf("3: Failed to marshal input '%+v': got %+v", input, result)
}
}
}
16 changes: 8 additions & 8 deletions pkg/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ func TestIntOrStringUnmarshalYAML(t *testing.T) {
for _, c := range cases {
var result IntOrStringHolder
if err := yaml.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if result.IOrS != c.result {
t.Errorf("Failed to unmarshal IntOrString: got %+v", result)
t.Errorf("Failed to unmarshal input '%v': expected: %+v, got %+v", c.input, c.result, result)
}
}
}
Expand All @@ -122,10 +122,10 @@ func TestIntOrStringMarshalYAML(t *testing.T) {
input := IntOrStringHolder{c.input}
result, err := yaml.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal: %v", err)
t.Errorf("Failed to marshal input '%v': %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal IntOrString: got %q", string(result))
t.Errorf("Failed to marshal input '%v': expected: %+v, got %q", input, c.result, string(result))
}
}
}
Expand All @@ -142,10 +142,10 @@ func TestIntOrStringUnmarshalJSON(t *testing.T) {
for _, c := range cases {
var result IntOrStringHolder
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if result.IOrS != c.result {
t.Errorf("Failed to unmarshal IntOrString: got %+v", result)
t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result)
}
}
}
Expand All @@ -163,10 +163,10 @@ func TestIntOrStringMarshalJSON(t *testing.T) {
input := IntOrStringHolder{c.input}
result, err := json.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal: %v", err)
t.Errorf("Failed to marshal input '%v': %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal IntOrString: got %q", string(result))
t.Errorf("Failed to marshal input '%v': expected: %+v, got %q", input, c.result, string(result))
}
}
}
Expand Down

0 comments on commit 8725612

Please sign in to comment.