2015-04-21 20:40:35 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
2015-04-21 20:40:35 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package controller
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-07-28 01:21:37 +00:00
|
|
|
"time"
|
2015-04-30 17:58:18 +00:00
|
|
|
|
2015-08-27 17:18:01 +00:00
|
|
|
"sync/atomic"
|
|
|
|
|
2015-08-05 22:05:17 +00:00
|
|
|
"github.com/golang/glog"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api"
|
|
|
|
"k8s.io/kubernetes/pkg/api/latest"
|
|
|
|
"k8s.io/kubernetes/pkg/api/validation"
|
2015-09-03 21:40:58 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/cache"
|
|
|
|
"k8s.io/kubernetes/pkg/client/record"
|
2015-09-03 21:43:19 +00:00
|
|
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/controller/framework"
|
2015-08-27 17:18:01 +00:00
|
|
|
"k8s.io/kubernetes/pkg/expapi"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/labels"
|
|
|
|
"k8s.io/kubernetes/pkg/runtime"
|
2015-04-21 20:40:35 +00:00
|
|
|
)
|
|
|
|
|
2015-05-01 15:49:06 +00:00
|
|
|
const (
|
|
|
|
CreatedByAnnotation = "kubernetes.io/created-by"
|
2015-07-28 01:21:37 +00:00
|
|
|
|
|
|
|
// If a watch drops a delete event for a pod, it'll take this long
|
|
|
|
// before a dormant controller waiting for those packets is woken up anyway. It is
|
|
|
|
// specifically targeted at the case where some problem prevents an update
|
|
|
|
// of expectations, without it the controller could stay asleep forever. This should
|
|
|
|
// be set based on the expected latency of watch events.
|
|
|
|
//
|
|
|
|
// Currently an controller can service (create *and* observe the watch events for said
|
|
|
|
// creation) about 10-20 pods a second, so it takes about 1 min to service
|
|
|
|
// 500 pods. Just creation is limited to 20qps, and watching happens with ~10-30s
|
|
|
|
// latency/pod at the scale of 3000 pods over 100 nodes.
|
|
|
|
ExpectationsTimeout = 3 * time.Minute
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
KeyFunc = framework.DeletionHandlingMetaNamespaceKeyFunc
|
2015-05-01 15:49:06 +00:00
|
|
|
)
|
2015-04-21 20:40:35 +00:00
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// Expectations are a way for controllers to tell the controller manager what they expect. eg:
|
|
|
|
// ControllerExpectations: {
|
|
|
|
// controller1: expects 2 adds in 2 minutes
|
|
|
|
// controller2: expects 2 dels in 2 minutes
|
|
|
|
// controller3: expects -1 adds in 2 minutes => controller3's expectations have already been met
|
2015-04-21 20:40:35 +00:00
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Implementation:
|
|
|
|
// PodExpectation = pair of atomic counters to track pod creation/deletion
|
2015-07-28 01:21:37 +00:00
|
|
|
// ControllerExpectationsStore = TTLStore + a PodExpectation per controller
|
2015-04-21 20:40:35 +00:00
|
|
|
//
|
|
|
|
// * Once set expectations can only be lowered
|
2015-07-28 01:21:37 +00:00
|
|
|
// * A controller isn't synced till its expectations are either fulfilled, or expire
|
|
|
|
// * Controllers that don't set expectations will get woken up for every matching pod
|
2015-04-21 20:40:35 +00:00
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// ExpKeyFunc to parse out the key from a PodExpectation
|
|
|
|
var ExpKeyFunc = func(obj interface{}) (string, error) {
|
2015-04-21 20:40:35 +00:00
|
|
|
if e, ok := obj.(*PodExpectations); ok {
|
|
|
|
return e.key, nil
|
|
|
|
}
|
|
|
|
return "", fmt.Errorf("Could not find key for obj %#v", obj)
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// ControllerExpectationsInterface is an interface that allows users to set and wait on expectations.
|
2015-05-12 21:39:23 +00:00
|
|
|
// Only abstracted out for testing.
|
2015-07-28 01:21:37 +00:00
|
|
|
// Warning: if using KeyFunc it is not safe to use a single ControllerExpectationsInterface with different
|
|
|
|
// types of controllers, because the keys might conflict across types.
|
|
|
|
type ControllerExpectationsInterface interface {
|
|
|
|
GetExpectations(controllerKey string) (*PodExpectations, bool, error)
|
|
|
|
SatisfiedExpectations(controllerKey string) bool
|
|
|
|
DeleteExpectations(controllerKey string)
|
|
|
|
SetExpectations(controllerKey string, add, del int) error
|
|
|
|
ExpectCreations(controllerKey string, adds int) error
|
|
|
|
ExpectDeletions(controllerKey string, dels int) error
|
|
|
|
CreationObserved(controllerKey string)
|
|
|
|
DeletionObserved(controllerKey string)
|
2015-05-12 21:39:23 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// ControllerExpectations is a ttl cache mapping controllers to what they expect to see before being woken up for a sync.
|
|
|
|
type ControllerExpectations struct {
|
2015-04-21 20:40:35 +00:00
|
|
|
cache.Store
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// GetExpectations returns the PodExpectations of the given controller.
|
|
|
|
func (r *ControllerExpectations) GetExpectations(controllerKey string) (*PodExpectations, bool, error) {
|
|
|
|
if podExp, exists, err := r.GetByKey(controllerKey); err == nil && exists {
|
2015-04-21 20:40:35 +00:00
|
|
|
return podExp.(*PodExpectations), true, nil
|
|
|
|
} else {
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// DeleteExpectations deletes the expectations of the given controller from the TTLStore.
|
|
|
|
func (r *ControllerExpectations) DeleteExpectations(controllerKey string) {
|
|
|
|
if podExp, exists, err := r.GetByKey(controllerKey); err == nil && exists {
|
2015-05-08 21:16:58 +00:00
|
|
|
if err := r.Delete(podExp); err != nil {
|
2015-07-28 01:21:37 +00:00
|
|
|
glog.V(2).Infof("Error deleting expectations for controller %v: %v", controllerKey, err)
|
2015-05-08 21:16:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// SatisfiedExpectations returns true if the required adds/dels for the given controller have been observed.
|
|
|
|
// Add/del counts are established by the controller at sync time, and updated as pods are observed by the controller
|
|
|
|
// manager.
|
|
|
|
func (r *ControllerExpectations) SatisfiedExpectations(controllerKey string) bool {
|
|
|
|
if podExp, exists, err := r.GetExpectations(controllerKey); exists {
|
2015-04-21 20:40:35 +00:00
|
|
|
if podExp.Fulfilled() {
|
|
|
|
return true
|
|
|
|
} else {
|
2015-05-06 21:39:14 +00:00
|
|
|
glog.V(4).Infof("Controller still waiting on expectations %#v", podExp)
|
2015-04-21 20:40:35 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
glog.V(2).Infof("Error encountered while checking expectations %#v, forcing sync", err)
|
|
|
|
} else {
|
2015-07-28 01:21:37 +00:00
|
|
|
// When a new controller is created, it doesn't have expectations.
|
2015-04-21 20:40:35 +00:00
|
|
|
// When it doesn't see expected watch events for > TTL, the expectations expire.
|
|
|
|
// - In this case it wakes up, creates/deletes pods, and sets expectations again.
|
|
|
|
// When it has satisfied expectations and no pods need to be created/destroyed > TTL, the expectations expire.
|
|
|
|
// - In this case it continues without setting expectations till it needs to create/delete pods.
|
2015-07-28 01:21:37 +00:00
|
|
|
glog.V(4).Infof("Controller %v either never recorded expectations, or the ttl expired.", controllerKey)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
// Trigger a sync if we either encountered and error (which shouldn't happen since we're
|
2015-07-28 01:21:37 +00:00
|
|
|
// getting from local store) or this controller hasn't established expectations.
|
2015-04-21 20:40:35 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// SetExpectations registers new expectations for the given controller. Forgets existing expectations.
|
|
|
|
func (r *ControllerExpectations) SetExpectations(controllerKey string, add, del int) error {
|
|
|
|
podExp := &PodExpectations{add: int64(add), del: int64(del), key: controllerKey}
|
2015-04-30 17:58:18 +00:00
|
|
|
glog.V(4).Infof("Setting expectations %+v", podExp)
|
|
|
|
return r.Add(podExp)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
func (r *ControllerExpectations) ExpectCreations(controllerKey string, adds int) error {
|
|
|
|
return r.SetExpectations(controllerKey, adds, 0)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
func (r *ControllerExpectations) ExpectDeletions(controllerKey string, dels int) error {
|
|
|
|
return r.SetExpectations(controllerKey, 0, dels)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// Decrements the expectation counts of the given controller.
|
|
|
|
func (r *ControllerExpectations) lowerExpectations(controllerKey string, add, del int) {
|
|
|
|
if podExp, exists, err := r.GetExpectations(controllerKey); err == nil && exists {
|
2015-04-21 20:40:35 +00:00
|
|
|
podExp.Seen(int64(add), int64(del))
|
2015-04-30 17:58:18 +00:00
|
|
|
// The expectations might've been modified since the update on the previous line.
|
|
|
|
glog.V(4).Infof("Lowering expectations %+v", podExp)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// CreationObserved atomically decrements the `add` expecation count of the given controller.
|
|
|
|
func (r *ControllerExpectations) CreationObserved(controllerKey string) {
|
|
|
|
r.lowerExpectations(controllerKey, 1, 0)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// DeletionObserved atomically decrements the `del` expectation count of the given controller.
|
|
|
|
func (r *ControllerExpectations) DeletionObserved(controllerKey string) {
|
|
|
|
r.lowerExpectations(controllerKey, 0, 1)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Expectations are either fulfilled, or expire naturally.
|
|
|
|
type Expectations interface {
|
|
|
|
Fulfilled() bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// PodExpectations track pod creates/deletes.
|
|
|
|
type PodExpectations struct {
|
|
|
|
add int64
|
|
|
|
del int64
|
|
|
|
key string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seen decrements the add and del counters.
|
|
|
|
func (e *PodExpectations) Seen(add, del int64) {
|
|
|
|
atomic.AddInt64(&e.add, -add)
|
|
|
|
atomic.AddInt64(&e.del, -del)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fulfilled returns true if this expectation has been fulfilled.
|
|
|
|
func (e *PodExpectations) Fulfilled() bool {
|
|
|
|
// TODO: think about why this line being atomic doesn't matter
|
|
|
|
return atomic.LoadInt64(&e.add) <= 0 && atomic.LoadInt64(&e.del) <= 0
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// GetExpectations returns the add and del expectations of the pod.
|
|
|
|
func (e *PodExpectations) GetExpectations() (int64, int64) {
|
2015-05-06 22:14:41 +00:00
|
|
|
return atomic.LoadInt64(&e.add), atomic.LoadInt64(&e.del)
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// NewControllerExpectations returns a store for PodExpectations.
|
|
|
|
func NewControllerExpectations() *ControllerExpectations {
|
|
|
|
return &ControllerExpectations{cache.NewTTLStore(ExpKeyFunc, ExpectationsTimeout)}
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PodControlInterface is an interface that knows how to add or delete pods
|
|
|
|
// created as an interface to allow testing.
|
|
|
|
type PodControlInterface interface {
|
2015-07-28 01:21:37 +00:00
|
|
|
// CreateReplica creates new replicated pods according to the spec.
|
|
|
|
CreateReplica(namespace string, controller *api.ReplicationController) error
|
2015-08-27 17:18:01 +00:00
|
|
|
// CreateReplicaOnNodes creates a new pod according to the spec, on a specified list of nodes.
|
|
|
|
CreateReplicaOnNode(namespace string, controller *expapi.DaemonSet, nodeNames string) error
|
2015-07-28 01:21:37 +00:00
|
|
|
// DeletePod deletes the pod identified by podID.
|
|
|
|
DeletePod(namespace string, podID string) error
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// RealPodControl is the default implementation of PodControllerInterface.
|
|
|
|
type RealPodControl struct {
|
2015-07-28 01:21:37 +00:00
|
|
|
KubeClient client.Interface
|
|
|
|
Recorder record.EventRecorder
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
func getReplicaLabelSet(template *api.PodTemplateSpec) labels.Set {
|
2015-04-21 20:40:35 +00:00
|
|
|
desiredLabels := make(labels.Set)
|
2015-07-28 01:21:37 +00:00
|
|
|
for k, v := range template.Labels {
|
2015-04-21 20:40:35 +00:00
|
|
|
desiredLabels[k] = v
|
|
|
|
}
|
2015-07-28 01:21:37 +00:00
|
|
|
return desiredLabels
|
|
|
|
}
|
|
|
|
|
|
|
|
func getReplicaAnnotationSet(template *api.PodTemplateSpec, object runtime.Object) (labels.Set, error) {
|
2015-04-21 20:40:35 +00:00
|
|
|
desiredAnnotations := make(labels.Set)
|
2015-07-28 01:21:37 +00:00
|
|
|
for k, v := range template.Annotations {
|
2015-04-21 20:40:35 +00:00
|
|
|
desiredAnnotations[k] = v
|
|
|
|
}
|
2015-07-28 01:21:37 +00:00
|
|
|
createdByRef, err := api.GetReference(object)
|
2015-04-21 20:40:35 +00:00
|
|
|
if err != nil {
|
2015-07-28 01:21:37 +00:00
|
|
|
return desiredAnnotations, fmt.Errorf("unable to get controller reference: %v", err)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
2015-04-25 05:26:52 +00:00
|
|
|
createdByRefJson, err := latest.Codec.Encode(&api.SerializedReference{
|
|
|
|
Reference: *createdByRef,
|
|
|
|
})
|
2015-04-21 20:40:35 +00:00
|
|
|
if err != nil {
|
2015-07-28 01:21:37 +00:00
|
|
|
return desiredAnnotations, fmt.Errorf("unable to serialize controller reference: %v", err)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
desiredAnnotations[CreatedByAnnotation] = string(createdByRefJson)
|
2015-07-28 01:21:37 +00:00
|
|
|
return desiredAnnotations, nil
|
|
|
|
}
|
2015-04-21 20:40:35 +00:00
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
func getReplicaPrefix(controllerName string) string {
|
2015-04-21 20:40:35 +00:00
|
|
|
// use the dash (if the name isn't too long) to make the pod name a bit prettier
|
2015-07-28 01:21:37 +00:00
|
|
|
prefix := fmt.Sprintf("%s-", controllerName)
|
2015-04-21 20:40:35 +00:00
|
|
|
if ok, _ := validation.ValidatePodName(prefix, true); !ok {
|
2015-07-28 01:21:37 +00:00
|
|
|
prefix = controllerName
|
|
|
|
}
|
|
|
|
return prefix
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RealPodControl) CreateReplica(namespace string, controller *api.ReplicationController) error {
|
|
|
|
desiredLabels := getReplicaLabelSet(controller.Spec.Template)
|
|
|
|
desiredAnnotations, err := getReplicaAnnotationSet(controller.Spec.Template, controller)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
2015-07-28 01:21:37 +00:00
|
|
|
prefix := getReplicaPrefix(controller.Name)
|
2015-04-21 20:40:35 +00:00
|
|
|
|
|
|
|
pod := &api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Labels: desiredLabels,
|
|
|
|
Annotations: desiredAnnotations,
|
|
|
|
GenerateName: prefix,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := api.Scheme.Convert(&controller.Spec.Template.Spec, &pod.Spec); err != nil {
|
|
|
|
return fmt.Errorf("unable to convert pod template: %v", err)
|
|
|
|
}
|
|
|
|
if labels.Set(pod.Labels).AsSelector().Empty() {
|
|
|
|
return fmt.Errorf("unable to create pod replica, no labels")
|
|
|
|
}
|
2015-07-28 01:21:37 +00:00
|
|
|
if newPod, err := r.KubeClient.Pods(namespace).Create(pod); err != nil {
|
2015-08-11 17:05:53 +00:00
|
|
|
r.Recorder.Eventf(controller, "FailedCreate", "Error creating: %v", err)
|
2015-04-21 20:40:35 +00:00
|
|
|
return fmt.Errorf("unable to create pod replica: %v", err)
|
|
|
|
} else {
|
|
|
|
glog.V(4).Infof("Controller %v created pod %v", controller.Name, newPod.Name)
|
2015-08-11 17:05:53 +00:00
|
|
|
r.Recorder.Eventf(controller, "SuccessfulCreate", "Created pod: %v", newPod.Name)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-08-27 17:18:01 +00:00
|
|
|
func (r RealPodControl) CreateReplicaOnNode(namespace string, controller *expapi.DaemonSet, nodeName string) error {
|
|
|
|
desiredLabels := getReplicaLabelSet(controller.Spec.Template)
|
|
|
|
desiredAnnotations, err := getReplicaAnnotationSet(controller.Spec.Template, controller)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
prefix := getReplicaPrefix(controller.Name)
|
|
|
|
|
|
|
|
pod := &api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Labels: desiredLabels,
|
|
|
|
Annotations: desiredAnnotations,
|
|
|
|
GenerateName: prefix,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := api.Scheme.Convert(&controller.Spec.Template.Spec, &pod.Spec); err != nil {
|
|
|
|
return fmt.Errorf("unable to convert pod template: %v", err)
|
|
|
|
}
|
|
|
|
if labels.Set(pod.Labels).AsSelector().Empty() {
|
|
|
|
return fmt.Errorf("unable to create pod replica, no labels")
|
|
|
|
}
|
|
|
|
pod.Spec.NodeName = nodeName
|
|
|
|
if newPod, err := r.KubeClient.Pods(namespace).Create(pod); err != nil {
|
|
|
|
r.Recorder.Eventf(controller, "failedCreate", "Error creating: %v", err)
|
|
|
|
return fmt.Errorf("unable to create pod replica: %v", err)
|
|
|
|
} else {
|
|
|
|
glog.V(4).Infof("Controller %v created pod %v", controller.Name, newPod.Name)
|
|
|
|
r.Recorder.Eventf(controller, "successfulCreate", "Created pod: %v", newPod.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
func (r RealPodControl) DeletePod(namespace, podID string) error {
|
|
|
|
return r.KubeClient.Pods(namespace).Delete(podID, nil)
|
2015-04-21 20:40:35 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// ActivePods type allows custom sorting of pods so a controller can pick the best ones to delete.
|
|
|
|
type ActivePods []*api.Pod
|
2015-04-21 20:40:35 +00:00
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
func (s ActivePods) Len() int { return len(s) }
|
|
|
|
func (s ActivePods) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
2015-04-21 20:40:35 +00:00
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
func (s ActivePods) Less(i, j int) bool {
|
2015-04-21 20:40:35 +00:00
|
|
|
// Unassigned < assigned
|
2015-05-22 23:40:57 +00:00
|
|
|
if s[i].Spec.NodeName == "" && s[j].Spec.NodeName != "" {
|
2015-04-21 20:40:35 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
// PodPending < PodUnknown < PodRunning
|
|
|
|
m := map[api.PodPhase]int{api.PodPending: 0, api.PodUnknown: 1, api.PodRunning: 2}
|
|
|
|
if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
|
|
|
|
return m[s[i].Status.Phase] < m[s[j].Status.Phase]
|
|
|
|
}
|
|
|
|
// Not ready < ready
|
|
|
|
if !api.IsPodReady(s[i]) && api.IsPodReady(s[j]) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2015-07-28 01:21:37 +00:00
|
|
|
// FilterActivePods returns pods that have not terminated.
|
|
|
|
func FilterActivePods(pods []api.Pod) []*api.Pod {
|
2015-04-21 20:40:35 +00:00
|
|
|
var result []*api.Pod
|
|
|
|
for i := range pods {
|
|
|
|
if api.PodSucceeded != pods[i].Status.Phase &&
|
2015-08-20 01:52:34 +00:00
|
|
|
api.PodFailed != pods[i].Status.Phase &&
|
|
|
|
pods[i].DeletionTimestamp == nil {
|
2015-04-21 20:40:35 +00:00
|
|
|
result = append(result, &pods[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|