// implements Unique func (p *Pod) GetUID() string { if id, err := cache.MetaNamespaceKeyFunc(p.Pod); err != nil { panic(fmt.Sprintf("failed to determine pod id for '%+v'", p.Pod)) } else { return id } }
// Generates skydns records for a headless service. func (ks *kube2sky) newHeadlessService(subdomain string, service *kapi.Service) error { // Create an A record for every pod in the service. // This record must be periodically updated. // Format is as follows: // For a service x, with pods a and b create DNS records, // a.x.ns.domain. and, b.x.ns.domain. ks.mlock.Lock() defer ks.mlock.Unlock() key, err := kcache.MetaNamespaceKeyFunc(service) if err != nil { return err } e, exists, err := ks.endpointsStore.GetByKey(key) if err != nil { return fmt.Errorf("failed to get endpoints object from endpoints store - %v", err) } if !exists { glog.V(1).Infof("Could not find endpoints for service %q in namespace %q. DNS records will be created once endpoints show up.", service.Name, service.Namespace) return nil } if e, ok := e.(*kapi.Endpoints); ok { return ks.generateRecordsForHeadlessService(subdomain, e, service) } return nil }
// Iterate through all pods in desired state of world, and remove if they no // longer exist in the informer func (dswp *desiredStateOfWorldPopulator) findAndRemoveDeletedPods() { for dswPodUID, dswPodToAdd := range dswp.desiredStateOfWorld.GetPodToAdd() { dswPodKey, err := kcache.MetaNamespaceKeyFunc(dswPodToAdd.Pod) if err != nil { glog.Errorf("MetaNamespaceKeyFunc failed for pod %q (UID %q) with: %v", dswPodKey, dswPodUID, err) continue } // retrieve the pod object from pod informer with the namespace key informerPodObj, exists, err := dswp.podInformer.GetStore().GetByKey(dswPodKey) if err != nil || informerPodObj == nil { glog.Errorf("podInformer GetByKey failed for pod %q (UID %q) with %v", dswPodKey, dswPodUID, err) continue } if exists { informerPod, ok := informerPodObj.(*api.Pod) if !ok { glog.Errorf("Failed to cast obj %#v to pod object for pod %q (UID %q)", informerPod, dswPodKey, dswPodUID) continue } informerPodUID := volumehelper.GetUniquePodName(informerPod) // Check whether the unique idenfier of the pod from dsw matches the one retrived from pod informer if informerPodUID == dswPodUID { glog.V(10).Infof( "Verified pod %q (UID %q) from dsw exists in pod informer.", dswPodKey, dswPodUID) continue } } // the pod from dsw does not exist in pod informer, or it does not match the unique idenfier retrieved // from the informer, delete it from dsw glog.V(1).Infof( "Removing pod %q (UID %q) from dsw because it does not exist in pod informer.", dswPodKey, dswPodUID) dswp.desiredStateOfWorld.DeletePod(dswPodUID, dswPodToAdd.VolumeName, dswPodToAdd.NodeName) } }
// Handle ensures an image stream is checked for scheduling and then runs a direct import func (b *scheduled) Handle(obj interface{}) error { stream := obj.(*api.ImageStream) if b.enabled && needsScheduling(stream) { key, _ := cache.MetaNamespaceKeyFunc(stream) b.scheduler.Add(key, uniqueItem{uid: string(stream.UID), resourceVersion: stream.ResourceVersion}) } return b.controller.Next(stream, b) }
// Importing is invoked when the controller decides to import a stream in order to push back // the next schedule time. func (b *scheduled) Importing(stream *api.ImageStream) { if !b.enabled { return } glog.V(5).Infof("DEBUG: stream %s was just imported", stream.Name) // Push the current key back to the end of the queue because it's just been imported key, _ := cache.MetaNamespaceKeyFunc(stream) b.scheduler.Delay(key) }
// Schedule implements the Scheduler interface of Kubernetes. // It returns the selectedMachine's name and error (if there's any). func (k *kubeScheduler) Schedule(pod *api.Pod, unused algorithm.MinionLister) (string, error) { log.Infof("Try to schedule pod %v\n", pod.Name) ctx := api.WithNamespace(api.NewDefaultContext(), pod.Namespace) // default upstream scheduler passes pod.Name as binding.PodID podKey, err := podtask.MakePodKey(ctx, pod.Name) if err != nil { return "", err } k.api.Lock() defer k.api.Unlock() switch task, state := k.api.tasks().ForPod(podKey); state { case podtask.StateUnknown: // There's a bit of a potential race here, a pod could have been yielded() and // then before we get *here* it could be deleted. // We use meta to index the pod in the store since that's what k8s reflector does. podName, err := cache.MetaNamespaceKeyFunc(pod) if err != nil { log.Warningf("aborting Schedule, unable to understand pod object %+v", pod) return "", noSuchPodErr } if deleted := k.podUpdates.Poll(podName, queue.DELETE_EVENT); deleted { // avoid scheduling a pod that's been deleted between yieldPod() and Schedule() log.Infof("aborting Schedule, pod has been deleted %+v", pod) return "", noSuchPodErr } return k.doSchedule(k.api.tasks().Register(k.api.createPodTask(ctx, pod))) //TODO(jdef) it's possible that the pod state has diverged from what //we knew previously, we should probably update the task.Pod state here //before proceeding with scheduling case podtask.StatePending: if pod.UID != task.Pod.UID { // we're dealing with a brand new pod spec here, so the old one must have been // deleted -- and so our task store is out of sync w/ respect to reality //TODO(jdef) reconcile task return "", fmt.Errorf("task %v spec is out of sync with pod %v spec, aborting schedule", task.ID, pod.Name) } else if task.Has(podtask.Launched) { // task has been marked as "launched" but the pod binding creation may have failed in k8s, // but we're going to let someone else handle it, probably the mesos task error handler return "", fmt.Errorf("task %s has already been launched, aborting schedule", task.ID) } else { return k.doSchedule(task, nil) } default: return "", fmt.Errorf("task %s is not pending, nothing to schedule", task.ID) } }
// GetImageReferenceForObjectReference returns corresponding image reference for the given object // reference representing either an image stream image or image stream tag or docker image. func GetImageReferenceForObjectReference(namespace string, objRef *kapi.ObjectReference) (string, error) { switch objRef.Kind { case "ImageStreamImage", "DockerImage": res, err := imageapi.ParseDockerImageReference(objRef.Name) if err != nil { return "", err } if objRef.Kind == "ImageStreamImage" { if res.Namespace == "" { res.Namespace = objRef.Namespace } if res.Namespace == "" { res.Namespace = namespace } if len(res.ID) == 0 { return "", fmt.Errorf("missing id in ImageStreamImage reference %q", objRef.Name) } } else { // objRef.Kind == "DockerImage" res = res.DockerClientDefaults() } // docker image reference return res.DaemonMinimal().Exact(), nil case "ImageStreamTag": isName, tag, err := imageapi.ParseImageStreamTagName(objRef.Name) if err != nil { return "", err } ns := namespace if len(objRef.Namespace) > 0 { ns = objRef.Namespace } // <namespace>/<isname>:<tag> return cache.MetaNamespaceKeyFunc(&kapi.ObjectMeta{ Namespace: ns, Name: imageapi.JoinImageStreamTag(isName, tag), }) } return "", fmt.Errorf("unsupported object reference kind %s", objRef.Kind) }
func (ks *kube2sky) getServiceFromEndpoints(e *kapi.Endpoints) (*kapi.Service, error) { key, err := kcache.MetaNamespaceKeyFunc(e) if err != nil { return nil, err } obj, exists, err := ks.servicesStore.GetByKey(key) if err != nil { return nil, fmt.Errorf("failed to get service object from services store - %v", err) } if !exists { glog.V(1).Infof("could not find service for endpoint %q in namespace %q", e.Name, e.Namespace) return nil, nil } if svc, ok := obj.(*kapi.Service); ok { return svc, nil } return nil, fmt.Errorf("got a non service object in services store %v", obj) }
func (kd *KubeDNS) getServiceFromEndpoints(e *v1.Endpoints) (*v1.Service, error) { key, err := kcache.MetaNamespaceKeyFunc(e) if err != nil { return nil, err } obj, exists, err := kd.servicesStore.GetByKey(key) if err != nil { return nil, fmt.Errorf("failed to get service object from services store - %v", err) } if !exists { glog.V(3).Infof("No service for endpoint %q in namespace %q", e.Name, e.Namespace) return nil, nil } if svc, ok := assertIsService(obj); ok { return svc, nil } return nil, fmt.Errorf("got a non service object in services store %v", obj) }
// retryFunc returns a function to retry a controller event func retryFunc(kind string, isFatal func(err error) bool) controller.RetryFunc { return func(obj interface{}, err error, retries controller.Retry) bool { name, keyErr := cache.MetaNamespaceKeyFunc(obj) if keyErr != nil { name = "Unknown" } if isFatal != nil && isFatal(err) { glog.V(3).Infof("Will not retry fatal error for %s %s: %v", kind, name, err) utilruntime.HandleError(err) return false } if retries.Count > maxRetries { glog.V(3).Infof("Giving up retrying %s %s: %v", kind, name, err) utilruntime.HandleError(err) return false } glog.V(4).Infof("Retrying %s %s: %v", kind, name, err) return true } }
func (s *sourceFile) extractFromFile(filename string) (pod *v1.Pod, err error) { glog.V(3).Infof("Reading config file %q", filename) defer func() { if err == nil && pod != nil { objKey, keyErr := cache.MetaNamespaceKeyFunc(pod) if keyErr != nil { err = keyErr return } s.fileKeyMapping[filename] = objKey } }() file, err := os.Open(filename) if err != nil { return pod, err } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { return pod, err } defaultFn := func(pod *api.Pod) error { return s.applyDefaults(pod, filename) } parsed, pod, podErr := tryDecodeSinglePod(data, defaultFn) if parsed { if podErr != nil { return pod, podErr } return pod, nil } return pod, fmt.Errorf("%v: read '%v', but couldn't parse as pod(%v).\n", filename, string(data), podErr) }
func (k2c *Kube2Consul) getServiceFromEndpoints(eps *kapi.Endpoints) (*kapi.Service, error) { key, err := kcache.MetaNamespaceKeyFunc(eps) if err != nil { return nil, err } obj, exist, err := k2c.servicesStore.GetByKey(key) if err != nil { return nil, fmt.Errorf("faild to get service from service store: %v", err) } if !exist { glog.Infof("can't find service for endpoint %s in namespace %s.", eps.Name, eps.Namespace) return nil, nil } if svc, ok := assertIsService(obj); ok { return svc, nil } return nil, fmt.Errorf("a none service object in service store: %v", obj) }
// Returns true if the service corresponding to the given message has endpoints. // Note: Works only for services with ClusterIP. Will return an error for headless service (service without a clusterIP). // Important: Assumes that we already have the cacheLock. Callers responsibility to acquire it. // This is because the code will panic, if we try to acquire it again if we already have it. func (kd *KubeDNS) serviceWithClusterIPHasEndpoints(msg *skymsg.Service) (bool, error) { svc, ok := kd.clusterIPServiceMap[msg.Host] if !ok { // It is a headless service. return false, fmt.Errorf("method not expected to be called for headless service") } key, err := kcache.MetaNamespaceKeyFunc(svc) if err != nil { return false, err } e, exists, err := kd.endpointsStore.GetByKey(key) if err != nil { return false, fmt.Errorf("failed to get endpoints object from endpoints store - %v", err) } if !exists { return false, nil } if e, ok := e.(*v1.Endpoints); ok { return len(e.Subsets) > 0, nil } return false, fmt.Errorf("unexpected: found non-endpoint object in endpoint store: %v", e) }
// implementation of scheduling plugin's NextPod func; see k8s plugin/pkg/scheduler func (q *queuer) yield() *api.Pod { log.V(2).Info("attempting to yield a pod") q.lock.Lock() defer q.lock.Unlock() for { // limit blocking here to short intervals so that we don't block the // enqueuer Run() routine for very long kpod := q.podQueue.Await(yieldPopTimeout) if kpod == nil { signalled := runtime.After(q.unscheduledCond.Wait) // lock is yielded at this point and we're going to wait for either // a timeout, or a signal that there's data select { case <-time.After(yieldWaitTimeout): q.unscheduledCond.Broadcast() // abort Wait() <-signalled // wait for the go-routine, and the lock log.V(4).Infoln("timed out waiting for a pod to yield") case <-signalled: // we have acquired the lock, and there // may be a pod for us to pop now } continue } pod := kpod.(*Pod).Pod if podName, err := cache.MetaNamespaceKeyFunc(pod); err != nil { log.Warningf("yield unable to understand pod object %+v, will skip: %v", pod, err) } else if !q.podUpdates.Poll(podName, queue.POP_EVENT) { log.V(1).Infof("yield popped a transitioning pod, skipping: %+v", pod) } else if annotatedForExecutor(pod) { // should never happen if enqueuePods is filtering properly log.Warningf("yield popped an already-scheduled pod, skipping: %+v", pod) } else { return pod } } }
func getServiceFromEndpoints(serviceStore kubeCache.Store, e *kubeAPI.Endpoints) (*kubeAPI.Service, error) { var ( err error key string obj interface{} exists bool ok bool svc *kubeAPI.Service ) if key, err = kubeCache.MetaNamespaceKeyFunc(e); err != nil { return nil, err } if obj, exists, err = serviceStore.GetByKey(key); err != nil { return nil, fmt.Errorf("Error getting service object from services store - %v", err) } if !exists { log.WithFields(log.Fields{"name": e.Name, "namespace": e.Namespace}).Warn("Unable to find service for endpoint") return nil, nil } if svc, ok = obj.(*kubeAPI.Service); !ok { return nil, fmt.Errorf("got a non service object in services store %v", obj) } return svc, nil }
func (k2c *Kube2Consul) newHeadlessService(svc *kapi.Service) { key, err := kcache.MetaNamespaceKeyFunc(svc) if err != nil { glog.Errorf("MetaNamespaceKeyFunc gets key error: %v.", err) return } obj, exist, err := k2c.endpointsStore.GetByKey(key) if err != nil { glog.Errorf("faild to get endpoints from endpointsStore: %v.", err) return } if !exist { glog.Infof("could not find endpoints for service %q in namespace %q, will be registered once endpoints show up.", svc.Name, svc.Namespace) return } if eps, ok := obj.(*kapi.Endpoints); ok { k2c.registerHeadlessService(eps) } else { glog.Errorf("a none endpoints object in endpoints store: %v.", obj) } }
func getEndpointsForService(endpointsStore kubeCache.Store, s *kubeAPI.Service) (*kubeAPI.Endpoints, error) { var ( err error key string obj interface{} exists bool ok bool e *kubeAPI.Endpoints ) if key, err = kubeCache.MetaNamespaceKeyFunc(s); err != nil { return nil, err } if obj, exists, err = endpointsStore.GetByKey(key); err != nil { return nil, fmt.Errorf("Error getting endpoints object from endpoints store - %v", err) } if !exists { log.WithFields(log.Fields{"name": s.Name, "namespace": s.Namespace}).Warn("Unable to find endpoint for service") return nil, nil } if e, ok = obj.(*kubeAPI.Endpoints); !ok { return nil, fmt.Errorf("got a non endpoints object in endpoints store %v", obj) } return e, nil }
func DoTestUnschedulableNodes(t *testing.T, restClient *client.Client, nodeStore cache.Store) { goodCondition := api.NodeCondition{ Type: api.NodeReady, Status: api.ConditionTrue, Reason: fmt.Sprintf("schedulable condition"), LastHeartbeatTime: unversioned.Time{time.Now()}, } badCondition := api.NodeCondition{ Type: api.NodeReady, Status: api.ConditionUnknown, Reason: fmt.Sprintf("unschedulable condition"), LastHeartbeatTime: unversioned.Time{time.Now()}, } // Create a new schedulable node, since we're first going to apply // the unschedulable condition and verify that pods aren't scheduled. node := &api.Node{ ObjectMeta: api.ObjectMeta{Name: "node-scheduling-test-node"}, Spec: api.NodeSpec{Unschedulable: false}, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []api.NodeCondition{goodCondition}, }, } nodeKey, err := cache.MetaNamespaceKeyFunc(node) if err != nil { t.Fatalf("Couldn't retrieve key for node %v", node.Name) } // The test does the following for each nodeStateManager in this list: // 1. Create a new node // 2. Apply the makeUnSchedulable function // 3. Create a new pod // 4. Check that the pod doesn't get assigned to the node // 5. Apply the schedulable function // 6. Check that the pod *does* get assigned to the node // 7. Delete the pod and node. nodeModifications := []nodeStateManager{ // Test node.Spec.Unschedulable=true/false { makeUnSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) { n.Spec.Unschedulable = true if _, err := c.Nodes().Update(n); err != nil { t.Fatalf("Failed to update node with unschedulable=true: %v", err) } err = waitForReflection(s, nodeKey, func(node interface{}) bool { // An unschedulable node should get deleted from the store return node == nil }) if err != nil { t.Fatalf("Failed to observe reflected update for setting unschedulable=true: %v", err) } }, makeSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) { n.Spec.Unschedulable = false if _, err := c.Nodes().Update(n); err != nil { t.Fatalf("Failed to update node with unschedulable=false: %v", err) } err = waitForReflection(s, nodeKey, func(node interface{}) bool { return node != nil && node.(*api.Node).Spec.Unschedulable == false }) if err != nil { t.Fatalf("Failed to observe reflected update for setting unschedulable=false: %v", err) } }, }, // Test node.Status.Conditions=ConditionTrue/Unknown { makeUnSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) { n.Status = api.NodeStatus{ Capacity: api.ResourceList{ api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []api.NodeCondition{badCondition}, } if _, err = c.Nodes().UpdateStatus(n); err != nil { t.Fatalf("Failed to update node with bad status condition: %v", err) } err = waitForReflection(s, nodeKey, func(node interface{}) bool { return node != nil && node.(*api.Node).Status.Conditions[0].Status == api.ConditionUnknown }) if err != nil { t.Fatalf("Failed to observe reflected update for status condition update: %v", err) } }, makeSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) { n.Status = api.NodeStatus{ Capacity: api.ResourceList{ api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []api.NodeCondition{goodCondition}, } if _, err = c.Nodes().UpdateStatus(n); err != nil { t.Fatalf("Failed to update node with healthy status condition: %v", err) } waitForReflection(s, nodeKey, func(node interface{}) bool { return node != nil && node.(*api.Node).Status.Conditions[0].Status == api.ConditionTrue }) if err != nil { t.Fatalf("Failed to observe reflected update for status condition update: %v", err) } }, }, } for i, mod := range nodeModifications { unSchedNode, err := restClient.Nodes().Create(node) if err != nil { t.Fatalf("Failed to create node: %v", err) } // Apply the unschedulable modification to the node, and wait for the reflection mod.makeUnSchedulable(t, unSchedNode, nodeStore, restClient) // Create the new pod, note that this needs to happen post unschedulable // modification or we have a race in the test. pod := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "node-scheduling-test-pod"}, Spec: api.PodSpec{ Containers: []api.Container{{Name: "container", Image: "kubernetes/pause:go"}}, }, } myPod, err := restClient.Pods(api.NamespaceDefault).Create(pod) if err != nil { t.Fatalf("Failed to create pod: %v", err) } // There are no schedulable nodes - the pod shouldn't be scheduled. err = wait.Poll(time.Second, util.ForeverTestTimeout, podScheduled(restClient, myPod.Namespace, myPod.Name)) if err == nil { t.Errorf("Pod scheduled successfully on unschedulable nodes") } if err != wait.ErrWaitTimeout { t.Errorf("Test %d: failed while trying to confirm the pod does not get scheduled on the node: %v", i, err) } else { t.Logf("Test %d: Pod did not get scheduled on an unschedulable node", i) } // Apply the schedulable modification to the node, and wait for the reflection schedNode, err := restClient.Nodes().Get(unSchedNode.Name) if err != nil { t.Fatalf("Failed to get node: %v", err) } mod.makeSchedulable(t, schedNode, nodeStore, restClient) // Wait until the pod is scheduled. err = wait.Poll(time.Second, util.ForeverTestTimeout, podScheduled(restClient, myPod.Namespace, myPod.Name)) if err != nil { t.Errorf("Test %d: failed to schedule a pod: %v", i, err) } else { t.Logf("Test %d: Pod got scheduled on a schedulable node", i) } err = restClient.Pods(api.NamespaceDefault).Delete(myPod.Name, api.NewDeleteOptions(0)) if err != nil { t.Errorf("Failed to delete pod: %v", err) } err = restClient.Nodes().Delete(schedNode.Name) if err != nil { t.Errorf("Failed to delete node: %v", err) } } }
// getPodKey returns the string key of a pod. func getPodKey(pod *v1.Pod) (string, error) { return clientcache.MetaNamespaceKeyFunc(pod) }
func objectArgumentsKeyFunc(obj interface{}) (string, error) { if args, ok := obj.(objectArguments); ok { return args.key, nil } return cache.MetaNamespaceKeyFunc(obj) }
// calculateArguments determines the arguments for a give delta and updates the argument store, or returns // an error. If the object can be transformed into a full JSON object, that is also returned. func (o *ObserveOptions) calculateArguments(delta cache.Delta) (runtime.Object, []string, []byte, error) { var arguments []string var object runtime.Object var key string var output []byte switch t := delta.Object.(type) { case cache.DeletedFinalStateUnknown: key = t.Key if obj, ok := t.Obj.(runtime.Object); ok { object = obj args, data, err := o.printer.Print(obj) if err != nil { return nil, nil, nil, fmt.Errorf("unable to write arguments: %v", err) } arguments = args output = data } else { value, _, err := o.argumentStore.GetByKey(key) if err != nil { return nil, nil, nil, err } if value != nil { args, ok := value.(objectArguments) if !ok { return nil, nil, nil, fmt.Errorf("unexpected cache value %T", value) } arguments = args.arguments output = args.output } } o.argumentStore.Remove(key) case runtime.Object: object = t args, data, err := o.printer.Print(t) if err != nil { return nil, nil, nil, fmt.Errorf("unable to write arguments: %v", err) } arguments = args output = data key, _ = cache.MetaNamespaceKeyFunc(t) if delta.Type == cache.Deleted { o.argumentStore.Remove(key) } else { saved := objectArguments{key: key, arguments: arguments} // only cache the object data if the commands will be using it. if len(o.objectEnvVar) > 0 { saved.output = output } o.argumentStore.Put(key, saved) } case objectArguments: key = t.key arguments = t.arguments output = t.output default: return nil, nil, nil, fmt.Errorf("unrecognized object %T from cache store", delta.Object) } if object == nil { namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { return nil, nil, nil, err } unstructured := &runtime.Unstructured{} unstructured.SetNamespace(namespace) unstructured.SetName(name) object = unstructured } return object, arguments, output, nil }
// Via integration test we can verify that if pod delete // event is somehow missed by AttachDetach controller - it still // gets cleaned up by Desired State of World populator. func TestPodDeletionWithDswp(t *testing.T) { _, server := framework.RunAMaster(nil) defer server.Close() namespaceName := "test-pod-deletion" node := &v1.Node{ ObjectMeta: v1.ObjectMeta{ Name: "node-sandbox", Annotations: map[string]string{ volumehelper.ControllerManagedAttachAnnotation: "true", }, }, } ns := framework.CreateTestingNamespace(namespaceName, server, t) defer framework.DeleteTestingNamespace(ns, server, t) testClient, ctrl, podInformer, nodeInformer := createAdClients(ns, t, server, defaultSyncPeriod) pod := fakePodWithVol(namespaceName) podStopCh := make(chan struct{}) if _, err := testClient.Core().Nodes().Create(node); err != nil { t.Fatalf("Failed to created node : %v", err) } go nodeInformer.Run(podStopCh) if _, err := testClient.Core().Pods(ns.Name).Create(pod); err != nil { t.Errorf("Failed to create pod : %v", err) } go podInformer.Run(podStopCh) // start controller loop stopCh := make(chan struct{}) go ctrl.Run(stopCh) waitToObservePods(t, podInformer, 1) podKey, err := cache.MetaNamespaceKeyFunc(pod) if err != nil { t.Fatalf("MetaNamespaceKeyFunc failed with : %v", err) } podInformerObj, _, err := podInformer.GetStore().GetByKey(podKey) if err != nil { t.Fatalf("Pod not found in Pod Informer cache : %v", err) } podsToAdd := ctrl.GetDesiredStateOfWorld().GetPodToAdd() if len(podsToAdd) == 0 { t.Fatalf("Pod not added to desired state of world") } // let's stop pod events from getting triggered close(podStopCh) err = podInformer.GetStore().Delete(podInformerObj) if err != nil { t.Fatalf("Error deleting pod : %v", err) } waitToObservePods(t, podInformer, 0) // the populator loop turns every 1 minute time.Sleep(80 * time.Second) podsToAdd = ctrl.GetDesiredStateOfWorld().GetPodToAdd() if len(podsToAdd) != 0 { t.Fatalf("All pods should have been removed") } close(stopCh) }
func DoTestUnschedulableNodes(t *testing.T, cs clientset.Interface, ns *v1.Namespace, nodeStore cache.Store) { // NOTE: This test cannot run in parallel, because it is creating and deleting // non-namespaced objects (Nodes). defer cs.Core().Nodes().DeleteCollection(nil, v1.ListOptions{}) goodCondition := v1.NodeCondition{ Type: v1.NodeReady, Status: v1.ConditionTrue, Reason: fmt.Sprintf("schedulable condition"), LastHeartbeatTime: metav1.Time{time.Now()}, } badCondition := v1.NodeCondition{ Type: v1.NodeReady, Status: v1.ConditionUnknown, Reason: fmt.Sprintf("unschedulable condition"), LastHeartbeatTime: metav1.Time{time.Now()}, } // Create a new schedulable node, since we're first going to apply // the unschedulable condition and verify that pods aren't scheduled. node := &v1.Node{ ObjectMeta: v1.ObjectMeta{Name: "node-scheduling-test-node"}, Spec: v1.NodeSpec{Unschedulable: false}, Status: v1.NodeStatus{ Capacity: v1.ResourceList{ v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []v1.NodeCondition{goodCondition}, }, } nodeKey, err := cache.MetaNamespaceKeyFunc(node) if err != nil { t.Fatalf("Couldn't retrieve key for node %v", node.Name) } // The test does the following for each nodeStateManager in this list: // 1. Create a new node // 2. Apply the makeUnSchedulable function // 3. Create a new pod // 4. Check that the pod doesn't get assigned to the node // 5. Apply the schedulable function // 6. Check that the pod *does* get assigned to the node // 7. Delete the pod and node. nodeModifications := []nodeStateManager{ // Test node.Spec.Unschedulable=true/false { makeUnSchedulable: func(t *testing.T, n *v1.Node, s cache.Store, c clientset.Interface) { n.Spec.Unschedulable = true if _, err := c.Core().Nodes().Update(n); err != nil { t.Fatalf("Failed to update node with unschedulable=true: %v", err) } err = waitForReflection(t, s, nodeKey, func(node interface{}) bool { // An unschedulable node should still be present in the store // Nodes that are unschedulable or that are not ready or // have their disk full (Node.Spec.Conditions) are exluded // based on NodeConditionPredicate, a separate check return node != nil && node.(*v1.Node).Spec.Unschedulable == true }) if err != nil { t.Fatalf("Failed to observe reflected update for setting unschedulable=true: %v", err) } }, makeSchedulable: func(t *testing.T, n *v1.Node, s cache.Store, c clientset.Interface) { n.Spec.Unschedulable = false if _, err := c.Core().Nodes().Update(n); err != nil { t.Fatalf("Failed to update node with unschedulable=false: %v", err) } err = waitForReflection(t, s, nodeKey, func(node interface{}) bool { return node != nil && node.(*v1.Node).Spec.Unschedulable == false }) if err != nil { t.Fatalf("Failed to observe reflected update for setting unschedulable=false: %v", err) } }, }, // Test node.Status.Conditions=ConditionTrue/Unknown { makeUnSchedulable: func(t *testing.T, n *v1.Node, s cache.Store, c clientset.Interface) { n.Status = v1.NodeStatus{ Capacity: v1.ResourceList{ v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []v1.NodeCondition{badCondition}, } if _, err = c.Core().Nodes().UpdateStatus(n); err != nil { t.Fatalf("Failed to update node with bad status condition: %v", err) } err = waitForReflection(t, s, nodeKey, func(node interface{}) bool { return node != nil && node.(*v1.Node).Status.Conditions[0].Status == v1.ConditionUnknown }) if err != nil { t.Fatalf("Failed to observe reflected update for status condition update: %v", err) } }, makeSchedulable: func(t *testing.T, n *v1.Node, s cache.Store, c clientset.Interface) { n.Status = v1.NodeStatus{ Capacity: v1.ResourceList{ v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []v1.NodeCondition{goodCondition}, } if _, err = c.Core().Nodes().UpdateStatus(n); err != nil { t.Fatalf("Failed to update node with healthy status condition: %v", err) } err = waitForReflection(t, s, nodeKey, func(node interface{}) bool { return node != nil && node.(*v1.Node).Status.Conditions[0].Status == v1.ConditionTrue }) if err != nil { t.Fatalf("Failed to observe reflected update for status condition update: %v", err) } }, }, } for i, mod := range nodeModifications { unSchedNode, err := cs.Core().Nodes().Create(node) if err != nil { t.Fatalf("Failed to create node: %v", err) } // Apply the unschedulable modification to the node, and wait for the reflection mod.makeUnSchedulable(t, unSchedNode, nodeStore, cs) // Create the new pod, note that this needs to happen post unschedulable // modification or we have a race in the test. pod := &v1.Pod{ ObjectMeta: v1.ObjectMeta{Name: "node-scheduling-test-pod"}, Spec: v1.PodSpec{ Containers: []v1.Container{{Name: "container", Image: e2e.GetPauseImageName(cs)}}, }, } myPod, err := cs.Core().Pods(ns.Name).Create(pod) if err != nil { t.Fatalf("Failed to create pod: %v", err) } // There are no schedulable nodes - the pod shouldn't be scheduled. err = wait.Poll(time.Second, wait.ForeverTestTimeout, podScheduled(cs, myPod.Namespace, myPod.Name)) if err == nil { t.Errorf("Pod scheduled successfully on unschedulable nodes") } if err != wait.ErrWaitTimeout { t.Errorf("Test %d: failed while trying to confirm the pod does not get scheduled on the node: %v", i, err) } else { t.Logf("Test %d: Pod did not get scheduled on an unschedulable node", i) } // Apply the schedulable modification to the node, and wait for the reflection schedNode, err := cs.Core().Nodes().Get(unSchedNode.Name) if err != nil { t.Fatalf("Failed to get node: %v", err) } mod.makeSchedulable(t, schedNode, nodeStore, cs) // Wait until the pod is scheduled. err = wait.Poll(time.Second, wait.ForeverTestTimeout, podScheduled(cs, myPod.Namespace, myPod.Name)) if err != nil { t.Errorf("Test %d: failed to schedule a pod: %v", i, err) } else { t.Logf("Test %d: Pod got scheduled on a schedulable node", i) } err = cs.Core().Pods(ns.Name).Delete(myPod.Name, v1.NewDeleteOptions(0)) if err != nil { t.Errorf("Failed to delete pod: %v", err) } err = cs.Core().Nodes().Delete(schedNode.Name, nil) if err != nil { t.Errorf("Failed to delete node: %v", err) } } }
// DeletionHandlingMetaNamespaceKeyFunc checks for // cache.DeletedFinalStateUnknown objects before calling // cache.MetaNamespaceKeyFunc. func DeletionHandlingMetaNamespaceKeyFunc(obj interface{}) (string, error) { if d, ok := obj.(cache.DeletedFinalStateUnknown); ok { return d.Key, nil } return cache.MetaNamespaceKeyFunc(obj) }