func TestStoreBasicExport(t *testing.T) { podA := api.Pod{ ObjectMeta: api.ObjectMeta{ Namespace: "test", Name: "foo", Labels: map[string]string{}, }, Spec: api.PodSpec{NodeName: "machine"}, Status: api.PodStatus{HostIP: "1.2.3.4"}, } server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) testContext := api.WithNamespace(api.NewContext(), "test") registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = true if !updateAndVerify(t, testContext, registry, &podA) { t.Errorf("Unexpected error updating podA") } obj, err := registry.Export(testContext, podA.Name, unversioned.ExportOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } exportedPod := obj.(*api.Pod) if exportedPod.Labels["prepare_create"] != "true" { t.Errorf("expected: prepare_create->true, found: %s", exportedPod.Labels["prepare_create"]) } delete(exportedPod.Labels, "prepare_create") exportObjectMeta(&podA.ObjectMeta, false) podA.Spec = exportedPod.Spec if !reflect.DeepEqual(&podA, exportedPod) { t.Errorf("expected:\n%v\nsaw:\n%v\n", &podA, exportedPod) } }
// ToAPIPod converts Pod to api.Pod. Note that if a field in api.Pod has no // corresponding field in Pod, the field would not be populated. func (p *Pod) ToAPIPod() *api.Pod { var pod api.Pod pod.UID = p.ID pod.Name = p.Name pod.Namespace = p.Namespace for _, c := range p.Containers { var container api.Container container.Name = c.Name container.Image = c.Image pod.Spec.Containers = append(pod.Spec.Containers, container) } return &pod }
// same as above func comments, except 'recyclerClient' is a narrower pod API // interface to ease testing func internalRecycleVolumeByWatchingPodUntilCompletion(pvName string, pod *api.Pod, recyclerClient recyclerClient) error { glog.V(5).Infof("creating recycler pod for volume %s\n", pod.Name) // Generate unique name for the recycler pod - we need to get "already // exists" error when a previous controller has already started recycling // the volume. Here we assume that pv.Name is already unique. pod.Name = "recycler-for-" + pvName pod.GenerateName = "" // Start the pod _, err := recyclerClient.CreatePod(pod) if err != nil { if errors.IsAlreadyExists(err) { glog.V(5).Infof("old recycler pod %q found for volume", pod.Name) } else { return fmt.Errorf("Unexpected error creating recycler pod: %+v\n", err) } } defer recyclerClient.DeletePod(pod.Name, pod.Namespace) // Now only the old pod or the new pod run. Watch it until it finishes. stopChannel := make(chan struct{}) defer close(stopChannel) nextPod := recyclerClient.WatchPod(pod.Name, pod.Namespace, stopChannel) for { watchedPod := nextPod() if watchedPod.Status.Phase == api.PodSucceeded { // volume.Recycle() returns nil on success, else error return nil } if watchedPod.Status.Phase == api.PodFailed { // volume.Recycle() returns nil on success, else error if watchedPod.Status.Message != "" { return fmt.Errorf(watchedPod.Status.Message) } else { return fmt.Errorf("pod failed, pod.Status.Message unknown.") } } } }
// updateAnnotations returns an Annotation map containing the api annotation map plus // locally managed annotations func updateAnnotations(existing, ref *api.Pod) { annotations := make(map[string]string, len(ref.Annotations)+len(localAnnotations)) for k, v := range ref.Annotations { annotations[k] = v } for _, k := range localAnnotations { if v, ok := existing.Annotations[k]; ok { annotations[k] = v } } existing.Annotations = annotations }
// checkAndUpdatePod updates existing, and: // * if ref makes a meaningful change, returns needUpdate=true // * if ref makes no meaningful change, but changes the pod status, returns needReconcile=true // * else return both false // Now, needUpdate and needReconcile should never be both true func checkAndUpdatePod(existing, ref *api.Pod) (needUpdate, needReconcile bool) { // TODO: it would be better to update the whole object and only preserve certain things // like the source annotation or the UID (to ensure safety) if !podsDifferSemantically(existing, ref) { // this is not an update // Only check reconcile when it is not an update, because if the pod is going to // be updated, an extra reconcile is unnecessary if !reflect.DeepEqual(existing.Status, ref.Status) { // Pod with changed pod status needs reconcile, because kubelet should // be the source of truth of pod status. existing.Status = ref.Status needReconcile = true } return } // this is an update // Overwrite the first-seen time with the existing one. This is our own // internal annotation, there is no need to update. ref.Annotations[kubetypes.ConfigFirstSeenAnnotationKey] = existing.Annotations[kubetypes.ConfigFirstSeenAnnotationKey] existing.Spec = ref.Spec existing.Labels = ref.Labels existing.DeletionTimestamp = ref.DeletionTimestamp existing.DeletionGracePeriodSeconds = ref.DeletionGracePeriodSeconds existing.Status = ref.Status updateAnnotations(existing, ref) needUpdate = true return }
func Convert_v1_Pod_To_api_Pod(in *Pod, out *api.Pod, s conversion.Scope) error { // TODO: when we move init container to beta, remove these conversions if value, ok := in.Annotations[PodInitContainersAnnotationKey]; ok { var values []Container if err := json.Unmarshal([]byte(value), &values); err != nil { return err } // Conversion from external to internal version exists more to // satisfy the needs of the decoder than it does to be a general // purpose tool. And Decode always creates an intermediate object // to decode to. Thus the caller of UnsafeConvertToVersion is // taking responsibility to ensure mutation of in is not exposed // back to the caller. in.Spec.InitContainers = values } if value, ok := in.Annotations[PodInitContainerStatusesAnnotationKey]; ok { var values []ContainerStatus if err := json.Unmarshal([]byte(value), &values); err != nil { return err } // Conversion from external to internal version exists more to // satisfy the needs of the decoder than it does to be a general // purpose tool. And Decode always creates an intermediate object // to decode to. Thus the caller of UnsafeConvertToVersion is // taking responsibility to ensure mutation of in is not exposed // back to the caller. in.Status.InitContainerStatuses = values } if err := autoConvert_v1_Pod_To_api_Pod(in, out, s); err != nil { return err } if len(out.Annotations) > 0 { old := out.Annotations out.Annotations = make(map[string]string, len(old)) for k, v := range old { out.Annotations[k] = v } delete(out.Annotations, PodInitContainersAnnotationKey) delete(out.Annotations, PodInitContainerStatusesAnnotationKey) } return nil }
func updatePod(t *testing.T, s storage.Interface, obj, old *api.Pod) *api.Pod { updateFn := func(input runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) { newObj, err := api.Scheme.DeepCopy(obj) if err != nil { t.Errorf("unexpected error: %v", err) return nil, nil, err } return newObj.(*api.Pod), nil, nil } key := etcdtest.AddPrefix("pods/ns/" + obj.Name) if err := s.GuaranteedUpdate(context.TODO(), key, &api.Pod{}, old == nil, nil, updateFn); err != nil { t.Errorf("unexpected error: %v", err) } obj.ResourceVersion = "" result := &api.Pod{} if err := s.Get(context.TODO(), key, result, false); err != nil { t.Errorf("unexpected error: %v", err) } return result }
// SetIdentity sets the pet namespace and name. func (n *NameIdentityMapper) SetIdentity(id string, pet *api.Pod) { pet.Name = fmt.Sprintf("%v-%v", n.ps.Name, id) pet.Namespace = n.ps.Namespace return }
func TestSyncBatchNoDeadlock(t *testing.T) { client := &fake.Clientset{} m := newTestManager(client) pod := getTestPod() // Setup fake client. var ret api.Pod var err error client.AddReactor("*", "pods", func(action core.Action) (bool, runtime.Object, error) { switch action := action.(type) { case core.GetAction: assert.Equal(t, pod.Name, action.GetName(), "Unexpeted GetAction: %+v", action) case core.UpdateAction: assert.Equal(t, pod.Name, action.GetObject().(*api.Pod).Name, "Unexpeted UpdateAction: %+v", action) default: assert.Fail(t, "Unexpected Action: %+v", action) } return true, &ret, err }) pod.Status.ContainerStatuses = []api.ContainerStatus{{State: api.ContainerState{Running: &api.ContainerStateRunning{}}}} getAction := core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: unversioned.GroupVersionResource{Resource: "pods"}}} updateAction := core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: unversioned.GroupVersionResource{Resource: "pods"}, Subresource: "status"}} // Pod not found. ret = *pod err = errors.NewNotFound(api.Resource("pods"), pod.Name) m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction}) client.ClearActions() // Pod was recreated. ret.UID = "other_pod" err = nil m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction}) client.ClearActions() // Pod not deleted (success case). ret = *pod m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction, updateAction}) client.ClearActions() // Pod is terminated, but still running. pod.DeletionTimestamp = new(unversioned.Time) m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction, updateAction}) client.ClearActions() // Pod is terminated successfully. pod.Status.ContainerStatuses[0].State.Running = nil pod.Status.ContainerStatuses[0].State.Terminated = &api.ContainerStateTerminated{} m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction, updateAction}) client.ClearActions() // Error case. err = fmt.Errorf("intentional test error") m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction}) client.ClearActions() }