diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go index 9abf7f8a6a..517802e769 100644 --- a/pkg/runtime/interfaces.go +++ b/pkg/runtime/interfaces.go @@ -40,6 +40,15 @@ type ResourceVersioner interface { ResourceVersion(obj Object) (uint64, error) } +// SelfLinker provides methods for setting and retrieving the SelfLink field of an API object. +type SelfLinker interface { + SetSelfLink(obj Object, selfLink string) error + SelfLink(obj Object) (string, error) + + // Knowing ID is sometimes necssary to use a SelfLinker. + ID(obj Object) (string, error) +} + // All api types must support the Object interface. It's deliberately tiny so that this is not an onerous // burden. Implement it with a pointer reciever; this will allow us to use the go compiler to check the // one thing about our objects that it's capable of checking for us. diff --git a/pkg/runtime/jsonbase.go b/pkg/runtime/jsonbase.go index f6f7c77fe7..4bd78a8288 100644 --- a/pkg/runtime/jsonbase.go +++ b/pkg/runtime/jsonbase.go @@ -21,15 +21,16 @@ import ( "reflect" ) -// NewJSONBaseResourceVersioner returns a resourceVersioner that can set or +// NewJSONBaseResourceVersioner returns a ResourceVersioner that can set or // retrieve ResourceVersion on objects derived from JSONBase. func NewJSONBaseResourceVersioner() ResourceVersioner { - return &jsonBaseResourceVersioner{} + return jsonBaseModifier{} } -type jsonBaseResourceVersioner struct{} +// jsonBaseModifier implements ResourceVersioner and SelfLinker. +type jsonBaseModifier struct{} -func (v jsonBaseResourceVersioner) ResourceVersion(obj Object) (uint64, error) { +func (v jsonBaseModifier) ResourceVersion(obj Object) (uint64, error) { json, err := FindJSONBase(obj) if err != nil { return 0, err @@ -37,7 +38,7 @@ func (v jsonBaseResourceVersioner) ResourceVersion(obj Object) (uint64, error) { return json.ResourceVersion(), nil } -func (v jsonBaseResourceVersioner) SetResourceVersion(obj Object, version uint64) error { +func (v jsonBaseModifier) SetResourceVersion(obj Object, version uint64) error { json, err := FindJSONBase(obj) if err != nil { return err @@ -46,6 +47,36 @@ func (v jsonBaseResourceVersioner) SetResourceVersion(obj Object, version uint64 return nil } +func (v jsonBaseModifier) ID(obj Object) (string, error) { + json, err := FindJSONBase(obj) + if err != nil { + return "", err + } + return json.ID(), nil +} + +func (v jsonBaseModifier) SelfLink(obj Object) (string, error) { + json, err := FindJSONBase(obj) + if err != nil { + return "", err + } + return json.SelfLink(), nil +} + +func (v jsonBaseModifier) SetSelfLink(obj Object, selfLink string) error { + json, err := FindJSONBase(obj) + if err != nil { + return err + } + json.SetSelfLink(selfLink) + return nil +} + +// NewJSONBaseSelfLinker returns a SelfLinker that works on all JSONBase SelfLink fields. +func NewJSONBaseSelfLinker() SelfLinker { + return jsonBaseModifier{} +} + // JSONBaseInterface lets you work with a JSONBase from any of the versioned or // internal APIObjects. type JSONBaseInterface interface { @@ -57,6 +88,8 @@ type JSONBaseInterface interface { SetKind(kind string) ResourceVersion() uint64 SetResourceVersion(version uint64) + SelfLink() string + SetSelfLink(selfLink string) } type genericJSONBase struct { @@ -64,6 +97,7 @@ type genericJSONBase struct { apiVersion *string kind *string resourceVersion *uint64 + selfLink *string } func (g genericJSONBase) ID() string { @@ -98,6 +132,14 @@ func (g genericJSONBase) SetResourceVersion(version uint64) { *g.resourceVersion = version } +func (g genericJSONBase) SelfLink() string { + return *g.selfLink +} + +func (g genericJSONBase) SetSelfLink(selfLink string) { + *g.selfLink = selfLink +} + // fieldPtr puts the address of fieldName, which must be a member of v, // into dest, which must be an address of a variable to which this field's // address can be assigned. @@ -140,5 +182,8 @@ func newGenericJSONBase(v reflect.Value) (genericJSONBase, error) { if err := fieldPtr(v, "ResourceVersion", &g.resourceVersion); err != nil { return g, err } + if err := fieldPtr(v, "SelfLink", &g.selfLink); err != nil { + return g, err + } return g, nil } diff --git a/pkg/runtime/jsonbase_test.go b/pkg/runtime/jsonbase_test.go index 0e0165aacb..0115dfee24 100644 --- a/pkg/runtime/jsonbase_test.go +++ b/pkg/runtime/jsonbase_test.go @@ -37,6 +37,7 @@ func TestGenericJSONBase(t *testing.T) { APIVersion: "a", Kind: "b", ResourceVersion: 1, + SelfLink: "some/place/only/we/know", } g, err := newGenericJSONBase(reflect.ValueOf(&j).Elem()) if err != nil { @@ -56,11 +57,15 @@ func TestGenericJSONBase(t *testing.T) { if e, a := uint64(1), jbi.ResourceVersion(); e != a { t.Errorf("expected %v, got %v", e, a) } + if e, a := "some/place/only/we/know", jbi.SelfLink(); e != a { + t.Errorf("expected %v, got %v", e, a) + } jbi.SetID("bar") jbi.SetAPIVersion("c") jbi.SetKind("d") jbi.SetResourceVersion(2) + jbi.SetSelfLink("google.com") // Prove that jbi changes the original object. if e, a := "bar", j.ID; e != a { @@ -75,6 +80,9 @@ func TestGenericJSONBase(t *testing.T) { if e, a := uint64(2), j.ResourceVersion; e != a { t.Errorf("expected %v, got %v", e, a) } + if e, a := "google.com", j.SelfLink; e != a { + t.Errorf("expected %v, got %v", e, a) + } } type MyAPIObject struct { @@ -141,3 +149,48 @@ func TestResourceVersionerOfAPI(t *testing.T) { } } } + +func TestJSONBaseSelfLinker(t *testing.T) { + table := map[string]struct { + obj Object + expect string + try string + succeed bool + }{ + "normal": { + obj: &MyAPIObject{JSONBase: JSONBase{SelfLink: "foobar"}}, + expect: "foobar", + try: "newbar", + succeed: true, + }, + "fail": { + obj: &MyIncorrectlyMarkedAsAPIObject{}, + succeed: false, + }, + } + + linker := NewJSONBaseSelfLinker() + for name, item := range table { + got, err := linker.SelfLink(item.obj) + if e, a := item.succeed, err == nil; e != a { + t.Errorf("%v: expected %v, got %v", name, e, a) + } + if e, a := item.expect, got; item.succeed && e != a { + t.Errorf("%v: expected %v, got %v", name, e, a) + } + + err = linker.SetSelfLink(item.obj, item.try) + if e, a := item.succeed, err == nil; e != a { + t.Errorf("%v: expected %v, got %v", name, e, a) + } + if item.succeed { + got, err := linker.SelfLink(item.obj) + if err != nil { + t.Errorf("%v: expected no err, got %v", name, err) + } + if e, a := item.try, got; e != a { + t.Errorf("%v: expected %v, got %v", name, e, a) + } + } + } +}