func newCondition(conditionType batch.JobConditionType, reason, message string) batch.JobCondition { return batch.JobCondition{ Type: conditionType, Status: api.ConditionTrue, LastProbeTime: unversioned.Now(), LastTransitionTime: unversioned.Now(), Reason: reason, Message: message, } }
func TestDoNotDeleteMirrorPods(t *testing.T) { staticPod := getTestPod() staticPod.Annotations = map[string]string{kubetypes.ConfigSourceAnnotationKey: "file"} mirrorPod := getTestPod() mirrorPod.UID = "mirror-12345678" mirrorPod.Annotations = map[string]string{ kubetypes.ConfigSourceAnnotationKey: "api", kubetypes.ConfigMirrorAnnotationKey: "mirror", } // Set the deletion timestamp. mirrorPod.DeletionTimestamp = new(unversioned.Time) client := fake.NewSimpleClientset(mirrorPod) m := newTestManager(client) m.podManager.AddPod(staticPod) m.podManager.AddPod(mirrorPod) // Verify setup. assert.True(t, kubepod.IsStaticPod(staticPod), "SetUp error: staticPod") assert.True(t, kubepod.IsMirrorPod(mirrorPod), "SetUp error: mirrorPod") assert.Equal(t, m.podManager.TranslatePodUID(mirrorPod.UID), staticPod.UID) status := getRandomPodStatus() now := unversioned.Now() status.StartTime = &now m.SetPodStatus(staticPod, status) m.testSyncBatch() // Expect not to see an delete action. verifyActions(t, m.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: unversioned.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: unversioned.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, }) }
func TestNamespaceStatusStrategy(t *testing.T) { ctx := api.NewDefaultContext() if StatusStrategy.NamespaceScoped() { t.Errorf("Namespaces should not be namespace scoped") } if StatusStrategy.AllowCreateOnUpdate() { t.Errorf("Namespaces should not allow create on update") } now := unversioned.Now() oldNamespace := &api.Namespace{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10", DeletionTimestamp: &now}, Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"kubernetes"}}, Status: api.NamespaceStatus{Phase: api.NamespaceActive}, } namespace := &api.Namespace{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "9", DeletionTimestamp: &now}, Status: api.NamespaceStatus{Phase: api.NamespaceTerminating}, } StatusStrategy.PrepareForUpdate(namespace, oldNamespace) if namespace.Status.Phase != api.NamespaceTerminating { t.Errorf("Namespace status updates should allow change of phase: %v", namespace.Status.Phase) } if len(namespace.Spec.Finalizers) != 1 || namespace.Spec.Finalizers[0] != api.FinalizerKubernetes { t.Errorf("PrepareForUpdate should have preserved old finalizers") } errs := StatusStrategy.ValidateUpdate(ctx, namespace, oldNamespace) if len(errs) != 0 { t.Errorf("Unexpected error %v", errs) } if namespace.ResourceVersion != "9" { t.Errorf("Incoming resource version on update should not be mutated") } }
func updateNetworkingCondition(node *api.Node, routeCreated bool) { _, networkingCondition := api.GetNodeCondition(&node.Status, api.NodeNetworkUnavailable) currentTime := unversioned.Now() if routeCreated { if networkingCondition != nil && networkingCondition.Status != api.ConditionFalse { networkingCondition.Status = api.ConditionFalse networkingCondition.Reason = "RouteCreated" networkingCondition.Message = "RouteController created a route" networkingCondition.LastTransitionTime = currentTime } else if networkingCondition == nil { node.Status.Conditions = append(node.Status.Conditions, api.NodeCondition{ Type: api.NodeNetworkUnavailable, Status: api.ConditionFalse, Reason: "RouteCreated", Message: "RouteController created a route", LastTransitionTime: currentTime, }) } } else { if networkingCondition != nil && networkingCondition.Status != api.ConditionTrue { networkingCondition.Status = api.ConditionTrue networkingCondition.Reason = "NoRouteCreated" networkingCondition.Message = "RouteController failed to create a route" networkingCondition.LastTransitionTime = currentTime } else if networkingCondition == nil { node.Status.Conditions = append(node.Status.Conditions, api.NodeCondition{ Type: api.NodeNetworkUnavailable, Status: api.ConditionTrue, Reason: "NoRouteCreated", Message: "RouteController failed to create a route", LastTransitionTime: currentTime, }) } } }
// BeforeDelete tests whether the object can be gracefully deleted. If graceful is set the object // should be gracefully deleted, if gracefulPending is set the object has already been gracefully deleted // (and the provided grace period is longer than the time to deletion), and an error is returned if the // condition cannot be checked or the gracePeriodSeconds is invalid. The options argument may be updated with // default values if graceful is true. func BeforeDelete(strategy RESTDeleteStrategy, ctx api.Context, obj runtime.Object, options *api.DeleteOptions) (graceful, gracefulPending bool, err error) { objectMeta, gvk, kerr := objectMetaAndKind(strategy, obj) if kerr != nil { return false, false, kerr } // Checking the Preconditions here to fail early. They'll be enforced later on when we actually do the deletion, too. if options.Preconditions != nil && options.Preconditions.UID != nil && *options.Preconditions.UID != objectMeta.UID { return false, false, errors.NewConflict(unversioned.GroupResource{Group: gvk.Group, Resource: gvk.Kind}, objectMeta.Name, fmt.Errorf("the UID in the precondition (%s) does not match the UID in record (%s). The object might have been deleted and then recreated", *options.Preconditions.UID, objectMeta.UID)) } gracefulStrategy, ok := strategy.(RESTGracefulDeleteStrategy) if !ok { return false, false, nil } // if the object is already being deleted if objectMeta.DeletionTimestamp != nil { // if we are already being deleted, we may only shorten the deletion grace period // this means the object was gracefully deleted previously but deletionGracePeriodSeconds was not set, // so we force deletion immediately if objectMeta.DeletionGracePeriodSeconds == nil { return false, false, nil } // only a shorter grace period may be provided by a user if options.GracePeriodSeconds != nil { period := int64(*options.GracePeriodSeconds) if period > *objectMeta.DeletionGracePeriodSeconds { return false, true, nil } now := unversioned.NewTime(unversioned.Now().Add(time.Second * time.Duration(*options.GracePeriodSeconds))) objectMeta.DeletionTimestamp = &now objectMeta.DeletionGracePeriodSeconds = &period options.GracePeriodSeconds = &period return true, false, nil } // graceful deletion is pending, do nothing options.GracePeriodSeconds = objectMeta.DeletionGracePeriodSeconds return false, true, nil } if !gracefulStrategy.CheckGracefulDelete(obj, options) { return false, false, nil } now := unversioned.NewTime(unversioned.Now().Add(time.Second * time.Duration(*options.GracePeriodSeconds))) objectMeta.DeletionTimestamp = &now objectMeta.DeletionGracePeriodSeconds = options.GracePeriodSeconds return true, false, nil }
func TestStaticPodStatus(t *testing.T) { staticPod := getTestPod() staticPod.Annotations = map[string]string{kubetypes.ConfigSourceAnnotationKey: "file"} mirrorPod := getTestPod() mirrorPod.UID = "mirror-12345678" mirrorPod.Annotations = map[string]string{ kubetypes.ConfigSourceAnnotationKey: "api", kubetypes.ConfigMirrorAnnotationKey: "mirror", } client := fake.NewSimpleClientset(mirrorPod) m := newTestManager(client) m.podManager.AddPod(staticPod) m.podManager.AddPod(mirrorPod) // Verify setup. assert.True(t, kubepod.IsStaticPod(staticPod), "SetUp error: staticPod") assert.True(t, kubepod.IsMirrorPod(mirrorPod), "SetUp error: mirrorPod") assert.Equal(t, m.podManager.TranslatePodUID(mirrorPod.UID), staticPod.UID) status := getRandomPodStatus() now := unversioned.Now() status.StartTime = &now m.SetPodStatus(staticPod, status) retrievedStatus := expectPodStatus(t, m, staticPod) normalizeStatus(staticPod, &status) assert.True(t, isStatusEqual(&status, &retrievedStatus), "Expected: %+v, Got: %+v", status, retrievedStatus) retrievedStatus, _ = m.GetPodStatus(mirrorPod.UID) assert.True(t, isStatusEqual(&status, &retrievedStatus), "Expected: %+v, Got: %+v", status, retrievedStatus) // Should translate mirrorPod / staticPod UID. m.testSyncBatch() verifyActions(t, m.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: unversioned.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: unversioned.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, }) updateAction := client.Actions()[1].(core.UpdateActionImpl) updatedPod := updateAction.Object.(*api.Pod) assert.Equal(t, mirrorPod.UID, updatedPod.UID, "Expected mirrorPod (%q), but got %q", mirrorPod.UID, updatedPod.UID) assert.True(t, isStatusEqual(&status, &updatedPod.Status), "Expected: %+v, Got: %+v", status, updatedPod.Status) client.ClearActions() // No changes. m.testSyncBatch() verifyActions(t, m.kubeClient, []core.Action{}) // Mirror pod identity changes. m.podManager.DeletePod(mirrorPod) mirrorPod.UID = "new-mirror-pod" mirrorPod.Status = api.PodStatus{} m.podManager.AddPod(mirrorPod) // Expect no update to mirror pod, since UID has changed. m.testSyncBatch() verifyActions(t, m.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: unversioned.GroupVersionResource{Resource: "pods"}}}, }) }
// pastActiveDeadline checks if job has ActiveDeadlineSeconds field set and if it is exceeded. func pastActiveDeadline(job *batch.Job) bool { if job.Spec.ActiveDeadlineSeconds == nil || job.Status.StartTime == nil { return false } now := unversioned.Now() start := job.Status.StartTime.Time duration := now.Time.Sub(start) allowedDuration := time.Duration(*job.Spec.ActiveDeadlineSeconds) * time.Second return duration >= allowedDuration }
// GetContainerLogs uses rkt's GetLogs API to get the logs of the container. // By default, it returns a snapshot of the container log. Set |follow| to true to // stream the log. Set |follow| to false and specify the number of lines (e.g. // "100" or "all") to tail the log. // // TODO(yifan): This doesn't work with lkvm stage1 yet. func (r *Runtime) GetContainerLogs(pod *api.Pod, containerID kubecontainer.ContainerID, logOptions *api.PodLogOptions, stdout, stderr io.Writer) error { id, err := parseContainerID(containerID) if err != nil { return err } var since int64 if logOptions.SinceSeconds != nil { t := unversioned.Now().Add(-time.Duration(*logOptions.SinceSeconds) * time.Second) since = t.Unix() } if logOptions.SinceTime != nil { since = logOptions.SinceTime.Unix() } getLogsRequest := &rktapi.GetLogsRequest{ PodId: id.uuid, AppName: id.appName, Follow: logOptions.Follow, SinceTime: since, } if logOptions.TailLines != nil { getLogsRequest.Lines = int32(*logOptions.TailLines) } stream, err := r.apisvc.GetLogs(context.Background(), getLogsRequest) if err != nil { glog.Errorf("rkt: Failed to create log stream for pod %q: %v", format.Pod(pod), err) return err } for { log, err := stream.Recv() if err == io.EOF { break } if err != nil { glog.Errorf("rkt: Failed to receive log for pod %q: %v", format.Pod(pod), err) return err } processLines(log.Lines, logOptions, stdout, stderr) } return nil }
func makeEvent(reason, message string, involvedObject api.ObjectReference) api.Event { eventTime := unversioned.Now() event := api.Event{ Reason: reason, Message: message, InvolvedObject: involvedObject, Source: api.EventSource{ Component: "kubelet", Host: "kublet.node1", }, Count: 1, FirstTimestamp: eventTime, LastTimestamp: eventTime, Type: api.EventTypeNormal, } return event }
func TestChangedStatusKeepsStartTime(t *testing.T) { syncer := newTestManager(&fake.Clientset{}) testPod := getTestPod() now := unversioned.Now() firstStatus := getRandomPodStatus() firstStatus.StartTime = &now syncer.SetPodStatus(testPod, firstStatus) syncer.SetPodStatus(testPod, getRandomPodStatus()) verifyUpdates(t, syncer, 2) finalStatus := expectPodStatus(t, syncer, testPod) if finalStatus.StartTime.IsZero() { t.Errorf("StartTime should not be zero") } expected := now.Rfc3339Copy() if !finalStatus.StartTime.Equal(expected) { t.Errorf("Expected %v, but got %v", expected, finalStatus.StartTime) } }
func (t *Tester) testCreateResetsUserData(valid runtime.Object) { objectMeta := t.getObjectMetaOrFail(valid) now := unversioned.Now() objectMeta.UID = "bad-uid" objectMeta.CreationTimestamp = now obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid) if err != nil { t.Fatalf("Unexpected error: %v", err) } if obj == nil { t.Fatalf("Unexpected object from result: %#v", obj) } defer t.delete(t.TestContext(), obj) if objectMeta.UID == "bad-uid" || objectMeta.CreationTimestamp == now { t.Errorf("ObjectMeta did not reset basic fields: %#v", objectMeta) } }
func TestNewStatusPreservesPodStartTime(t *testing.T) { syncer := newTestManager(&fake.Clientset{}) pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "12345678", Name: "foo", Namespace: "new", }, Status: api.PodStatus{}, } now := unversioned.Now() startTime := unversioned.NewTime(now.Time.Add(-1 * time.Minute)) pod.Status.StartTime = &startTime syncer.SetPodStatus(pod, getRandomPodStatus()) status := expectPodStatus(t, syncer, pod) if !status.StartTime.Time.Equal(startTime.Time) { t.Errorf("Unexpected start time, expected %v, actual %v", startTime, status.StartTime) } }
func TestDeletePods(t *testing.T) { pod := getTestPod() // Set the deletion timestamp. pod.DeletionTimestamp = new(unversioned.Time) client := fake.NewSimpleClientset(pod) m := newTestManager(client) m.podManager.AddPod(pod) status := getRandomPodStatus() now := unversioned.Now() status.StartTime = &now m.SetPodStatus(pod, status) m.testSyncBatch() // Expect to see an delete action. verifyActions(t, m.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: unversioned.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: unversioned.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, core.DeleteActionImpl{ActionImpl: core.ActionImpl{Verb: "delete", Resource: unversioned.GroupVersionResource{Resource: "pods"}}}, }) }
func TestDeleteNamespaceWithCompleteFinalizers(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) key := etcdtest.AddPrefix("namespaces/foo") ctx := api.NewContext() now := unversioned.Now() namespace := &api.Namespace{ ObjectMeta: api.ObjectMeta{ Name: "foo", DeletionTimestamp: &now, }, Spec: api.NamespaceSpec{ Finalizers: []api.FinalizerName{}, }, Status: api.NamespaceStatus{Phase: api.NamespaceActive}, } if err := storage.Storage.Create(ctx, key, namespace, nil, 0); err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := storage.Delete(ctx, "foo", nil); err != nil { t.Errorf("unexpected error: %v", err) } }
// Delete enforces life-cycle rules for namespace termination func (r *REST) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) { nsObj, err := r.Get(ctx, name) if err != nil { return nil, err } namespace := nsObj.(*api.Namespace) // Ensure we have a UID precondition if options == nil { options = api.NewDeleteOptions(0) } if options.Preconditions == nil { options.Preconditions = &api.Preconditions{} } if options.Preconditions.UID == nil { options.Preconditions.UID = &namespace.UID } else if *options.Preconditions.UID != namespace.UID { err = apierrors.NewConflict( api.Resource("namespaces"), name, fmt.Errorf("Precondition failed: UID in precondition: %v, UID in object meta: %v", *options.Preconditions.UID, namespace.UID), ) return nil, err } // upon first request to delete, we switch the phase to start namespace termination // TODO: enhance graceful deletion's calls to DeleteStrategy to allow phase change and finalizer patterns if namespace.DeletionTimestamp.IsZero() { key, err := r.Store.KeyFunc(ctx, name) if err != nil { return nil, err } preconditions := storage.Preconditions{UID: options.Preconditions.UID} out := r.Store.NewFunc() err = r.Store.Storage.GuaranteedUpdate( ctx, key, out, false, &preconditions, storage.SimpleUpdate(func(existing runtime.Object) (runtime.Object, error) { existingNamespace, ok := existing.(*api.Namespace) if !ok { // wrong type return nil, fmt.Errorf("expected *api.Namespace, got %v", existing) } // Set the deletion timestamp if needed if existingNamespace.DeletionTimestamp.IsZero() { now := unversioned.Now() existingNamespace.DeletionTimestamp = &now } // Set the namespace phase to terminating, if needed if existingNamespace.Status.Phase != api.NamespaceTerminating { existingNamespace.Status.Phase = api.NamespaceTerminating } return existingNamespace, nil }), ) if err != nil { err = storageerr.InterpretGetError(err, api.Resource("namespaces"), name) err = storageerr.InterpretUpdateError(err, api.Resource("namespaces"), name) if _, ok := err.(*apierrors.StatusError); !ok { err = apierrors.NewInternalError(err) } return nil, err } return out, nil } // prior to final deletion, we must ensure that finalizers is empty if len(namespace.Spec.Finalizers) != 0 { err = apierrors.NewConflict(api.Resource("namespaces"), namespace.Name, fmt.Errorf("The system is ensuring all content is removed from this namespace. Upon completion, this namespace will automatically be purged by the system.")) return nil, err } return r.Store.Delete(ctx, name, options) }
// FillObjectMetaSystemFields populates fields that are managed by the system on ObjectMeta. func FillObjectMetaSystemFields(ctx Context, meta *ObjectMeta) { meta.CreationTimestamp = unversioned.Now() meta.UID = util.NewUUID() meta.SelfLink = "" }
func TestSortingActivePods(t *testing.T) { numPods := 9 // This rc is not needed by the test, only the newPodList to give the pods labels/a namespace. rc := newReplicationController(0) podList := newPodList(nil, numPods, api.PodRunning, rc) pods := make([]*api.Pod, len(podList.Items)) for i := range podList.Items { pods[i] = &podList.Items[i] } // pods[0] is not scheduled yet. pods[0].Spec.NodeName = "" pods[0].Status.Phase = api.PodPending // pods[1] is scheduled but pending. pods[1].Spec.NodeName = "bar" pods[1].Status.Phase = api.PodPending // pods[2] is unknown. pods[2].Spec.NodeName = "foo" pods[2].Status.Phase = api.PodUnknown // pods[3] is running but not ready. pods[3].Spec.NodeName = "foo" pods[3].Status.Phase = api.PodRunning // pods[4] is running and ready but without LastTransitionTime. now := unversioned.Now() pods[4].Spec.NodeName = "foo" pods[4].Status.Phase = api.PodRunning pods[4].Status.Conditions = []api.PodCondition{{Type: api.PodReady, Status: api.ConditionTrue}} pods[4].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}} // pods[5] is running and ready and with LastTransitionTime. pods[5].Spec.NodeName = "foo" pods[5].Status.Phase = api.PodRunning pods[5].Status.Conditions = []api.PodCondition{{Type: api.PodReady, Status: api.ConditionTrue, LastTransitionTime: now}} pods[5].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}} // pods[6] is running ready for a longer time than pods[5]. then := unversioned.Time{Time: now.AddDate(0, -1, 0)} pods[6].Spec.NodeName = "foo" pods[6].Status.Phase = api.PodRunning pods[6].Status.Conditions = []api.PodCondition{{Type: api.PodReady, Status: api.ConditionTrue, LastTransitionTime: then}} pods[6].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}} // pods[7] has lower container restart count than pods[6]. pods[7].Spec.NodeName = "foo" pods[7].Status.Phase = api.PodRunning pods[7].Status.Conditions = []api.PodCondition{{Type: api.PodReady, Status: api.ConditionTrue, LastTransitionTime: then}} pods[7].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 2}, {RestartCount: 1}} pods[7].CreationTimestamp = now // pods[8] is older than pods[7]. pods[8].Spec.NodeName = "foo" pods[8].Status.Phase = api.PodRunning pods[8].Status.Conditions = []api.PodCondition{{Type: api.PodReady, Status: api.ConditionTrue, LastTransitionTime: then}} pods[8].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 2}, {RestartCount: 1}} pods[8].CreationTimestamp = then getOrder := func(pods []*api.Pod) []string { names := make([]string, len(pods)) for i := range pods { names[i] = pods[i].Name } return names } expected := getOrder(pods) for i := 0; i < 20; i++ { idx := rand.Perm(numPods) randomizedPods := make([]*api.Pod, numPods) for j := 0; j < numPods; j++ { randomizedPods[j] = pods[idx[j]] } sort.Sort(ActivePods(randomizedPods)) actual := getOrder(randomizedPods) if !reflect.DeepEqual(actual, expected) { t.Errorf("expected %v, got %v", expected, actual) } } }
// syncJob will sync the job with the given key if it has had its expectations fulfilled, meaning // it did not expect to see any more of its pods created or deleted. This function is not meant to be invoked // concurrently with the same key. func (jm *JobController) syncJob(key string) error { startTime := time.Now() defer func() { glog.V(4).Infof("Finished syncing job %q (%v)", key, time.Now().Sub(startTime)) }() if !jm.podStoreSynced() { // Sleep so we give the pod reflector goroutine a chance to run. time.Sleep(replicationcontroller.PodStoreSyncedPollPeriod) glog.V(4).Infof("Waiting for pods controller to sync, requeuing job %v", key) jm.queue.Add(key) return nil } obj, exists, err := jm.jobStore.Store.GetByKey(key) if !exists { glog.V(4).Infof("Job has been deleted: %v", key) jm.expectations.DeleteExpectations(key) return nil } if err != nil { glog.Errorf("Unable to retrieve job %v from store: %v", key, err) jm.queue.Add(key) return err } job := *obj.(*batch.Job) // Check the expectations of the job before counting active pods, otherwise a new pod can sneak in // and update the expectations after we've retrieved active pods from the store. If a new pod enters // the store after we've checked the expectation, the job sync is just deferred till the next relist. jobKey, err := controller.KeyFunc(&job) if err != nil { glog.Errorf("Couldn't get key for job %#v: %v", job, err) return err } jobNeedsSync := jm.expectations.SatisfiedExpectations(jobKey) selector, _ := unversioned.LabelSelectorAsSelector(job.Spec.Selector) podList, err := jm.podStore.Pods(job.Namespace).List(selector) if err != nil { glog.Errorf("Error getting pods for job %q: %v", key, err) jm.queue.Add(key) return err } activePods := controller.FilterActivePods(podList.Items) active := int32(len(activePods)) succeeded, failed := getStatus(podList.Items) conditions := len(job.Status.Conditions) if job.Status.StartTime == nil { now := unversioned.Now() job.Status.StartTime = &now } // if job was finished previously, we don't want to redo the termination if isJobFinished(&job) { return nil } if pastActiveDeadline(&job) { // TODO: below code should be replaced with pod termination resulting in // pod failures, rather than killing pods. Unfortunately none such solution // exists ATM. There's an open discussion in the topic in // https://github.com/kubernetes/kubernetes/issues/14602 which might give // some sort of solution to above problem. // kill remaining active pods wait := sync.WaitGroup{} wait.Add(int(active)) for i := int32(0); i < active; i++ { go func(ix int32) { defer wait.Done() if err := jm.podControl.DeletePod(job.Namespace, activePods[ix].Name, &job); err != nil { defer utilruntime.HandleError(err) } }(i) } wait.Wait() // update status values accordingly failed += active active = 0 job.Status.Conditions = append(job.Status.Conditions, newCondition(batch.JobFailed, "DeadlineExceeded", "Job was active longer than specified deadline")) jm.recorder.Event(&job, api.EventTypeNormal, "DeadlineExceeded", "Job was active longer than specified deadline") } else { if jobNeedsSync { active = jm.manageJob(activePods, succeeded, &job) } completions := succeeded complete := false if job.Spec.Completions == nil { // This type of job is complete when any pod exits with success. // Each pod is capable of // determining whether or not the entire Job is done. Subsequent pods are // not expected to fail, but if they do, the failure is ignored. Once any // pod succeeds, the controller waits for remaining pods to finish, and // then the job is complete. if succeeded > 0 && active == 0 { complete = true } } else { // Job specifies a number of completions. This type of job signals // success by having that number of successes. Since we do not // start more pods than there are remaining completions, there should // not be any remaining active pods once this count is reached. if completions >= *job.Spec.Completions { complete = true if active > 0 { jm.recorder.Event(&job, api.EventTypeWarning, "TooManyActivePods", "Too many active pods running after completion count reached") } if completions > *job.Spec.Completions { jm.recorder.Event(&job, api.EventTypeWarning, "TooManySucceededPods", "Too many succeeded pods running after completion count reached") } } } if complete { job.Status.Conditions = append(job.Status.Conditions, newCondition(batch.JobComplete, "", "")) now := unversioned.Now() job.Status.CompletionTime = &now } } // no need to update the job if the status hasn't changed since last time if job.Status.Active != active || job.Status.Succeeded != succeeded || job.Status.Failed != failed || len(job.Status.Conditions) != conditions { job.Status.Active = active job.Status.Succeeded = succeeded job.Status.Failed = failed if err := jm.updateHandler(&job); err != nil { glog.Errorf("Failed to update job %v, requeuing. Error: %v", job.Name, err) jm.enqueueController(&job) } } return nil }
// tryAcquireOrRenew tries to acquire a leader lease if it is not already acquired, // else it tries to renew the lease if it has already been acquired. Returns true // on success else returns false. func (le *LeaderElector) tryAcquireOrRenew() bool { now := unversioned.Now() leaderElectionRecord := LeaderElectionRecord{ HolderIdentity: le.config.Identity, LeaseDurationSeconds: int(le.config.LeaseDuration / time.Second), RenewTime: now, AcquireTime: now, } e, err := le.config.Client.Endpoints(le.config.EndpointsMeta.Namespace).Get(le.config.EndpointsMeta.Name) if err != nil { if !errors.IsNotFound(err) { glog.Errorf("error retrieving endpoint: %v", err) return false } leaderElectionRecordBytes, err := json.Marshal(leaderElectionRecord) if err != nil { return false } _, err = le.config.Client.Endpoints(le.config.EndpointsMeta.Namespace).Create(&api.Endpoints{ ObjectMeta: api.ObjectMeta{ Name: le.config.EndpointsMeta.Name, Namespace: le.config.EndpointsMeta.Namespace, Annotations: map[string]string{ LeaderElectionRecordAnnotationKey: string(leaderElectionRecordBytes), }, }, }) if err != nil { glog.Errorf("error initially creating endpoints: %v", err) return false } le.observedRecord = leaderElectionRecord le.observedTime = time.Now() return true } if e.Annotations == nil { e.Annotations = make(map[string]string) } var oldLeaderElectionRecord LeaderElectionRecord if oldLeaderElectionRecordBytes, found := e.Annotations[LeaderElectionRecordAnnotationKey]; found { if err := json.Unmarshal([]byte(oldLeaderElectionRecordBytes), &oldLeaderElectionRecord); err != nil { glog.Errorf("error unmarshaling leader election record: %v", err) return false } if !reflect.DeepEqual(le.observedRecord, oldLeaderElectionRecord) { le.observedRecord = oldLeaderElectionRecord le.observedTime = time.Now() } if le.observedTime.Add(le.config.LeaseDuration).After(now.Time) && oldLeaderElectionRecord.HolderIdentity != le.config.Identity { glog.Infof("lock is held by %v and has not yet expired", oldLeaderElectionRecord.HolderIdentity) return false } } // We're going to try to update. The leaderElectionRecord is set to it's default // here. Let's correct it before updating. if oldLeaderElectionRecord.HolderIdentity == le.config.Identity { leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime } else { leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1 } leaderElectionRecordBytes, err := json.Marshal(leaderElectionRecord) if err != nil { glog.Errorf("err marshaling leader election record: %v", err) return false } e.Annotations[LeaderElectionRecordAnnotationKey] = string(leaderElectionRecordBytes) _, err = le.config.Client.Endpoints(le.config.EndpointsMeta.Namespace).Update(e) if err != nil { glog.Errorf("err: %v", err) return false } le.observedRecord = leaderElectionRecord le.observedTime = time.Now() return true }
func (recorder *recorderImpl) Event(object runtime.Object, eventtype, reason, message string) { recorder.generateEvent(object, unversioned.Now(), eventtype, reason, message) }
func TestDoProbe(t *testing.T) { m := newTestManager() // Test statuses. runningStatus := getTestRunningStatus() pendingStatus := getTestRunningStatus() pendingStatus.ContainerStatuses[0].State.Running = nil terminatedStatus := getTestRunningStatus() terminatedStatus.ContainerStatuses[0].State.Running = nil terminatedStatus.ContainerStatuses[0].State.Terminated = &api.ContainerStateTerminated{ StartedAt: unversioned.Now(), } otherStatus := getTestRunningStatus() otherStatus.ContainerStatuses[0].Name = "otherContainer" failedStatus := getTestRunningStatus() failedStatus.Phase = api.PodFailed tests := []struct { probe api.Probe podStatus *api.PodStatus expectContinue bool expectSet bool expectedResult results.Result }{ { // No status. expectContinue: true, }, { // Pod failed podStatus: &failedStatus, }, { // No container status podStatus: &otherStatus, expectContinue: true, }, { // Container waiting podStatus: &pendingStatus, expectContinue: true, expectSet: true, }, { // Container terminated podStatus: &terminatedStatus, expectSet: true, }, { // Probe successful. podStatus: &runningStatus, expectContinue: true, expectSet: true, expectedResult: results.Success, }, { // Initial delay passed podStatus: &runningStatus, probe: api.Probe{ InitialDelaySeconds: -100, }, expectContinue: true, expectSet: true, expectedResult: results.Success, }, } for _, probeType := range [...]probeType{liveness, readiness} { for i, test := range tests { w := newTestWorker(m, probeType, test.probe) if test.podStatus != nil { m.statusManager.SetPodStatus(w.pod, *test.podStatus) } if c := w.doProbe(); c != test.expectContinue { t.Errorf("[%s-%d] Expected continue to be %v but got %v", probeType, i, test.expectContinue, c) } result, ok := resultsManager(m, probeType).Get(testContainerID) if ok != test.expectSet { t.Errorf("[%s-%d] Expected to have result: %v but got %v", probeType, i, test.expectSet, ok) } if result != test.expectedResult { t.Errorf("[%s-%d] Expected result: %v but got %v", probeType, i, test.expectedResult, result) } // Clean up. m.statusManager = status.NewManager(&fake.Clientset{}, kubepod.NewBasicPodManager(nil)) resultsManager(m, probeType).Remove(testContainerID) } } }