Skip to content

Commit 057be1f

Browse files
committed
Merge pull request kubernetes#12406 from smarterclayton/namespace_estimate_delete
Namespace must estimate and requeue content deletions
2 parents f195a80 + 62b6ca1 commit 057be1f

File tree

2 files changed

+93
-26
lines changed

2 files changed

+93
-26
lines changed

pkg/controller/framework/controller.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ func (c *Controller) HasSynced() bool {
106106
return c.reflector.LastSyncResourceVersion() != ""
107107
}
108108

109+
// Requeue adds the provided object back into the queue if it does not already exist.
110+
func (c *Controller) Requeue(obj interface{}) error {
111+
return c.config.Queue.AddIfNotPresent(cache.Deltas{
112+
cache.Delta{
113+
Type: cache.Sync,
114+
Object: obj,
115+
},
116+
})
117+
}
118+
109119
// processLoop drains the work queue.
110120
// TODO: Consider doing the processing in parallel. This will require a little thought
111121
// to make sure that we don't end up processing the same object multiple times

pkg/controller/namespace/namespace_controller.go

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package namespacecontroller
1818

1919
import (
20+
"fmt"
2021
"time"
2122

2223
"k8s.io/kubernetes/pkg/api"
@@ -41,7 +42,8 @@ type NamespaceController struct {
4142

4243
// NewNamespaceController creates a new NamespaceController
4344
func NewNamespaceController(kubeClient client.Interface, resyncPeriod time.Duration) *NamespaceController {
44-
_, controller := framework.NewInformer(
45+
var controller *framework.Controller
46+
_, controller = framework.NewInformer(
4547
&cache.ListWatch{
4648
ListFunc: func() (runtime.Object, error) {
4749
return kubeClient.Namespaces().List(labels.Everything(), fields.Everything())
@@ -55,16 +57,41 @@ func NewNamespaceController(kubeClient client.Interface, resyncPeriod time.Durat
5557
framework.ResourceEventHandlerFuncs{
5658
AddFunc: func(obj interface{}) {
5759
namespace := obj.(*api.Namespace)
58-
err := syncNamespace(kubeClient, *namespace)
59-
if err != nil {
60-
glog.Error(err)
60+
if err := syncNamespace(kubeClient, *namespace); err != nil {
61+
if estimate, ok := err.(*contentRemainingError); ok {
62+
go func() {
63+
// Estimate is the aggregate total of TerminationGracePeriodSeconds, which defaults to 30s
64+
// for pods. However, most processes will terminate faster - within a few seconds, probably
65+
// with a peak within 5-10s. So this division is a heuristic that avoids waiting the full
66+
// duration when in many cases things complete more quickly. The extra second added is to
67+
// ensure we never wait 0 seconds.
68+
t := estimate.Estimate/2 + 1
69+
glog.V(4).Infof("Content remaining in namespace %s, waiting %d seconds", namespace.Name, t)
70+
time.Sleep(time.Duration(t) * time.Second)
71+
if err := controller.Requeue(namespace); err != nil {
72+
util.HandleError(err)
73+
}
74+
}()
75+
return
76+
}
77+
util.HandleError(err)
6178
}
6279
},
6380
UpdateFunc: func(oldObj, newObj interface{}) {
6481
namespace := newObj.(*api.Namespace)
65-
err := syncNamespace(kubeClient, *namespace)
66-
if err != nil {
67-
glog.Error(err)
82+
if err := syncNamespace(kubeClient, *namespace); err != nil {
83+
if estimate, ok := err.(*contentRemainingError); ok {
84+
go func() {
85+
t := estimate.Estimate/2 + 1
86+
glog.V(4).Infof("Content remaining in namespace %s, waiting %d seconds", namespace.Name, t)
87+
time.Sleep(time.Duration(t) * time.Second)
88+
if err := controller.Requeue(namespace); err != nil {
89+
util.HandleError(err)
90+
}
91+
}()
92+
return
93+
}
94+
util.HandleError(err)
6895
}
6996
},
7097
},
@@ -114,52 +141,64 @@ func finalize(kubeClient client.Interface, namespace api.Namespace) (*api.Namesp
114141
return kubeClient.Namespaces().Finalize(&namespaceFinalize)
115142
}
116143

117-
// deleteAllContent will delete all content known to the system in a namespace
118-
func deleteAllContent(kubeClient client.Interface, namespace string) (err error) {
144+
type contentRemainingError struct {
145+
Estimate int64
146+
}
147+
148+
func (e *contentRemainingError) Error() string {
149+
return fmt.Sprintf("some content remains in the namespace, estimate %d seconds before it is removed", e.Estimate)
150+
}
151+
152+
// deleteAllContent will delete all content known to the system in a namespace. It returns an estimate
153+
// of the time remaining before the remaining resources are deleted. If estimate > 0 not all resources
154+
// are guaranteed to be gone.
155+
func deleteAllContent(kubeClient client.Interface, namespace string, before util.Time) (estimate int64, err error) {
119156
err = deleteServiceAccounts(kubeClient, namespace)
120157
if err != nil {
121-
return err
158+
return estimate, err
122159
}
123160
err = deleteServices(kubeClient, namespace)
124161
if err != nil {
125-
return err
162+
return estimate, err
126163
}
127164
err = deleteReplicationControllers(kubeClient, namespace)
128165
if err != nil {
129-
return err
166+
return estimate, err
130167
}
131-
err = deletePods(kubeClient, namespace)
168+
estimate, err = deletePods(kubeClient, namespace, before)
132169
if err != nil {
133-
return err
170+
return estimate, err
134171
}
135172
err = deleteSecrets(kubeClient, namespace)
136173
if err != nil {
137-
return err
174+
return estimate, err
138175
}
139176
err = deletePersistentVolumeClaims(kubeClient, namespace)
140177
if err != nil {
141-
return err
178+
return estimate, err
142179
}
143180
err = deleteLimitRanges(kubeClient, namespace)
144181
if err != nil {
145-
return err
182+
return estimate, err
146183
}
147184
err = deleteResourceQuotas(kubeClient, namespace)
148185
if err != nil {
149-
return err
186+
return estimate, err
150187
}
151188
err = deleteEvents(kubeClient, namespace)
152189
if err != nil {
153-
return err
190+
return estimate, err
154191
}
155-
return nil
192+
193+
return estimate, nil
156194
}
157195

158196
// syncNamespace makes namespace life-cycle decisions
159197
func syncNamespace(kubeClient client.Interface, namespace api.Namespace) (err error) {
160198
if namespace.DeletionTimestamp == nil {
161199
return nil
162200
}
201+
glog.V(4).Infof("Syncing namespace %s", namespace.Name)
163202

164203
// if there is a deletion timestamp, and the status is not terminating, then update status
165204
if !namespace.DeletionTimestamp.IsZero() && namespace.Status.Phase != api.NamespaceTerminating {
@@ -185,10 +224,13 @@ func syncNamespace(kubeClient client.Interface, namespace api.Namespace) (err er
185224
}
186225

187226
// there may still be content for us to remove
188-
err = deleteAllContent(kubeClient, namespace.Name)
227+
estimate, err := deleteAllContent(kubeClient, namespace.Name, *namespace.DeletionTimestamp)
189228
if err != nil {
190229
return err
191230
}
231+
if estimate > 0 {
232+
return &contentRemainingError{estimate}
233+
}
192234

193235
// we have removed content, so mark it finalized by us
194236
result, err := finalize(kubeClient, namespace)
@@ -277,18 +319,33 @@ func deleteReplicationControllers(kubeClient client.Interface, ns string) error
277319
return nil
278320
}
279321

280-
func deletePods(kubeClient client.Interface, ns string) error {
322+
func deletePods(kubeClient client.Interface, ns string, before util.Time) (int64, error) {
281323
items, err := kubeClient.Pods(ns).List(labels.Everything(), fields.Everything())
282324
if err != nil {
283-
return err
325+
return 0, err
284326
}
327+
expired := util.Now().After(before.Time)
328+
var deleteOptions *api.DeleteOptions
329+
if expired {
330+
deleteOptions = api.NewDeleteOptions(0)
331+
}
332+
estimate := int64(0)
285333
for i := range items.Items {
286-
err := kubeClient.Pods(ns).Delete(items.Items[i].Name, nil)
334+
if items.Items[i].Spec.TerminationGracePeriodSeconds != nil {
335+
grace := *items.Items[i].Spec.TerminationGracePeriodSeconds
336+
if grace > estimate {
337+
estimate = grace
338+
}
339+
}
340+
err := kubeClient.Pods(ns).Delete(items.Items[i].Name, deleteOptions)
287341
if err != nil && !errors.IsNotFound(err) {
288-
return err
342+
return 0, err
289343
}
290344
}
291-
return nil
345+
if expired {
346+
estimate = 0
347+
}
348+
return estimate, nil
292349
}
293350

294351
func deleteEvents(kubeClient client.Interface, ns string) error {

0 commit comments

Comments
 (0)