@@ -17,6 +17,7 @@ limitations under the License.
1717package namespacecontroller
1818
1919import (
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
4344func 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
159197func 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
294351func deleteEvents (kubeClient client.Interface , ns string ) error {
0 commit comments