Change:
What happened: There seems to be a regression after 1.13 release. A Cronjob with default settings does not clean up completed pods. The job themselves are getting deleted as expected, but the pods are left behind. This seems to be related to What you expected to happen: Expected the pods for now non-existent child jobs of a cronjob to be cleaned up. This is the way it used to work prior to 1.13/ How to reproduce it (as minimally and precisely as possible): run on this manifest: Wait, more than 3 minutes, observe, that while only 3 successful jobs are present, the pods from now deleted jobs are left behind and never get deleted. Anything else we need to know?: pointed out the following relevant places in the code base: deleting a job orphans pods by default and the cronjob controller doesn't do anything different than the default when deleting Environment: Kubernetes version (use ): 1.13.0 Cloud provider or hardware configuration: ubuntu VM in vSphere OS (e.g. from /etc/os-release): 16.04.5 LTS (Xenial Xerus) Kernel (e.g. ): 4.4.0-135-generic Install tools: kubeadm <!-- DO NOT EDIT BELOW THIS LINE --/kind bug
/sig apps
Should include a known issues in 1.13.0 as well /priority critical-urgent
Lemme see if I can easily reproduce this.
/milestone v1.13
Fixes are in (master) and (1.13).

Discussion:
} func (r realJobControl) DeleteJob(namespace string, name string) error { <del> return r.KubeClient.BatchV1().Jobs(namespace).Delete(name, nil) </del> <ins> background := metav1.DeletePropagationBackground return r.KubeClient.BatchV1().Jobs(namespace).Delete(name, &metav1.DeleteOptions{PropagationPolicy: &background}) </ins> } type fakeJobControl struct {

Expect(len(finishedJobs) == 1).To(BeTrue()) // Job should get deleted when the next job finishes the next minute <del> By("Ensuring this job does not exist anymore") err = waitForJobNotExist(f.ClientSet, f.Namespace.Name, finishedJobs[0]) </del> <ins> By("Ensuring this job and its pods does not exist anymore") err = waitForJobToDisappear(f.ClientSet, f.Namespace.Name, finishedJobs[0]) Expect(err).NotTo(HaveOccurred()) err = waitForJobsPodToDisappear(f.ClientSet, f.Namespace.Name, finishedJobs[0]) </ins> Expect(err).NotTo(HaveOccurred()) By("Ensuring there is 1 finished job by listing jobs explicitly")

}) } <del> // Wait for a job to not exist by listing jobs explicitly. func waitForJobNotExist(c clientset.Interface, ns string, targetJob *batchv1.Job) error { </del> <ins> // Wait for a job to disappear by listing them explicitly. func waitForJobToDisappear(c clientset.Interface, ns string, targetJob *batchv1.Job) error { </ins> return wait.Poll(framework.Poll, cronJobTimeout, func() (bool, error) { jobs, err := c.BatchV1().Jobs(ns).List(metav1.ListOptions{}) if err != nil {

}) } <ins> // Wait for a pod to disappear by listing them explicitly. func waitForJobsPodToDisappear(c clientset.Interface, ns string, targetJob *batchv1.Job) error { return wait.Poll(framework.Poll, cronJobTimeout, func() (bool, error) { options := metav1.ListOptions{LabelSelector: fmt.Sprintf("controller-uid=%s", targetJob.UID)} pods, err := c.CoreV1().Pods(ns).List(options) if err != nil { return false, err } return len(pods.Items) == 0, nil }) } </ins> // Wait for a job to be replaced with a new one. func waitForJobReplaced(c clientset.Interface, ns, previousJobName string) error { return wait.Poll(framework.Poll, cronJobTimeout, func() (bool, error) {