func (p *podNodeSelector) getNodeSelectorMap(namespace *api.Namespace) (labels.Set, error) { selector := labels.Set{} labelsMap := labels.Set{} var err error found := false if len(namespace.ObjectMeta.Annotations) > 0 { for _, annotation := range NamespaceNodeSelectors { if ns, ok := namespace.ObjectMeta.Annotations[annotation]; ok { labelsMap, err = labels.ConvertSelectorToLabelsMap(ns) if err != nil { return labels.Set{}, err } if labels.Conflicts(selector, labelsMap) { nsName := namespace.ObjectMeta.Name return labels.Set{}, fmt.Errorf("%s annotations' node label selectors conflict", nsName) } selector = labels.Merge(selector, labelsMap) found = true } } } if !found { selector, err = labels.ConvertSelectorToLabelsMap(p.clusterNodeSelectors["clusterDefaultNodeSelector"]) if err != nil { return labels.Set{}, err } } return selector, nil }
// Admit enforces that pod and its namespace node label selectors matches at least a node in the cluster. func (p *podNodeSelector) Admit(a admission.Attributes) error { resource := a.GetResource().GroupResource() if resource != api.Resource("pods") { return nil } if a.GetSubresource() != "" { // only run the checks below on pods proper and not subresources return nil } obj := a.GetObject() pod, ok := obj.(*api.Pod) if !ok { glog.Errorf("expected pod but got %s", a.GetKind().Kind) return nil } if !p.WaitForReady() { return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) } name := pod.Name nsName := a.GetNamespace() var namespace *api.Namespace namespaceObj, exists, err := p.namespaceInformer.GetStore().Get(&api.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: nsName, Namespace: "", }, }) if err != nil { return errors.NewInternalError(err) } if exists { namespace = namespaceObj.(*api.Namespace) } else { namespace, err = p.defaultGetNamespace(nsName) if err != nil { if errors.IsNotFound(err) { return err } return errors.NewInternalError(err) } } namespaceNodeSelector, err := p.getNodeSelectorMap(namespace) if err != nil { return err } if labels.Conflicts(namespaceNodeSelector, labels.Set(pod.Spec.NodeSelector)) { return errors.NewForbidden(resource, name, fmt.Errorf("pod node label selector conflicts with its namespace node label selector")) } whitelist, err := labels.ConvertSelectorToLabelsMap(p.clusterNodeSelectors[namespace.Name]) if err != nil { return err } // Merge pod node selector = namespace node selector + current pod node selector podNodeSelectorLabels := labels.Merge(namespaceNodeSelector, pod.Spec.NodeSelector) // whitelist verification if !labels.AreLabelsInWhiteList(podNodeSelectorLabels, whitelist) { return errors.NewForbidden(resource, name, fmt.Errorf("pod node label selector labels conflict with its namespace whitelist")) } // Updated pod node selector = namespace node selector + current pod node selector pod.Spec.NodeSelector = map[string]string(podNodeSelectorLabels) return nil }