Merge pull request #856 from pmorie/workaround-timestamp

Set CreationTimestamp in each storage implementation
pull/6/head
Clayton Coleman 2014-08-14 17:25:58 -04:00
commit 87256127e0
10 changed files with 262 additions and 22 deletions

View File

@ -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.
@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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,

View File

@ -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"
@ -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 {

View File

@ -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.
@ -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 {

View File

@ -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"
@ -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

97
pkg/util/time.go Normal file
View File

@ -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
pkg/util/time_test.go Normal file
View File

@ -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)
}
}
}

View File

@ -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)
}
}
}
@ -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))
}
}
}
@ -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)
}
}
}
@ -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))
}
}
}