Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
a56bbbf8
Commit
a56bbbf8
authored
Feb 28, 2016
by
Brian Grant
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #22143 from bprashanth/graceful_del
Don't double count graceful deletion
parents
cdf456af
859f6b13
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
232 additions
and
41 deletions
+232
-41
replica_set.go
pkg/controller/replicaset/replica_set.go
+35
-22
replica_set_test.go
pkg/controller/replicaset/replica_set_test.go
+82
-0
replication_controller.go
pkg/controller/replication/replication_controller.go
+34
-19
replication_controller_test.go
pkg/controller/replication/replication_controller_test.go
+81
-0
No files found.
pkg/controller/replicaset/replica_set.go
View file @
a56bbbf8
...
@@ -289,21 +289,24 @@ func isReplicaSetMatch(pod *api.Pod, rs *extensions.ReplicaSet) bool {
...
@@ -289,21 +289,24 @@ func isReplicaSetMatch(pod *api.Pod, rs *extensions.ReplicaSet) bool {
// When a pod is created, enqueue the replica set that manages it and update it's expectations.
// When a pod is created, enqueue the replica set that manages it and update it's expectations.
func
(
rsc
*
ReplicaSetController
)
addPod
(
obj
interface
{})
{
func
(
rsc
*
ReplicaSetController
)
addPod
(
obj
interface
{})
{
pod
:=
obj
.
(
*
api
.
Pod
)
pod
:=
obj
.
(
*
api
.
Pod
)
rs
:=
rsc
.
getPodReplicaSet
(
pod
)
if
rs
==
nil
{
return
}
rsKey
,
err
:=
controller
.
KeyFunc
(
rs
)
if
err
!=
nil
{
glog
.
Errorf
(
"Couldn't get key for replication controller %#v: %v"
,
rs
,
err
)
return
}
if
pod
.
DeletionTimestamp
!=
nil
{
if
pod
.
DeletionTimestamp
!=
nil
{
// on a restart of the controller manager, it's possible a new pod shows up in a state that
// on a restart of the controller manager, it's possible a new pod shows up in a state that
// is already pending deletion. Prevent the pod from being a creation observation.
// is already pending deletion. Prevent the pod from being a creation observation.
rsc
.
deletePod
(
pod
)
rsc
.
expectations
.
DeletionObserved
(
rsKey
)
return
}
else
{
}
if
rs
:=
rsc
.
getPodReplicaSet
(
pod
);
rs
!=
nil
{
rsKey
,
err
:=
controller
.
KeyFunc
(
rs
)
if
err
!=
nil
{
glog
.
Errorf
(
"Couldn't get key for ReplicaSet %#v: %v"
,
rs
,
err
)
return
}
rsc
.
expectations
.
CreationObserved
(
rsKey
)
rsc
.
expectations
.
CreationObserved
(
rsKey
)
rsc
.
enqueueReplicaSet
(
rs
)
}
}
rsc
.
enqueueReplicaSet
(
rs
)
}
}
// When a pod is updated, figure out what replica set/s manage it and wake them
// When a pod is updated, figure out what replica set/s manage it and wake them
...
@@ -314,22 +317,28 @@ func (rsc *ReplicaSetController) updatePod(old, cur interface{}) {
...
@@ -314,22 +317,28 @@ func (rsc *ReplicaSetController) updatePod(old, cur interface{}) {
// A periodic relist will send update events for all known pods.
// A periodic relist will send update events for all known pods.
return
return
}
}
// TODO: Write a unittest for this case
curPod
:=
cur
.
(
*
api
.
Pod
)
curPod
:=
cur
.
(
*
api
.
Pod
)
if
curPod
.
DeletionTimestamp
!=
nil
{
rs
:=
rsc
.
getPodReplicaSet
(
curPod
)
// when a pod is deleted gracefully it's deletion timestamp is first modified to reflect a grace period,
if
rs
==
nil
{
// and after such time has passed, the kubelet actually deletes it from the store. We receive an update
// for modification of the deletion timestamp and expect an ReplicaSet to create more replicas asap, not wait
// until the kubelet actually deletes the pod. This is different from the Phase of a pod changing, because
// a ReplicaSet never initiates a phase change, and so is never asleep waiting for the same.
rsc
.
deletePod
(
curPod
)
return
return
}
}
if
rs
:=
rsc
.
getPodReplicaSet
(
curPod
);
rs
!=
nil
{
rsKey
,
err
:=
controller
.
KeyFunc
(
rs
)
rsc
.
enqueueReplicaSet
(
rs
)
if
err
!=
nil
{
glog
.
Errorf
(
"Couldn't get key for replication controller %#v: %v"
,
rs
,
err
)
return
}
}
oldPod
:=
old
.
(
*
api
.
Pod
)
oldPod
:=
old
.
(
*
api
.
Pod
)
// Only need to get the old replica set if the labels changed.
if
curPod
.
DeletionTimestamp
!=
nil
&&
oldPod
.
DeletionTimestamp
==
nil
{
// when a pod is deleted gracefully it's deletion timestamp is first modified to reflect a grace period,
// and after such time has passed, the kubelet actually deletes it from the store. We receive an update
// for modification of the deletion timestamp and expect an rc to create more replicas asap, not wait
// until the kubelet actually deletes the pod. This is different from the Phase of a pod changing, because
// an rc never initiates a phase change, and so is never asleep waiting for the same.
rsc
.
expectations
.
DeletionObserved
(
rsKey
)
}
rsc
.
enqueueReplicaSet
(
rs
)
if
!
reflect
.
DeepEqual
(
curPod
.
Labels
,
oldPod
.
Labels
)
{
if
!
reflect
.
DeepEqual
(
curPod
.
Labels
,
oldPod
.
Labels
)
{
// If the old and new ReplicaSet are the same, the first one that syncs
// If the old and new ReplicaSet are the same, the first one that syncs
// will set expectations preventing any damage from the second.
// will set expectations preventing any damage from the second.
...
@@ -366,7 +375,11 @@ func (rsc *ReplicaSetController) deletePod(obj interface{}) {
...
@@ -366,7 +375,11 @@ func (rsc *ReplicaSetController) deletePod(obj interface{}) {
glog
.
Errorf
(
"Couldn't get key for ReplicaSet %#v: %v"
,
rs
,
err
)
glog
.
Errorf
(
"Couldn't get key for ReplicaSet %#v: %v"
,
rs
,
err
)
return
return
}
}
rsc
.
expectations
.
DeletionObserved
(
rsKey
)
// This method only manages expectations for the case where a pod is
// deleted without a grace period.
if
pod
.
DeletionTimestamp
==
nil
{
rsc
.
expectations
.
DeletionObserved
(
rsKey
)
}
rsc
.
enqueueReplicaSet
(
rs
)
rsc
.
enqueueReplicaSet
(
rs
)
}
}
}
}
...
...
pkg/controller/replicaset/replica_set_test.go
View file @
a56bbbf8
...
@@ -910,3 +910,85 @@ func TestOverlappingRSs(t *testing.T) {
...
@@ -910,3 +910,85 @@ func TestOverlappingRSs(t *testing.T) {
}
}
}
}
}
}
func
TestDeletionTimestamp
(
t
*
testing
.
T
)
{
c
:=
clientset
.
NewForConfigOrDie
(
&
client
.
Config
{
Host
:
""
,
ContentConfig
:
client
.
ContentConfig
{
GroupVersion
:
testapi
.
Default
.
GroupVersion
()}})
labelMap
:=
map
[
string
]
string
{
"foo"
:
"bar"
}
manager
:=
NewReplicaSetController
(
c
,
controller
.
NoResyncPeriodFunc
,
10
,
0
)
manager
.
podStoreSynced
=
alwaysReady
rs
:=
newReplicaSet
(
1
,
labelMap
)
manager
.
rsStore
.
Store
.
Add
(
rs
)
rsKey
,
err
:=
controller
.
KeyFunc
(
rs
)
if
err
!=
nil
{
t
.
Errorf
(
"Couldn't get key for object %+v: %v"
,
rs
,
err
)
}
pod
:=
newPodList
(
nil
,
1
,
api
.
PodPending
,
labelMap
,
rs
)
.
Items
[
0
]
pod
.
DeletionTimestamp
=
&
unversioned
.
Time
{
time
.
Now
()}
manager
.
expectations
.
SetExpectations
(
rsKey
,
0
,
1
)
// A pod added with a deletion timestamp should decrement deletions, not creations.
manager
.
addPod
(
&
pod
)
queueRC
,
_
:=
manager
.
queue
.
Get
()
if
queueRC
!=
rsKey
{
t
.
Fatalf
(
"Expected to find key %v in queue, found %v"
,
rsKey
,
queueRC
)
}
manager
.
queue
.
Done
(
rsKey
)
podExp
,
exists
,
err
:=
manager
.
expectations
.
GetExpectations
(
rsKey
)
if
!
exists
||
err
!=
nil
||
!
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
// An update from no deletion timestamp to having one should be treated
// as a deletion.
oldPod
:=
newPodList
(
nil
,
1
,
api
.
PodPending
,
labelMap
,
rs
)
.
Items
[
0
]
manager
.
expectations
.
SetExpectations
(
rsKey
,
0
,
1
)
manager
.
updatePod
(
&
oldPod
,
&
pod
)
queueRC
,
_
=
manager
.
queue
.
Get
()
if
queueRC
!=
rsKey
{
t
.
Fatalf
(
"Expected to find key %v in queue, found %v"
,
rsKey
,
queueRC
)
}
manager
.
queue
.
Done
(
rsKey
)
podExp
,
exists
,
err
=
manager
.
expectations
.
GetExpectations
(
rsKey
)
if
!
exists
||
err
!=
nil
||
!
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
// An update to the pod (including an update to the deletion timestamp)
// should not be counted as a second delete.
manager
.
expectations
.
SetExpectations
(
rsKey
,
0
,
1
)
oldPod
.
DeletionTimestamp
=
&
unversioned
.
Time
{
time
.
Now
()}
manager
.
updatePod
(
&
oldPod
,
&
pod
)
podExp
,
exists
,
err
=
manager
.
expectations
.
GetExpectations
(
rsKey
)
if
!
exists
||
err
!=
nil
||
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
// A pod with a non-nil deletion timestamp should also be ignored by the
// delete handler, because it's already been counted in the update.
manager
.
deletePod
(
&
pod
)
podExp
,
exists
,
err
=
manager
.
expectations
.
GetExpectations
(
rsKey
)
if
!
exists
||
err
!=
nil
||
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
// A pod with a nil timestamp should be counted as a deletion.
pod
.
DeletionTimestamp
=
nil
manager
.
deletePod
(
&
pod
)
queueRC
,
_
=
manager
.
queue
.
Get
()
if
queueRC
!=
rsKey
{
t
.
Fatalf
(
"Expected to find key %v in queue, found %v"
,
rsKey
,
queueRC
)
}
manager
.
queue
.
Done
(
rsKey
)
podExp
,
exists
,
err
=
manager
.
expectations
.
GetExpectations
(
rsKey
)
if
!
exists
||
err
!=
nil
||
!
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
}
pkg/controller/replication/replication_controller.go
View file @
a56bbbf8
...
@@ -286,21 +286,25 @@ func isControllerMatch(pod *api.Pod, rc *api.ReplicationController) bool {
...
@@ -286,21 +286,25 @@ func isControllerMatch(pod *api.Pod, rc *api.ReplicationController) bool {
// When a pod is created, enqueue the controller that manages it and update it's expectations.
// When a pod is created, enqueue the controller that manages it and update it's expectations.
func
(
rm
*
ReplicationManager
)
addPod
(
obj
interface
{})
{
func
(
rm
*
ReplicationManager
)
addPod
(
obj
interface
{})
{
pod
:=
obj
.
(
*
api
.
Pod
)
pod
:=
obj
.
(
*
api
.
Pod
)
rc
:=
rm
.
getPodController
(
pod
)
if
rc
==
nil
{
return
}
rcKey
,
err
:=
controller
.
KeyFunc
(
rc
)
if
err
!=
nil
{
glog
.
Errorf
(
"Couldn't get key for replication controller %#v: %v"
,
rc
,
err
)
return
}
if
pod
.
DeletionTimestamp
!=
nil
{
if
pod
.
DeletionTimestamp
!=
nil
{
// on a restart of the controller manager, it's possible a new pod shows up in a state that
// on a restart of the controller manager, it's possible a new pod shows up in a state that
// is already pending deletion. Prevent the pod from being a creation observation.
// is already pending deletion. Prevent the pod from being a creation observation.
rm
.
deletePod
(
pod
)
rm
.
expectations
.
DeletionObserved
(
rcKey
)
return
}
else
{
}
if
rc
:=
rm
.
getPodController
(
pod
);
rc
!=
nil
{
rcKey
,
err
:=
controller
.
KeyFunc
(
rc
)
if
err
!=
nil
{
glog
.
Errorf
(
"Couldn't get key for replication controller %#v: %v"
,
rc
,
err
)
return
}
rm
.
expectations
.
CreationObserved
(
rcKey
)
rm
.
expectations
.
CreationObserved
(
rcKey
)
rm
.
enqueueController
(
rc
)
}
}
rm
.
enqueueController
(
rc
)
}
}
// When a pod is updated, figure out what controller/s manage it and wake them
// When a pod is updated, figure out what controller/s manage it and wake them
...
@@ -311,21 +315,28 @@ func (rm *ReplicationManager) updatePod(old, cur interface{}) {
...
@@ -311,21 +315,28 @@ func (rm *ReplicationManager) updatePod(old, cur interface{}) {
// A periodic relist will send update events for all known pods.
// A periodic relist will send update events for all known pods.
return
return
}
}
// TODO: Write a unittest for this case
curPod
:=
cur
.
(
*
api
.
Pod
)
curPod
:=
cur
.
(
*
api
.
Pod
)
if
curPod
.
DeletionTimestamp
!=
nil
{
rc
:=
rm
.
getPodController
(
curPod
)
if
rc
==
nil
{
return
}
rcKey
,
err
:=
controller
.
KeyFunc
(
rc
)
if
err
!=
nil
{
glog
.
Errorf
(
"Couldn't get key for replication controller %#v: %v"
,
rc
,
err
)
return
}
oldPod
:=
old
.
(
*
api
.
Pod
)
if
curPod
.
DeletionTimestamp
!=
nil
&&
oldPod
.
DeletionTimestamp
==
nil
{
// when a pod is deleted gracefully it's deletion timestamp is first modified to reflect a grace period,
// when a pod is deleted gracefully it's deletion timestamp is first modified to reflect a grace period,
// and after such time has passed, the kubelet actually deletes it from the store. We receive an update
// and after such time has passed, the kubelet actually deletes it from the store. We receive an update
// for modification of the deletion timestamp and expect an rc to create more replicas asap, not wait
// for modification of the deletion timestamp and expect an rc to create more replicas asap, not wait
// until the kubelet actually deletes the pod. This is different from the Phase of a pod changing, because
// until the kubelet actually deletes the pod. This is different from the Phase of a pod changing, because
// an rc never initiates a phase change, and so is never asleep waiting for the same.
// an rc never initiates a phase change, and so is never asleep waiting for the same.
rm
.
deletePod
(
curPod
)
rm
.
expectations
.
DeletionObserved
(
rcKey
)
return
}
if
rc
:=
rm
.
getPodController
(
curPod
);
rc
!=
nil
{
rm
.
enqueueController
(
rc
)
}
}
oldPod
:=
old
.
(
*
api
.
Pod
)
rm
.
enqueueController
(
rc
)
// Only need to get the old controller if the labels changed.
// Only need to get the old controller if the labels changed.
if
!
reflect
.
DeepEqual
(
curPod
.
Labels
,
oldPod
.
Labels
)
{
if
!
reflect
.
DeepEqual
(
curPod
.
Labels
,
oldPod
.
Labels
)
{
// If the old and new rc are the same, the first one that syncs
// If the old and new rc are the same, the first one that syncs
...
@@ -363,7 +374,11 @@ func (rm *ReplicationManager) deletePod(obj interface{}) {
...
@@ -363,7 +374,11 @@ func (rm *ReplicationManager) deletePod(obj interface{}) {
glog
.
Errorf
(
"Couldn't get key for replication controller %#v: %v"
,
rc
,
err
)
glog
.
Errorf
(
"Couldn't get key for replication controller %#v: %v"
,
rc
,
err
)
return
return
}
}
rm
.
expectations
.
DeletionObserved
(
rcKey
)
// This method only manages expectations for the case where a pod is
// deleted without a grace period.
if
pod
.
DeletionTimestamp
==
nil
{
rm
.
expectations
.
DeletionObserved
(
rcKey
)
}
rm
.
enqueueController
(
rc
)
rm
.
enqueueController
(
rc
)
}
}
}
}
...
...
pkg/controller/replication/replication_controller_test.go
View file @
a56bbbf8
...
@@ -893,6 +893,87 @@ func TestOverlappingRCs(t *testing.T) {
...
@@ -893,6 +893,87 @@ func TestOverlappingRCs(t *testing.T) {
}
}
}
}
func
TestDeletionTimestamp
(
t
*
testing
.
T
)
{
c
:=
clientset
.
NewForConfigOrDie
(
&
client
.
Config
{
Host
:
""
,
ContentConfig
:
client
.
ContentConfig
{
GroupVersion
:
testapi
.
Default
.
GroupVersion
()}})
manager
:=
NewReplicationManager
(
c
,
controller
.
NoResyncPeriodFunc
,
10
,
0
)
manager
.
podStoreSynced
=
alwaysReady
controllerSpec
:=
newReplicationController
(
1
)
manager
.
rcStore
.
Store
.
Add
(
controllerSpec
)
rcKey
,
err
:=
controller
.
KeyFunc
(
controllerSpec
)
if
err
!=
nil
{
t
.
Errorf
(
"Couldn't get key for object %+v: %v"
,
controllerSpec
,
err
)
}
pod
:=
newPodList
(
nil
,
1
,
api
.
PodPending
,
controllerSpec
)
.
Items
[
0
]
pod
.
DeletionTimestamp
=
&
unversioned
.
Time
{
time
.
Now
()}
manager
.
expectations
.
SetExpectations
(
rcKey
,
0
,
1
)
// A pod added with a deletion timestamp should decrement deletions, not creations.
manager
.
addPod
(
&
pod
)
queueRC
,
_
:=
manager
.
queue
.
Get
()
if
queueRC
!=
rcKey
{
t
.
Fatalf
(
"Expected to find key %v in queue, found %v"
,
rcKey
,
queueRC
)
}
manager
.
queue
.
Done
(
rcKey
)
podExp
,
exists
,
err
:=
manager
.
expectations
.
GetExpectations
(
rcKey
)
if
!
exists
||
err
!=
nil
||
!
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
// An update from no deletion timestamp to having one should be treated
// as a deletion.
oldPod
:=
newPodList
(
nil
,
1
,
api
.
PodPending
,
controllerSpec
)
.
Items
[
0
]
manager
.
expectations
.
SetExpectations
(
rcKey
,
0
,
1
)
manager
.
updatePod
(
&
oldPod
,
&
pod
)
queueRC
,
_
=
manager
.
queue
.
Get
()
if
queueRC
!=
rcKey
{
t
.
Fatalf
(
"Expected to find key %v in queue, found %v"
,
rcKey
,
queueRC
)
}
manager
.
queue
.
Done
(
rcKey
)
podExp
,
exists
,
err
=
manager
.
expectations
.
GetExpectations
(
rcKey
)
if
!
exists
||
err
!=
nil
||
!
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
// An update to the pod (including an update to the deletion timestamp)
// should not be counted as a second delete.
manager
.
expectations
.
SetExpectations
(
rcKey
,
0
,
1
)
oldPod
.
DeletionTimestamp
=
&
unversioned
.
Time
{
time
.
Now
()}
manager
.
updatePod
(
&
oldPod
,
&
pod
)
podExp
,
exists
,
err
=
manager
.
expectations
.
GetExpectations
(
rcKey
)
if
!
exists
||
err
!=
nil
||
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
// A pod with a non-nil deletion timestamp should also be ignored by the
// delete handler, because it's already been counted in the update.
manager
.
deletePod
(
&
pod
)
podExp
,
exists
,
err
=
manager
.
expectations
.
GetExpectations
(
rcKey
)
if
!
exists
||
err
!=
nil
||
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
// A pod with a nil timestamp should be counted as a deletion.
pod
.
DeletionTimestamp
=
nil
manager
.
deletePod
(
&
pod
)
queueRC
,
_
=
manager
.
queue
.
Get
()
if
queueRC
!=
rcKey
{
t
.
Fatalf
(
"Expected to find key %v in queue, found %v"
,
rcKey
,
queueRC
)
}
manager
.
queue
.
Done
(
rcKey
)
podExp
,
exists
,
err
=
manager
.
expectations
.
GetExpectations
(
rcKey
)
if
!
exists
||
err
!=
nil
||
!
podExp
.
Fulfilled
()
{
t
.
Fatalf
(
"Wrong expectations %+v"
,
podExp
)
}
}
func
BenchmarkGetPodControllerMultiNS
(
b
*
testing
.
B
)
{
func
BenchmarkGetPodControllerMultiNS
(
b
*
testing
.
B
)
{
client
:=
clientset
.
NewForConfigOrDie
(
&
client
.
Config
{
Host
:
""
,
ContentConfig
:
client
.
ContentConfig
{
GroupVersion
:
testapi
.
Default
.
GroupVersion
()}})
client
:=
clientset
.
NewForConfigOrDie
(
&
client
.
Config
{
Host
:
""
,
ContentConfig
:
client
.
ContentConfig
{
GroupVersion
:
testapi
.
Default
.
GroupVersion
()}})
manager
:=
NewReplicationManager
(
client
,
controller
.
NoResyncPeriodFunc
,
BurstReplicas
,
0
)
manager
:=
NewReplicationManager
(
client
,
controller
.
NoResyncPeriodFunc
,
BurstReplicas
,
0
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment