Merge pull request #60709 from deads2k/client-02-fake-copy

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

deep copy fake client actions to avoid accidental mutation

I just got bit by this downstream.  Without a deep copy it is possible accidentally mutate the thing you created, thus invalidating your testing.  It's particularly nasty inside of a controller doing a loop on objects, making refs to them, and creating.  This works running in an actual process since we serialize and write, but fails unit tests since there is no serialization step.

@kubernetes/sig-api-machinery-bugs 

```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2018-04-20 08:28:41 -07:00 committed by GitHub
commit bfae47ad87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 6 deletions

View File

@ -324,6 +324,10 @@ type Action interface {
GetResource() schema.GroupVersionResource GetResource() schema.GroupVersionResource
GetSubresource() string GetSubresource() string
Matches(verb, resource string) bool Matches(verb, resource string) bool
// DeepCopy is used to copy an action to avoid any risk of accidental mutation. Most people never need to call this
// because the invocation logic deep copies before calls to storage and reactors.
DeepCopy() Action
} }
type GenericAction interface { type GenericAction interface {
@ -404,6 +408,10 @@ func (a ActionImpl) Matches(verb, resource string) bool {
return strings.ToLower(verb) == strings.ToLower(a.Verb) && return strings.ToLower(verb) == strings.ToLower(a.Verb) &&
strings.ToLower(resource) == strings.ToLower(a.Resource.Resource) strings.ToLower(resource) == strings.ToLower(a.Resource.Resource)
} }
func (a ActionImpl) DeepCopy() Action {
ret := a
return ret
}
type GenericActionImpl struct { type GenericActionImpl struct {
ActionImpl ActionImpl
@ -414,6 +422,14 @@ func (a GenericActionImpl) GetValue() interface{} {
return a.Value return a.Value
} }
func (a GenericActionImpl) DeepCopy() Action {
return GenericActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
// TODO this is wrong, but no worse than before
Value: a.Value,
}
}
type GetActionImpl struct { type GetActionImpl struct {
ActionImpl ActionImpl
Name string Name string
@ -423,6 +439,13 @@ func (a GetActionImpl) GetName() string {
return a.Name return a.Name
} }
func (a GetActionImpl) DeepCopy() Action {
return GetActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
Name: a.Name,
}
}
type ListActionImpl struct { type ListActionImpl struct {
ActionImpl ActionImpl
Kind schema.GroupVersionKind Kind schema.GroupVersionKind
@ -438,6 +461,18 @@ func (a ListActionImpl) GetListRestrictions() ListRestrictions {
return a.ListRestrictions return a.ListRestrictions
} }
func (a ListActionImpl) DeepCopy() Action {
return ListActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
Kind: a.Kind,
Name: a.Name,
ListRestrictions: ListRestrictions{
Labels: a.ListRestrictions.Labels.DeepCopySelector(),
Fields: a.ListRestrictions.Fields.DeepCopySelector(),
},
}
}
type CreateActionImpl struct { type CreateActionImpl struct {
ActionImpl ActionImpl
Name string Name string
@ -448,6 +483,14 @@ func (a CreateActionImpl) GetObject() runtime.Object {
return a.Object return a.Object
} }
func (a CreateActionImpl) DeepCopy() Action {
return CreateActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
Name: a.Name,
Object: a.Object.DeepCopyObject(),
}
}
type UpdateActionImpl struct { type UpdateActionImpl struct {
ActionImpl ActionImpl
Object runtime.Object Object runtime.Object
@ -457,6 +500,13 @@ func (a UpdateActionImpl) GetObject() runtime.Object {
return a.Object return a.Object
} }
func (a UpdateActionImpl) DeepCopy() Action {
return UpdateActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
Object: a.Object.DeepCopyObject(),
}
}
type PatchActionImpl struct { type PatchActionImpl struct {
ActionImpl ActionImpl
Name string Name string
@ -471,6 +521,16 @@ func (a PatchActionImpl) GetPatch() []byte {
return a.Patch return a.Patch
} }
func (a PatchActionImpl) DeepCopy() Action {
patch := make([]byte, len(a.Patch))
copy(patch, a.Patch)
return PatchActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
Name: a.Name,
Patch: patch,
}
}
type DeleteActionImpl struct { type DeleteActionImpl struct {
ActionImpl ActionImpl
Name string Name string
@ -480,6 +540,13 @@ func (a DeleteActionImpl) GetName() string {
return a.Name return a.Name
} }
func (a DeleteActionImpl) DeepCopy() Action {
return DeleteActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
Name: a.Name,
}
}
type DeleteCollectionActionImpl struct { type DeleteCollectionActionImpl struct {
ActionImpl ActionImpl
ListRestrictions ListRestrictions ListRestrictions ListRestrictions
@ -489,6 +556,16 @@ func (a DeleteCollectionActionImpl) GetListRestrictions() ListRestrictions {
return a.ListRestrictions return a.ListRestrictions
} }
func (a DeleteCollectionActionImpl) DeepCopy() Action {
return DeleteCollectionActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
ListRestrictions: ListRestrictions{
Labels: a.ListRestrictions.Labels.DeepCopySelector(),
Fields: a.ListRestrictions.Fields.DeepCopySelector(),
},
}
}
type WatchActionImpl struct { type WatchActionImpl struct {
ActionImpl ActionImpl
WatchRestrictions WatchRestrictions WatchRestrictions WatchRestrictions
@ -498,6 +575,17 @@ func (a WatchActionImpl) GetWatchRestrictions() WatchRestrictions {
return a.WatchRestrictions return a.WatchRestrictions
} }
func (a WatchActionImpl) DeepCopy() Action {
return WatchActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
WatchRestrictions: WatchRestrictions{
Labels: a.WatchRestrictions.Labels.DeepCopySelector(),
Fields: a.WatchRestrictions.Fields.DeepCopySelector(),
ResourceVersion: a.WatchRestrictions.ResourceVersion,
},
}
}
type ProxyGetActionImpl struct { type ProxyGetActionImpl struct {
ActionImpl ActionImpl
Scheme string Scheme string
@ -526,3 +614,18 @@ func (a ProxyGetActionImpl) GetPath() string {
func (a ProxyGetActionImpl) GetParams() map[string]string { func (a ProxyGetActionImpl) GetParams() map[string]string {
return a.Params return a.Params
} }
func (a ProxyGetActionImpl) DeepCopy() Action {
params := map[string]string{}
for k, v := range a.Params {
params[k] = v
}
return ProxyGetActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
Scheme: a.Scheme,
Name: a.Name,
Port: a.Port,
Path: a.Path,
Params: params,
}
}

View File

@ -131,13 +131,13 @@ func (c *Fake) Invokes(action Action, defaultReturnObj runtime.Object) (runtime.
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
c.actions = append(c.actions, action) c.actions = append(c.actions, action.DeepCopy())
for _, reactor := range c.ReactionChain { for _, reactor := range c.ReactionChain {
if !reactor.Handles(action) { if !reactor.Handles(action) {
continue continue
} }
handled, ret, err := reactor.React(action) handled, ret, err := reactor.React(action.DeepCopy())
if !handled { if !handled {
continue continue
} }
@ -154,13 +154,13 @@ func (c *Fake) InvokesWatch(action Action) (watch.Interface, error) {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
c.actions = append(c.actions, action) c.actions = append(c.actions, action.DeepCopy())
for _, reactor := range c.WatchReactionChain { for _, reactor := range c.WatchReactionChain {
if !reactor.Handles(action) { if !reactor.Handles(action) {
continue continue
} }
handled, ret, err := reactor.React(action) handled, ret, err := reactor.React(action.DeepCopy())
if !handled { if !handled {
continue continue
} }
@ -177,13 +177,13 @@ func (c *Fake) InvokesProxy(action Action) restclient.ResponseWrapper {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
c.actions = append(c.actions, action) c.actions = append(c.actions, action.DeepCopy())
for _, reactor := range c.ProxyReactionChain { for _, reactor := range c.ProxyReactionChain {
if !reactor.Handles(action) { if !reactor.Handles(action) {
continue continue
} }
handled, ret, err := reactor.React(action) handled, ret, err := reactor.React(action.DeepCopy())
if !handled || err != nil { if !handled || err != nil {
continue continue
} }

View File

@ -51,6 +51,18 @@ func (i GetForActionImpl) GetSubresource() string {
return i.MetricName return i.MetricName
} }
func (i GetForActionImpl) DeepCopy() testing.Action {
var labelSelector labels.Selector
if i.LabelSelector != nil {
labelSelector = i.LabelSelector.DeepCopySelector()
}
return GetForActionImpl{
GetAction: i.GetAction.DeepCopy().(testing.GetAction),
MetricName: i.MetricName,
LabelSelector: labelSelector,
}
}
func NewGetForAction(groupKind schema.GroupKind, namespace, name string, metricName string, labelSelector labels.Selector) GetForActionImpl { func NewGetForAction(groupKind schema.GroupKind, namespace, name string, metricName string, labelSelector labels.Selector) GetForActionImpl {
// the version doesn't matter // the version doesn't matter
gvk := groupKind.WithVersion("") gvk := groupKind.WithVersion("")