diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index f79468b811e..66a7ba8a9f0 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -395,7 +395,7 @@ containers: glog.Fatalf("%s FAILED: mirror pod has not been created or is not running: %v", desc, err) } // Delete the mirror pod, and wait for it to be recreated. - c.Pods(namespace).Delete(podName) + c.Pods(namespace).Delete(podName, nil) if err = wait.Poll(time.Second, time.Minute*1, podRunning(c, namespace, podName)); err != nil { glog.Fatalf("%s FAILED: mirror pod has not been re-created or is not running: %v", desc, err) @@ -928,7 +928,7 @@ func runSchedulerNoPhantomPodsTest(client *client.Client) { // Delete a pod to free up room. glog.Infof("Deleting pod %v", bar.Name) - err = client.Pods(api.NamespaceDefault).Delete(bar.Name) + err = client.Pods(api.NamespaceDefault).Delete(bar.Name, nil) if err != nil { glog.Fatalf("FAILED: couldn't delete pod %q: %v", bar.Name, err) } diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 0a07a3aab44..346fc8639ff 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -350,6 +350,7 @@ _kubectl_delete() two_word_flags+=("-f") flags_with_completion+=("-f") flags_completion+=("_filedir '@(json|yaml|yml)'") + flags+=("--grace-period=") flags+=("--help") flags+=("-h") flags+=("--selector=") @@ -564,6 +565,7 @@ _kubectl_stop() two_word_flags+=("-f") flags_with_completion+=("-f") flags_completion+=("_filedir '@(json|yaml|yml)'") + flags+=("--grace-period=") flags+=("--help") flags+=("-h") flags+=("--selector=") diff --git a/docs/kubectl_delete.md b/docs/kubectl_delete.md index eb36fe0684f..57c454a878a 100644 --- a/docs/kubectl_delete.md +++ b/docs/kubectl_delete.md @@ -45,6 +45,7 @@ $ kubectl delete pods --all --all=false: [-all] to select all the specified resources --cascade=true: If true, cascade the delete resources managed by this resource (e.g. Pods created by a ReplicationController). Default true. -f, --filename=[]: Filename, directory, or URL to a file containing the resource to delete + --grace-period=-1: Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. -h, --help=false: help for delete -l, --selector="": Selector (label query) to filter on ``` @@ -82,4 +83,4 @@ $ kubectl delete pods --all ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-29 15:25:11.031169223 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-30 14:53:47.856200003 +0000 UTC diff --git a/docs/kubectl_stop.md b/docs/kubectl_stop.md index a6f0ed61306..92eae9a9875 100644 --- a/docs/kubectl_stop.md +++ b/docs/kubectl_stop.md @@ -35,6 +35,7 @@ $ kubectl stop -f path/to/resources ``` --all=false: [-all] to select all the specified resources -f, --filename=[]: Filename, directory, or URL to file of resource(s) to be stopped + --grace-period=-1: Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. -h, --help=false: help for stop -l, --selector="": Selector (label query) to filter on ``` @@ -72,4 +73,4 @@ $ kubectl stop -f path/to/resources ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-29 15:25:11.033329682 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-30 14:53:47.85772498 +0000 UTC diff --git a/docs/man/man1/kubectl-delete.1 b/docs/man/man1/kubectl-delete.1 index 3542f199d78..57df45ecf7e 100644 --- a/docs/man/man1/kubectl-delete.1 +++ b/docs/man/man1/kubectl-delete.1 @@ -41,6 +41,10 @@ will be lost along with the rest of the resource. \fB\-f\fP, \fB\-\-filename\fP=[] Filename, directory, or URL to a file containing the resource to delete +.PP +\fB\-\-grace\-period\fP=\-1 + Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. + .PP \fB\-h\fP, \fB\-\-help\fP=false help for delete diff --git a/docs/man/man1/kubectl-stop.1 b/docs/man/man1/kubectl-stop.1 index 029474cce06..060f2eaf211 100644 --- a/docs/man/man1/kubectl-stop.1 +++ b/docs/man/man1/kubectl-stop.1 @@ -29,6 +29,10 @@ If the resource is resizable it will be resized to 0 before deletion. \fB\-f\fP, \fB\-\-filename\fP=[] Filename, directory, or URL to file of resource(s) to be stopped +.PP +\fB\-\-grace\-period\fP=\-1 + Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. + .PP \fB\-h\fP, \fB\-\-help\fP=false help for stop diff --git a/pkg/api/conversion.go b/pkg/api/conversion.go index bd33db677d1..95f17791433 100644 --- a/pkg/api/conversion.go +++ b/pkg/api/conversion.go @@ -137,6 +137,10 @@ func init() { if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { return err } + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = in.DNSPolicy out.Version = "v1beta2" return nil @@ -151,6 +155,10 @@ func init() { if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { return err } + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = in.DNSPolicy return nil }, diff --git a/pkg/api/types.go b/pkg/api/types.go index 15ce2042ae7..ef7aa616895 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -799,6 +799,13 @@ type PodSpec struct { // Required: there must be at least one container in a pod. Containers []Container `json:"containers"` RestartPolicy RestartPolicy `json:"restartPolicy,omitempty"` + // Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. + // Value must be non-negative integer. The value zero indicates delete immediately. + // If this value is nil, the default grace period will be used instead. + // The grace period is the duration in seconds after the processes running in the pod are sent + // a termination signal and the time when the processes are forcibly halted with a kill signal). + // Set this value longer than the expected cleanup time for your process. + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` // Required: Set DNS policy. DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty"` // NodeSelector is a selector which must be true for the pod to fit on a node @@ -1643,10 +1650,11 @@ type ContainerManifest struct { // TODO: UUID on Manifest is deprecated in the future once we are done // with the API refactoring. It is required for now to determine the instance // of a Pod. - UUID types.UID `json:"uuid,omitempty"` - Volumes []Volume `json:"volumes"` - Containers []Container `json:"containers"` - RestartPolicy RestartPolicy `json:"restartPolicy,omitempty"` + UUID types.UID `json:"uuid,omitempty"` + Volumes []Volume `json:"volumes"` + Containers []Container `json:"containers"` + RestartPolicy RestartPolicy `json:"restartPolicy,omitempty"` + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` // Required: Set DNS policy. DNSPolicy DNSPolicy `json:"dnsPolicy"` HostNetwork bool `json:"hostNetwork,omitempty"` diff --git a/pkg/api/v1/conversion.go b/pkg/api/v1/conversion.go index 6cb2a39356c..a5d0e9405a9 100644 --- a/pkg/api/v1/conversion.go +++ b/pkg/api/v1/conversion.go @@ -1829,6 +1829,10 @@ func init() { } } out.RestartPolicy = newer.RestartPolicy(in.RestartPolicy) + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = newer.DNSPolicy(in.DNSPolicy) if in.NodeSelector != nil { out.NodeSelector = make(map[string]string) @@ -1858,6 +1862,10 @@ func init() { } } out.RestartPolicy = RestartPolicy(in.RestartPolicy) + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = DNSPolicy(in.DNSPolicy) if in.NodeSelector != nil { out.NodeSelector = make(map[string]string) diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 525514777a1..898418c10a8 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -797,6 +797,13 @@ type PodSpec struct { // Required: there must be at least one container in a pod. Containers []Container `json:"containers" description:"list of containers belonging to the pod; cannot be updated; containers cannot currently be added or removed; there must be at least one container in a Pod" patchStrategy:"merge" patchMergeKey:"name"` RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" description:"restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"` + // Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. + // Value must be non-negative integer. The value zero indicates delete immediately. + // If this value is nil, the default grace period will be used instead. + // The grace period is the duration in seconds after the processes running in the pod are sent + // a termination signal and the time when the processes are forcibly halted with a kill signal). + // Set this value longer than the expected cleanup time for your process. + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" description:"optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal); set this value longer than the expected cleanup time for your process"` // Optional: Set DNS policy. Defaults to "ClusterFirst" DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty" description:"DNS policy for containers within the pod; one of 'ClusterFirst' or 'Default'"` // NodeSelector is a selector which must be true for the pod to fit on a node diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index 5817e7a40fb..e194bccb280 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -686,6 +686,10 @@ func init() { if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { return err } + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = DNSPolicy(in.DNSPolicy) out.Version = "v1beta2" out.HostNetwork = in.HostNetwork @@ -701,6 +705,10 @@ func init() { if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { return err } + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = newer.DNSPolicy(in.DNSPolicy) out.HostNetwork = in.HostNetwork return nil diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 52ab9fb31b4..4efa13ede21 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -61,6 +61,13 @@ type ContainerManifest struct { Volumes []Volume `json:"volumes" description:"list of volumes that can be mounted by containers belonging to the pod"` Containers []Container `json:"containers" description:"list of containers belonging to the pod; containers cannot currently be added or removed"` RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" description:"restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"` + // Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. + // Value must be non-negative integer. The value zero indicates delete immediately. + // If this value is nil, the default grace period will be used instead. + // The grace period is the duration in seconds after the processes running in the pod are sent + // a termination signal and the time when the processes are forcibly halted with a kill signal). + // Set this value longer than the expected cleanup time for your process. + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" description:"optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal); set this value longer than the expected cleanup time for your process"` // Optional: Set DNS policy. Defaults to "ClusterFirst" DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty" description:"DNS policy for containers within the pod; one of 'ClusterFirst' or 'Default'"` // Uses the host's network namespace. If this option is set, the ports that will be @@ -1447,6 +1454,7 @@ type PodSpec struct { // Required: there must be at least one container in a pod. Containers []Container `json:"containers" description:"list of containers belonging to the pod; containers cannot currently be added or removed; there must be at least one container in a Pod"` RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" description:"restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"` + // TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` // Optional: Set DNS policy. Defaults to "ClusterFirst" DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty" description:"DNS policy for containers within the pod; one of 'ClusterFirst' or 'Default'"` // NodeSelector is a selector which must be true for the pod to fit on a node diff --git a/pkg/api/v1beta2/conversion.go b/pkg/api/v1beta2/conversion.go index d07390a0b5b..1e6bad42b14 100644 --- a/pkg/api/v1beta2/conversion.go +++ b/pkg/api/v1beta2/conversion.go @@ -466,6 +466,10 @@ func init() { if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { return err } + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = DNSPolicy(in.DNSPolicy) out.Version = "v1beta2" out.HostNetwork = in.HostNetwork @@ -481,6 +485,10 @@ func init() { if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { return err } + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = newer.DNSPolicy(in.DNSPolicy) out.HostNetwork = in.HostNetwork return nil diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index 5c822d17d30..76954e96b78 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -1485,6 +1485,13 @@ type ContainerManifest struct { Volumes []Volume `json:"volumes" description:"list of volumes that can be mounted by containers belonging to the pod"` Containers []Container `json:"containers" description:"list of containers belonging to the pod; cannot be updated; containers cannot currently be added or removed"` RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" description:"restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"` + // Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. + // Value must be non-negative integer. The value zero indicates delete immediately. + // If this value is nil, the default grace period will be used instead. + // The grace period is the duration in seconds after the processes running in the pod are sent + // a termination signal and the time when the processes are forcibly halted with a kill signal). + // Set this value longer than the expected cleanup time for your process. + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" description:"optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal); set this value longer than the expected cleanup time for your process"` // Optional: Set DNS policy. Defaults to "ClusterFirst" DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty" description:"DNS policy for containers within the pod; one of 'ClusterFirst' or 'Default'"` // Uses the host's network namespace. If this option is set, the ports that will be diff --git a/pkg/api/v1beta3/conversion.go b/pkg/api/v1beta3/conversion.go index 0f1a07a7094..aae4eb312df 100644 --- a/pkg/api/v1beta3/conversion.go +++ b/pkg/api/v1beta3/conversion.go @@ -1955,6 +1955,10 @@ func convert_v1beta3_PodSpec_To_api_PodSpec(in *PodSpec, out *newer.PodSpec, s c } } out.RestartPolicy = newer.RestartPolicy(in.RestartPolicy) + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = newer.DNSPolicy(in.DNSPolicy) if in.NodeSelector != nil { out.NodeSelector = make(map[string]string) @@ -1985,6 +1989,10 @@ func convert_api_PodSpec_To_v1beta3_PodSpec(in *newer.PodSpec, out *PodSpec, s c } } out.RestartPolicy = RestartPolicy(in.RestartPolicy) + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } out.DNSPolicy = DNSPolicy(in.DNSPolicy) if in.NodeSelector != nil { out.NodeSelector = make(map[string]string) diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 48fc3d4cb21..7545d33987b 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -797,6 +797,13 @@ type PodSpec struct { // Required: there must be at least one container in a pod. Containers []Container `json:"containers" description:"list of containers belonging to the pod; cannot be updated; containers cannot currently be added or removed; there must be at least one container in a Pod" patchStrategy:"merge" patchMergeKey:"name"` RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" description:"restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"` + // Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. + // Value must be non-negative integer. The value zero indicates delete immediately. + // If this value is nil, the default grace period will be used instead. + // The grace period is the duration in seconds after the processes running in the pod are sent + // a termination signal and the time when the processes are forcibly halted with a kill signal). + // Set this value longer than the expected cleanup time for your process. + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" description:"optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal); set this value longer than the expected cleanup time for your process"` // Optional: Set DNS policy. Defaults to "ClusterFirst" DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty" description:"DNS policy for containers within the pod; one of 'ClusterFirst' or 'Default'"` // NodeSelector is a selector which must be true for the pod to fit on a node diff --git a/pkg/client/pods.go b/pkg/client/pods.go index b61c4ed4f0e..a31ad91696b 100644 --- a/pkg/client/pods.go +++ b/pkg/client/pods.go @@ -32,7 +32,7 @@ type PodsNamespacer interface { type PodInterface interface { List(label labels.Selector, field fields.Selector) (*api.PodList, error) Get(name string) (*api.Pod, error) - Delete(name string) error + Delete(name string, options *api.DeleteOptions) error Create(pod *api.Pod) (*api.Pod, error) Update(pod *api.Pod) (*api.Pod, error) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) @@ -69,8 +69,16 @@ func (c *pods) Get(name string) (result *api.Pod, err error) { } // Delete takes the name of the pod, and returns an error if one occurs -func (c *pods) Delete(name string) error { - return c.r.Delete().Namespace(c.ns).Resource("pods").Name(name).Do().Error() +func (c *pods) Delete(name string, options *api.DeleteOptions) error { + // TODO: to make this reusable in other client libraries + if options == nil { + return c.r.Delete().Namespace(c.ns).Resource("pods").Name(name).Do().Error() + } + body, err := api.Scheme.EncodeToVersion(options, c.r.APIVersion()) + if err != nil { + return err + } + return c.r.Delete().Namespace(c.ns).Resource("pods").Name(name).Body(body).Do().Error() } // Create takes the representation of a pod. Returns the server's representation of the pod, and an error, if it occurs. diff --git a/pkg/client/pods_test.go b/pkg/client/pods_test.go index 37a68e6c88b..5d6280f206c 100644 --- a/pkg/client/pods_test.go +++ b/pkg/client/pods_test.go @@ -136,7 +136,7 @@ func TestDeletePod(t *testing.T) { Request: testRequest{Method: "DELETE", Path: testapi.ResourcePath("pods", ns, "foo"), Query: buildQueryValues(ns, nil)}, Response: Response{StatusCode: 200}, } - err := c.Setup().Pods(ns).Delete("foo") + err := c.Setup().Pods(ns).Delete("foo", nil) c.Validate(t, nil, err) } diff --git a/pkg/client/testclient/fake_pods.go b/pkg/client/testclient/fake_pods.go index 9cba18ca77f..eb4258bc3a2 100644 --- a/pkg/client/testclient/fake_pods.go +++ b/pkg/client/testclient/fake_pods.go @@ -40,7 +40,7 @@ func (c *FakePods) Get(name string) (*api.Pod, error) { return obj.(*api.Pod), err } -func (c *FakePods) Delete(name string) error { +func (c *FakePods) Delete(name string, options *api.DeleteOptions) error { _, err := c.Fake.Invokes(FakeAction{Action: "delete-pod", Value: name}, &api.Pod{}) return err } diff --git a/pkg/cloudprovider/nodecontroller/nodecontroller.go b/pkg/cloudprovider/nodecontroller/nodecontroller.go index f29e9e54f6d..9430f1881a9 100644 --- a/pkg/cloudprovider/nodecontroller/nodecontroller.go +++ b/pkg/cloudprovider/nodecontroller/nodecontroller.go @@ -593,7 +593,7 @@ func (nc *NodeController) deletePods(nodeID string) error { continue } glog.V(2).Infof("Delete pod %v", pod.Name) - if err := nc.kubeClient.Pods(pod.Namespace).Delete(pod.Name); err != nil { + if err := nc.kubeClient.Pods(pod.Namespace).Delete(pod.Name, nil); err != nil { glog.Errorf("Error deleting pod %v: %v", pod.Name, err) } } diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index 7947cda8093..a81cda1a1d2 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -233,7 +233,7 @@ func (r RealPodControl) createReplica(namespace string, controller *api.Replicat } func (r RealPodControl) deletePod(namespace, podID string) error { - return r.kubeClient.Pods(namespace).Delete(podID) + return r.kubeClient.Pods(namespace).Delete(podID, nil) } // activePods type allows custom sorting of pods so an rc can pick the best ones to delete. diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 9205bfdc703..f45915dd34b 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -22,6 +22,7 @@ import ( "github.com/spf13/cobra" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" @@ -73,6 +74,7 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") cmd.Flags().Bool("all", false, "[-all] to select all the specified resources") cmd.Flags().Bool("cascade", true, "If true, cascade the delete resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.") + cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.") return cmd } @@ -98,12 +100,12 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str // By default use a reaper to delete all related resources. if cmdutil.GetFlagBool(cmd, "cascade") { - return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade")) + return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), cmdutil.GetFlagInt(cmd, "grace-period")) } return DeleteResult(r, out) } -func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefaultDelete bool) error { +func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefaultDelete bool, gracePeriod int) error { found := 0 err := r.IgnoreErrors(errors.IsNotFound).Visit(func(info *resource.Info) error { found++ @@ -115,7 +117,11 @@ func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefault } return err } - if _, err := reaper.Stop(info.Namespace, info.Name); err != nil { + var options *api.DeleteOptions + if gracePeriod >= 0 { + options = api.NewDeleteOptions(int64(gracePeriod)) + } + if _, err := reaper.Stop(info.Namespace, info.Name, options); err != nil { return err } fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name) diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index dea0d0469a8..f5397cd73be 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -345,7 +345,7 @@ func addDeploymentKeyToReplicationController(oldRc *api.ReplicationController, c for ix := range podList.Items { pod := &podList.Items[ix] if value, found := pod.Labels[deploymentKey]; !found || value != oldHash { - if err := client.Pods(namespace).Delete(pod.Name); err != nil { + if err := client.Pods(namespace).Delete(pod.Name, nil); err != nil { return err } } diff --git a/pkg/kubectl/cmd/stop.go b/pkg/kubectl/cmd/stop.go index 41a25dc92c3..aceec128d54 100644 --- a/pkg/kubectl/cmd/stop.go +++ b/pkg/kubectl/cmd/stop.go @@ -61,6 +61,7 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command { kubectl.AddJsonFilenameFlag(cmd, &flags.Filenames, usage) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") cmd.Flags().Bool("all", false, "[-all] to select all the specified resources") + cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.") return cmd } @@ -82,5 +83,5 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, filenames ut if r.Err() != nil { return r.Err() } - return ReapResult(r, f, out, false) + return ReapResult(r, f, out, false, cmdutil.GetFlagInt(cmd, "grace-period")) } diff --git a/pkg/kubectl/stop.go b/pkg/kubectl/stop.go index ebdc95db64d..9b7e52060f9 100644 --- a/pkg/kubectl/stop.go +++ b/pkg/kubectl/stop.go @@ -20,6 +20,7 @@ import ( "fmt" "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" ) @@ -32,7 +33,7 @@ const ( // A Reaper handles terminating an object as gracefully as possible. type Reaper interface { - Stop(namespace, name string) (string, error) + Stop(namespace, name string, gracePeriod *api.DeleteOptions) (string, error) } type NoSuchReaperError struct { @@ -76,7 +77,7 @@ type objInterface interface { Get(name string) (meta.Interface, error) } -func (reaper *ReplicationControllerReaper) Stop(namespace, name string) (string, error) { +func (reaper *ReplicationControllerReaper) Stop(namespace, name string, gracePeriod *api.DeleteOptions) (string, error) { rc := reaper.ReplicationControllers(namespace) resizer, err := ResizerFor("ReplicationController", NewResizerClient(*reaper)) if err != nil { @@ -91,19 +92,20 @@ func (reaper *ReplicationControllerReaper) Stop(namespace, name string) (string, return fmt.Sprintf("%s stopped", name), nil } -func (reaper *PodReaper) Stop(namespace, name string) (string, error) { +func (reaper *PodReaper) Stop(namespace, name string, gracePeriod *api.DeleteOptions) (string, error) { pods := reaper.Pods(namespace) _, err := pods.Get(name) if err != nil { return "", err } - if err := pods.Delete(name); err != nil { + if err := pods.Delete(name, gracePeriod); err != nil { return "", err } + return fmt.Sprintf("%s stopped", name), nil } -func (reaper *ServiceReaper) Stop(namespace, name string) (string, error) { +func (reaper *ServiceReaper) Stop(namespace, name string, gracePeriod *api.DeleteOptions) (string, error) { services := reaper.Services(namespace) _, err := services.Get(name) if err != nil { diff --git a/pkg/kubectl/stop_test.go b/pkg/kubectl/stop_test.go index 40e5eb7a0d8..b15ae49914c 100644 --- a/pkg/kubectl/stop_test.go +++ b/pkg/kubectl/stop_test.go @@ -34,7 +34,7 @@ func TestReplicationControllerStop(t *testing.T) { }) reaper := ReplicationControllerReaper{fake, time.Millisecond, time.Millisecond} name := "foo" - s, err := reaper.Stop("default", name) + s, err := reaper.Stop("default", name, nil) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -142,7 +142,7 @@ func TestSimpleStop(t *testing.T) { if err != nil { t.Errorf("unexpected error: %v (%s)", err, test.test) } - s, err := reaper.Stop("default", "foo") + s, err := reaper.Stop("default", "foo", nil) if err != nil && !test.expectError { t.Errorf("unexpected error: %v (%s)", err, test.test) } diff --git a/pkg/kubelet/mirror_client.go b/pkg/kubelet/mirror_client.go index caa12de3007..981b92552c1 100644 --- a/pkg/kubelet/mirror_client.go +++ b/pkg/kubelet/mirror_client.go @@ -64,7 +64,7 @@ func (mc *basicMirrorClient) DeleteMirrorPod(podFullName string) error { return err } glog.V(4).Infof("Deleting a mirror pod %q", podFullName) - if err := mc.apiserverClient.Pods(namespace).Delete(name); err != nil { + if err := mc.apiserverClient.Pods(namespace).Delete(name, nil); err != nil { glog.Errorf("Failed deleting a mirror pod %q: %v", podFullName, err) } return nil diff --git a/pkg/namespace/namespace_controller.go b/pkg/namespace/namespace_controller.go index c24950089cd..9fda9e9acb8 100644 --- a/pkg/namespace/namespace_controller.go +++ b/pkg/namespace/namespace_controller.go @@ -251,7 +251,7 @@ func deletePods(kubeClient client.Interface, ns string) error { return err } for i := range items.Items { - err := kubeClient.Pods(ns).Delete(items.Items[i].Name) + err := kubeClient.Pods(ns).Delete(items.Items[i].Name, nil) if err != nil { return err } diff --git a/test/e2e/es_cluster_logging.go b/test/e2e/es_cluster_logging.go index 11fff3d4785..89e8a315b9d 100644 --- a/test/e2e/es_cluster_logging.go +++ b/test/e2e/es_cluster_logging.go @@ -213,7 +213,7 @@ func ClusterLevelLoggingWithElasticsearch(c *client.Client) { // Cleanup the pods when we are done. defer func() { for _, pod := range podNames { - if err = c.Pods(ns).Delete(pod); err != nil { + if err = c.Pods(ns).Delete(pod, nil); err != nil { Logf("Failed to delete pod %s: %v", pod, err) } } diff --git a/test/e2e/events.go b/test/e2e/events.go index ff7e6f6bcbc..62cc4746cc7 100644 --- a/test/e2e/events.go +++ b/test/e2e/events.go @@ -69,7 +69,7 @@ var _ = Describe("Events", func() { By("submitting the pod to kubernetes") defer func() { By("deleting the pod") - podClient.Delete(pod.Name) + podClient.Delete(pod.Name, nil) }() if _, err := podClient.Create(pod); err != nil { Failf("Failed to create pod: %v", err) diff --git a/test/e2e/networking.go b/test/e2e/networking.go index 0c6b45ac880..2334a4d4c1b 100644 --- a/test/e2e/networking.go +++ b/test/e2e/networking.go @@ -188,7 +188,7 @@ var _ = Describe("Networking", func() { defer GinkgoRecover() By("Cleaning up the webserver pods") for _, podName := range podNames { - if err = c.Pods(namespace.Name).Delete(podName); err != nil { + if err = c.Pods(namespace.Name).Delete(podName, nil); err != nil { Logf("Failed to delete pod %s: %v", podName, err) } } diff --git a/test/e2e/pd.go b/test/e2e/pd.go index cdef0e226f2..df777b42955 100644 --- a/test/e2e/pd.go +++ b/test/e2e/pd.go @@ -82,8 +82,8 @@ var _ = Describe("PD", func() { By("cleaning up PD-RW test environment") // Teardown pods, PD. Ignore errors. // Teardown should do nothing unless test failed. - podClient.Delete(host0Pod.Name) - podClient.Delete(host1Pod.Name) + podClient.Delete(host0Pod.Name, nil) + podClient.Delete(host1Pod.Name, nil) detachPD(host0Name, diskName) detachPD(host1Name, diskName) deletePD(diskName) @@ -96,7 +96,7 @@ var _ = Describe("PD", func() { expectNoError(waitForPodRunning(c, host0Pod.Name)) By("deleting host0Pod") - expectNoError(podClient.Delete(host0Pod.Name), "Failed to delete host0Pod") + expectNoError(podClient.Delete(host0Pod.Name, nil), "Failed to delete host0Pod") By("submitting host1Pod to kubernetes") _, err = podClient.Create(host1Pod) @@ -105,7 +105,7 @@ var _ = Describe("PD", func() { expectNoError(waitForPodRunning(c, host1Pod.Name)) By("deleting host1Pod") - expectNoError(podClient.Delete(host1Pod.Name), "Failed to delete host1Pod") + expectNoError(podClient.Delete(host1Pod.Name, nil), "Failed to delete host1Pod") By(fmt.Sprintf("deleting PD %q", diskName)) for start := time.Now(); time.Since(start) < 180*time.Second; time.Sleep(5 * time.Second) { @@ -142,9 +142,9 @@ var _ = Describe("PD", func() { By("cleaning up PD-RO test environment") // Teardown pods, PD. Ignore errors. // Teardown should do nothing unless test failed. - podClient.Delete(rwPod.Name) - podClient.Delete(host0ROPod.Name) - podClient.Delete(host1ROPod.Name) + podClient.Delete(rwPod.Name, nil) + podClient.Delete(host0ROPod.Name, nil) + podClient.Delete(host1ROPod.Name, nil) detachPD(host0Name, diskName) detachPD(host1Name, diskName) @@ -155,7 +155,7 @@ var _ = Describe("PD", func() { _, err = podClient.Create(rwPod) expectNoError(err, "Failed to create rwPod") expectNoError(waitForPodRunning(c, rwPod.Name)) - expectNoError(podClient.Delete(rwPod.Name), "Failed to delete host0Pod") + expectNoError(podClient.Delete(rwPod.Name, nil), "Failed to delete host0Pod") By("submitting host0ROPod to kubernetes") _, err = podClient.Create(host0ROPod) @@ -170,10 +170,10 @@ var _ = Describe("PD", func() { expectNoError(waitForPodRunning(c, host1ROPod.Name)) By("deleting host0ROPod") - expectNoError(podClient.Delete(host0ROPod.Name), "Failed to delete host0ROPod") + expectNoError(podClient.Delete(host0ROPod.Name, nil), "Failed to delete host0ROPod") By("deleting host1ROPod") - expectNoError(podClient.Delete(host1ROPod.Name), "Failed to delete host1ROPod") + expectNoError(podClient.Delete(host1ROPod.Name, nil), "Failed to delete host1ROPod") By(fmt.Sprintf("deleting PD %q", diskName)) for start := time.Now(); time.Since(start) < 180*time.Second; time.Sleep(5 * time.Second) { diff --git a/test/e2e/pods.go b/test/e2e/pods.go index a4e930fca99..c35ccc2c208 100644 --- a/test/e2e/pods.go +++ b/test/e2e/pods.go @@ -44,7 +44,7 @@ func runLivenessTest(c *client.Client, podDescr *api.Pod) { // At the end of the test, clean up by removing the pod. defer func() { By("deleting the pod") - c.Pods(ns).Delete(podDescr.Name) + c.Pods(ns).Delete(podDescr.Name, nil) }() // Wait until the pod is not pending. (Here we need to check for something other than @@ -87,7 +87,7 @@ func testHostIP(c *client.Client, pod *api.Pod) { ns := "e2e-test-" + string(util.NewUUID()) podClient := c.Pods(ns) By("creating pod") - defer podClient.Delete(pod.Name) + defer podClient.Delete(pod.Name, nil) _, err := podClient.Create(pod) if err != nil { Fail(fmt.Sprintf("Failed to create pod: %v", err)) @@ -191,7 +191,7 @@ var _ = Describe("Pods", func() { // We call defer here in case there is a problem with // the test so we can ensure that we clean up after // ourselves - defer podClient.Delete(pod.Name) + defer podClient.Delete(pod.Name, nil) _, err = podClient.Create(pod) if err != nil { Fail(fmt.Sprintf("Failed to create pod: %v", err)) @@ -215,7 +215,7 @@ var _ = Describe("Pods", func() { } By("deleting the pod") - podClient.Delete(pod.Name) + podClient.Delete(pod.Name, nil) pods, err = podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})), fields.Everything()) if err != nil { Fail(fmt.Sprintf("Failed to delete pod: %v", err)) @@ -278,7 +278,7 @@ var _ = Describe("Pods", func() { By("submitting the pod to kubernetes") defer func() { By("deleting the pod") - podClient.Delete(pod.Name) + podClient.Delete(pod.Name, nil) }() pod, err := podClient.Create(pod) if err != nil { @@ -342,7 +342,7 @@ var _ = Describe("Pods", func() { }, }, } - defer c.Pods(api.NamespaceDefault).Delete(serverPod.Name) + defer c.Pods(api.NamespaceDefault).Delete(serverPod.Name, nil) _, err := c.Pods(api.NamespaceDefault).Create(serverPod) if err != nil { Fail(fmt.Sprintf("Failed to create serverPod: %v", err)) diff --git a/test/e2e/rc.go b/test/e2e/rc.go index 624342a26db..d7c1f6c3c21 100644 --- a/test/e2e/rc.go +++ b/test/e2e/rc.go @@ -103,7 +103,7 @@ func ServeImageOrFail(c *client.Client, test string, image string) { if err != nil { Logf("Failed to cleanup replication controller %v: %v.", controller.Name, err) } - if _, err = rcReaper.Stop(ns, controller.Name); err != nil { + if _, err = rcReaper.Stop(ns, controller.Name, nil); err != nil { Logf("Failed to stop replication controller %v: %v.", controller.Name, err) } }() diff --git a/test/e2e/service.go b/test/e2e/service.go index 9c9fa5f0231..a7fb87d44cf 100644 --- a/test/e2e/service.go +++ b/test/e2e/service.go @@ -119,7 +119,7 @@ var _ = Describe("Services", func() { defer func() { By("deleting the pod") defer GinkgoRecover() - podClient.Delete(pod.Name) + podClient.Delete(pod.Name, nil) }() if _, err := podClient.Create(pod); err != nil { Failf("Failed to create %s pod: %v", pod.Name, err) @@ -220,7 +220,7 @@ var _ = Describe("Services", func() { var names []string defer func() { for _, name := range names { - err := c.Pods(ns).Delete(name) + err := c.Pods(ns).Delete(name, nil) Expect(err).NotTo(HaveOccurred()) } }() @@ -237,13 +237,13 @@ var _ = Describe("Services", func() { validateEndpointsOrFail(c, ns, serviceName, expectedPort, names) - err = c.Pods(ns).Delete(name1) + err = c.Pods(ns).Delete(name1, nil) Expect(err).NotTo(HaveOccurred()) names = []string{name2} validateEndpointsOrFail(c, ns, serviceName, expectedPort, names) - err = c.Pods(ns).Delete(name2) + err = c.Pods(ns).Delete(name2, nil) Expect(err).NotTo(HaveOccurred()) names = []string{} @@ -323,7 +323,7 @@ var _ = Describe("Services", func() { defer func() { By("deleting pod " + pod.Name) defer GinkgoRecover() - podClient.Delete(pod.Name) + podClient.Delete(pod.Name, nil) }() if _, err := podClient.Create(pod); err != nil { Failf("Failed to create pod %s: %v", pod.Name, err) diff --git a/test/e2e/util.go b/test/e2e/util.go index 2788f63f681..4bc9183cb9b 100644 --- a/test/e2e/util.go +++ b/test/e2e/util.go @@ -363,7 +363,7 @@ func testContainerOutput(scenarioName string, c *client.Client, pod *api.Pod, ex func testContainerOutputInNamespace(scenarioName string, c *client.Client, pod *api.Pod, expectedOutput []string, ns string) { By(fmt.Sprintf("Creating a pod to test %v", scenarioName)) - defer c.Pods(ns).Delete(pod.Name) + defer c.Pods(ns).Delete(pod.Name, nil) if _, err := c.Pods(ns).Create(pod); err != nil { Failf("Failed to create pod: %v", err) } diff --git a/test/integration/scheduler_test.go b/test/integration/scheduler_test.go index 774a4e6a5dd..e243075805a 100644 --- a/test/integration/scheduler_test.go +++ b/test/integration/scheduler_test.go @@ -145,7 +145,7 @@ func DoTestUnschedulableNodes(t *testing.T, client *client.Client) { t.Errorf("Failed to schedule a pod: %v", err) } - err = client.Pods(api.NamespaceDefault).Delete(myPod.Name) + err = client.Pods(api.NamespaceDefault).Delete(myPod.Name, nil) if err != nil { t.Errorf("Failed to delete pod: %v", err) } diff --git a/test/integration/secret_test.go b/test/integration/secret_test.go index 92b229a4de7..fa5664f222d 100644 --- a/test/integration/secret_test.go +++ b/test/integration/secret_test.go @@ -39,7 +39,7 @@ func init() { } func deletePodOrErrorf(t *testing.T, c *client.Client, ns, name string) { - if err := c.Pods(ns).Delete(name); err != nil { + if err := c.Pods(ns).Delete(name, nil); err != nil { t.Errorf("unable to delete pod %v: %v", name, err) } } diff --git a/test/soak/serve_hostnames/serve_hostnames.go b/test/soak/serve_hostnames/serve_hostnames.go index db9f2c94a0f..74f82186cbc 100644 --- a/test/soak/serve_hostnames/serve_hostnames.go +++ b/test/soak/serve_hostnames/serve_hostnames.go @@ -206,7 +206,7 @@ func main() { // Make several attempts to delete the pods. for _, podName := range podNames { for start := time.Now(); time.Since(start) < deleteTimeout; time.Sleep(1 * time.Second) { - if err = c.Pods(ns).Delete(podName); err == nil { + if err = c.Pods(ns).Delete(podName, nil); err == nil { break } glog.Warningf("After %v failed to delete pod %s/%s: %v", time.Since(start), ns, podName, err)