extensions: add minReadySeconds/availableReplicas to replica sets
This commit is contained in:
parent
c1e8c6d878
commit
f7c232b8c6
@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
)
|
)
|
||||||
@ -74,6 +76,24 @@ func GetExistingContainerStatus(statuses []ContainerStatus, name string) Contain
|
|||||||
return ContainerStatus{}
|
return ContainerStatus{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPodAvailable returns true if a pod is available; false otherwise.
|
||||||
|
// Precondition for an available pod is that it must be ready. On top
|
||||||
|
// of that, there are two cases when a pod can be considered available:
|
||||||
|
// 1. minReadySeconds == 0, or
|
||||||
|
// 2. LastTransitionTime (is set) + minReadySeconds < current time
|
||||||
|
func IsPodAvailable(pod *Pod, minReadySeconds int32, now unversioned.Time) bool {
|
||||||
|
if !IsPodReady(pod) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
c := GetPodReadyCondition(pod.Status)
|
||||||
|
minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second
|
||||||
|
if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// IsPodReady returns true if a pod is ready; false otherwise.
|
// IsPodReady returns true if a pod is ready; false otherwise.
|
||||||
func IsPodReady(pod *Pod) bool {
|
func IsPodReady(pod *Pod) bool {
|
||||||
return IsPodReadyConditionTrue(pod.Status)
|
return IsPodReadyConditionTrue(pod.Status)
|
||||||
|
@ -18,8 +18,10 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResourceHelpers(t *testing.T) {
|
func TestResourceHelpers(t *testing.T) {
|
||||||
@ -61,3 +63,58 @@ func TestDefaultResourceHelpers(t *testing.T) {
|
|||||||
t.Errorf("expected %v, actual %v", resource.BinarySI, resourceList.Memory().Format)
|
t.Errorf("expected %v, actual %v", resource.BinarySI, resourceList.Memory().Format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newPod(now unversioned.Time, ready bool, beforeSec int) *Pod {
|
||||||
|
conditionStatus := ConditionFalse
|
||||||
|
if ready {
|
||||||
|
conditionStatus = ConditionTrue
|
||||||
|
}
|
||||||
|
return &Pod{
|
||||||
|
Status: PodStatus{
|
||||||
|
Conditions: []PodCondition{
|
||||||
|
{
|
||||||
|
Type: PodReady,
|
||||||
|
LastTransitionTime: unversioned.NewTime(now.Time.Add(-1 * time.Duration(beforeSec) * time.Second)),
|
||||||
|
Status: conditionStatus,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsPodAvailable(t *testing.T) {
|
||||||
|
now := unversioned.Now()
|
||||||
|
tests := []struct {
|
||||||
|
pod *Pod
|
||||||
|
minReadySeconds int32
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pod: newPod(now, false, 0),
|
||||||
|
minReadySeconds: 0,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pod: newPod(now, true, 0),
|
||||||
|
minReadySeconds: 1,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pod: newPod(now, true, 0),
|
||||||
|
minReadySeconds: 0,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pod: newPod(now, true, 51),
|
||||||
|
minReadySeconds: 50,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
isAvailable := IsPodAvailable(test.pod, test.minReadySeconds, now)
|
||||||
|
if isAvailable != test.expected {
|
||||||
|
t.Errorf("[tc #%d] expected available pod: %t, got: %t", i, test.expected, isAvailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1717,6 +1717,11 @@ type ReplicationControllerSpec struct {
|
|||||||
// Replicas is the number of desired replicas.
|
// Replicas is the number of desired replicas.
|
||||||
Replicas int32 `json:"replicas"`
|
Replicas int32 `json:"replicas"`
|
||||||
|
|
||||||
|
// Minimum number of seconds for which a newly created pod should be ready
|
||||||
|
// without any of its container crashing, for it to be considered available.
|
||||||
|
// Defaults to 0 (pod will be considered available as soon as it is ready)
|
||||||
|
MinReadySeconds int32 `json:"minReadySeconds,omitempty"`
|
||||||
|
|
||||||
// Selector is a label query over pods that should match the Replicas count.
|
// Selector is a label query over pods that should match the Replicas count.
|
||||||
Selector map[string]string `json:"selector"`
|
Selector map[string]string `json:"selector"`
|
||||||
|
|
||||||
@ -1743,6 +1748,9 @@ type ReplicationControllerStatus struct {
|
|||||||
// The number of ready replicas for this replication controller.
|
// The number of ready replicas for this replication controller.
|
||||||
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
|
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
|
||||||
|
|
||||||
|
// The number of available replicas (ready for at least minReadySeconds) for this replication controller.
|
||||||
|
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
|
||||||
|
|
||||||
// ObservedGeneration is the most recent generation observed by the controller.
|
// ObservedGeneration is the most recent generation observed by the controller.
|
||||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -2023,6 +2023,11 @@ type ReplicationControllerSpec struct {
|
|||||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller
|
// More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller
|
||||||
Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`
|
Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`
|
||||||
|
|
||||||
|
// Minimum number of seconds for which a newly created pod should be ready
|
||||||
|
// without any of its container crashing, for it to be considered available.
|
||||||
|
// Defaults to 0 (pod will be considered available as soon as it is ready)
|
||||||
|
MinReadySeconds int32 `json:"minReadySeconds,omitempty"`
|
||||||
|
|
||||||
// Selector is a label query over pods that should match the Replicas count.
|
// Selector is a label query over pods that should match the Replicas count.
|
||||||
// If Selector is empty, it is defaulted to the labels present on the Pod template.
|
// If Selector is empty, it is defaulted to the labels present on the Pod template.
|
||||||
// Label keys and values that must match in order to be controlled by this replication
|
// Label keys and values that must match in order to be controlled by this replication
|
||||||
@ -2054,6 +2059,9 @@ type ReplicationControllerStatus struct {
|
|||||||
// The number of ready replicas for this replication controller.
|
// The number of ready replicas for this replication controller.
|
||||||
ReadyReplicas int32 `json:"readyReplicas,omitempty" protobuf:"varint,4,opt,name=readyReplicas"`
|
ReadyReplicas int32 `json:"readyReplicas,omitempty" protobuf:"varint,4,opt,name=readyReplicas"`
|
||||||
|
|
||||||
|
// The number of available replicas (ready for at least minReadySeconds) for this replication controller.
|
||||||
|
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
|
||||||
|
|
||||||
// ObservedGeneration reflects the generation of the most recently observed replication controller.
|
// ObservedGeneration reflects the generation of the most recently observed replication controller.
|
||||||
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
|
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
|
||||||
}
|
}
|
||||||
|
@ -5655,6 +5655,7 @@ func autoConvert_v1_ReplicationControllerSpec_To_api_ReplicationControllerSpec(i
|
|||||||
if err := api.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil {
|
if err := api.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
out.MinReadySeconds = in.MinReadySeconds
|
||||||
out.Selector = in.Selector
|
out.Selector = in.Selector
|
||||||
if in.Template != nil {
|
if in.Template != nil {
|
||||||
in, out := &in.Template, &out.Template
|
in, out := &in.Template, &out.Template
|
||||||
@ -5672,6 +5673,7 @@ func autoConvert_api_ReplicationControllerSpec_To_v1_ReplicationControllerSpec(i
|
|||||||
if err := api.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil {
|
if err := api.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
out.MinReadySeconds = in.MinReadySeconds
|
||||||
out.Selector = in.Selector
|
out.Selector = in.Selector
|
||||||
if in.Template != nil {
|
if in.Template != nil {
|
||||||
in, out := &in.Template, &out.Template
|
in, out := &in.Template, &out.Template
|
||||||
@ -5689,6 +5691,7 @@ func autoConvert_v1_ReplicationControllerStatus_To_api_ReplicationControllerStat
|
|||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
||||||
out.ReadyReplicas = in.ReadyReplicas
|
out.ReadyReplicas = in.ReadyReplicas
|
||||||
|
out.AvailableReplicas = in.AvailableReplicas
|
||||||
out.ObservedGeneration = in.ObservedGeneration
|
out.ObservedGeneration = in.ObservedGeneration
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -5701,6 +5704,7 @@ func autoConvert_api_ReplicationControllerStatus_To_v1_ReplicationControllerStat
|
|||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
||||||
out.ReadyReplicas = in.ReadyReplicas
|
out.ReadyReplicas = in.ReadyReplicas
|
||||||
|
out.AvailableReplicas = in.AvailableReplicas
|
||||||
out.ObservedGeneration = in.ObservedGeneration
|
out.ObservedGeneration = in.ObservedGeneration
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2942,6 +2942,7 @@ func DeepCopy_v1_ReplicationControllerSpec(in interface{}, out interface{}, c *c
|
|||||||
} else {
|
} else {
|
||||||
out.Replicas = nil
|
out.Replicas = nil
|
||||||
}
|
}
|
||||||
|
out.MinReadySeconds = in.MinReadySeconds
|
||||||
if in.Selector != nil {
|
if in.Selector != nil {
|
||||||
in, out := &in.Selector, &out.Selector
|
in, out := &in.Selector, &out.Selector
|
||||||
*out = make(map[string]string)
|
*out = make(map[string]string)
|
||||||
@ -2971,6 +2972,7 @@ func DeepCopy_v1_ReplicationControllerStatus(in interface{}, out interface{}, c
|
|||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
||||||
out.ReadyReplicas = in.ReadyReplicas
|
out.ReadyReplicas = in.ReadyReplicas
|
||||||
|
out.AvailableReplicas = in.AvailableReplicas
|
||||||
out.ObservedGeneration = in.ObservedGeneration
|
out.ObservedGeneration = in.ObservedGeneration
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2606,6 +2606,8 @@ func ValidateReplicationControllerStatusUpdate(controller, oldController *api.Re
|
|||||||
statusPath := field.NewPath("status")
|
statusPath := field.NewPath("status")
|
||||||
allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.Replicas), statusPath.Child("replicas"))...)
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.Replicas), statusPath.Child("replicas"))...)
|
||||||
allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.FullyLabeledReplicas), statusPath.Child("fullyLabeledReplicas"))...)
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.FullyLabeledReplicas), statusPath.Child("fullyLabeledReplicas"))...)
|
||||||
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.ReadyReplicas), statusPath.Child("readyReplicas"))...)
|
||||||
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.AvailableReplicas), statusPath.Child("availableReplicas"))...)
|
||||||
allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.ObservedGeneration), statusPath.Child("observedGeneration"))...)
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.ObservedGeneration), statusPath.Child("observedGeneration"))...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -2649,6 +2651,7 @@ func ValidatePodTemplateSpecForRC(template *api.PodTemplateSpec, selectorMap map
|
|||||||
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
|
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
|
||||||
func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
|
||||||
allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...)
|
allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...)
|
||||||
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
||||||
allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, spec.Replicas, fldPath.Child("template"))...)
|
allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, spec.Replicas, fldPath.Child("template"))...)
|
||||||
|
@ -2994,6 +2994,7 @@ func DeepCopy_api_ReplicationControllerSpec(in interface{}, out interface{}, c *
|
|||||||
in := in.(*ReplicationControllerSpec)
|
in := in.(*ReplicationControllerSpec)
|
||||||
out := out.(*ReplicationControllerSpec)
|
out := out.(*ReplicationControllerSpec)
|
||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
|
out.MinReadySeconds = in.MinReadySeconds
|
||||||
if in.Selector != nil {
|
if in.Selector != nil {
|
||||||
in, out := &in.Selector, &out.Selector
|
in, out := &in.Selector, &out.Selector
|
||||||
*out = make(map[string]string)
|
*out = make(map[string]string)
|
||||||
@ -3023,6 +3024,7 @@ func DeepCopy_api_ReplicationControllerStatus(in interface{}, out interface{}, c
|
|||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
||||||
out.ReadyReplicas = in.ReadyReplicas
|
out.ReadyReplicas = in.ReadyReplicas
|
||||||
|
out.AvailableReplicas = in.AvailableReplicas
|
||||||
out.ObservedGeneration = in.ObservedGeneration
|
out.ObservedGeneration = in.ObservedGeneration
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -610,6 +610,11 @@ type ReplicaSetSpec struct {
|
|||||||
// Replicas is the number of desired replicas.
|
// Replicas is the number of desired replicas.
|
||||||
Replicas int32 `json:"replicas"`
|
Replicas int32 `json:"replicas"`
|
||||||
|
|
||||||
|
// Minimum number of seconds for which a newly created pod should be ready
|
||||||
|
// without any of its container crashing, for it to be considered available.
|
||||||
|
// Defaults to 0 (pod will be considered available as soon as it is ready)
|
||||||
|
MinReadySeconds int32 `json:"minReadySeconds,omitempty"`
|
||||||
|
|
||||||
// Selector is a label query over pods that should match the replica count.
|
// Selector is a label query over pods that should match the replica count.
|
||||||
// Must match in order to be controlled.
|
// Must match in order to be controlled.
|
||||||
// If empty, defaulted to labels on pod template.
|
// If empty, defaulted to labels on pod template.
|
||||||
@ -632,6 +637,9 @@ type ReplicaSetStatus struct {
|
|||||||
// The number of ready replicas for this replica set.
|
// The number of ready replicas for this replica set.
|
||||||
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
|
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
|
||||||
|
|
||||||
|
// The number of available replicas (ready for at least minReadySeconds) for this replica set.
|
||||||
|
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
|
||||||
|
|
||||||
// ObservedGeneration is the most recent generation observed by the controller.
|
// ObservedGeneration is the most recent generation observed by the controller.
|
||||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -887,6 +887,11 @@ type ReplicaSetSpec struct {
|
|||||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller
|
// More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller
|
||||||
Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`
|
Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`
|
||||||
|
|
||||||
|
// Minimum number of seconds for which a newly created pod should be ready
|
||||||
|
// without any of its container crashing, for it to be considered available.
|
||||||
|
// Defaults to 0 (pod will be considered available as soon as it is ready)
|
||||||
|
MinReadySeconds int32 `json:"minReadySeconds,omitempty"`
|
||||||
|
|
||||||
// Selector is a label query over pods that should match the replica count.
|
// Selector is a label query over pods that should match the replica count.
|
||||||
// If the selector is empty, it is defaulted to the labels present on the pod template.
|
// If the selector is empty, it is defaulted to the labels present on the pod template.
|
||||||
// Label keys and values that must match in order to be controlled by this replica set.
|
// Label keys and values that must match in order to be controlled by this replica set.
|
||||||
@ -911,6 +916,9 @@ type ReplicaSetStatus struct {
|
|||||||
// The number of ready replicas for this replica set.
|
// The number of ready replicas for this replica set.
|
||||||
ReadyReplicas int32 `json:"readyReplicas,omitempty" protobuf:"varint,4,opt,name=readyReplicas"`
|
ReadyReplicas int32 `json:"readyReplicas,omitempty" protobuf:"varint,4,opt,name=readyReplicas"`
|
||||||
|
|
||||||
|
// The number of available replicas (ready for at least minReadySeconds) for this replica set.
|
||||||
|
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
|
||||||
|
|
||||||
// ObservedGeneration reflects the generation of the most recently observed ReplicaSet.
|
// ObservedGeneration reflects the generation of the most recently observed ReplicaSet.
|
||||||
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
|
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
|
||||||
}
|
}
|
||||||
|
@ -2240,6 +2240,7 @@ func autoConvert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec(in *Replica
|
|||||||
if err := api.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil {
|
if err := api.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
out.MinReadySeconds = in.MinReadySeconds
|
||||||
if in.Selector != nil {
|
if in.Selector != nil {
|
||||||
in, out := &in.Selector, &out.Selector
|
in, out := &in.Selector, &out.Selector
|
||||||
*out = new(unversioned.LabelSelector)
|
*out = new(unversioned.LabelSelector)
|
||||||
@ -2259,6 +2260,7 @@ func autoConvert_extensions_ReplicaSetSpec_To_v1beta1_ReplicaSetSpec(in *extensi
|
|||||||
if err := api.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil {
|
if err := api.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
out.MinReadySeconds = in.MinReadySeconds
|
||||||
if in.Selector != nil {
|
if in.Selector != nil {
|
||||||
in, out := &in.Selector, &out.Selector
|
in, out := &in.Selector, &out.Selector
|
||||||
*out = new(LabelSelector)
|
*out = new(LabelSelector)
|
||||||
@ -2278,6 +2280,7 @@ func autoConvert_v1beta1_ReplicaSetStatus_To_extensions_ReplicaSetStatus(in *Rep
|
|||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
||||||
out.ReadyReplicas = in.ReadyReplicas
|
out.ReadyReplicas = in.ReadyReplicas
|
||||||
|
out.AvailableReplicas = in.AvailableReplicas
|
||||||
out.ObservedGeneration = in.ObservedGeneration
|
out.ObservedGeneration = in.ObservedGeneration
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2290,6 +2293,7 @@ func autoConvert_extensions_ReplicaSetStatus_To_v1beta1_ReplicaSetStatus(in *ext
|
|||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
||||||
out.ReadyReplicas = in.ReadyReplicas
|
out.ReadyReplicas = in.ReadyReplicas
|
||||||
|
out.AvailableReplicas = in.AvailableReplicas
|
||||||
out.ObservedGeneration = in.ObservedGeneration
|
out.ObservedGeneration = in.ObservedGeneration
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1191,6 +1191,7 @@ func DeepCopy_v1beta1_ReplicaSetSpec(in interface{}, out interface{}, c *convers
|
|||||||
} else {
|
} else {
|
||||||
out.Replicas = nil
|
out.Replicas = nil
|
||||||
}
|
}
|
||||||
|
out.MinReadySeconds = in.MinReadySeconds
|
||||||
if in.Selector != nil {
|
if in.Selector != nil {
|
||||||
in, out := &in.Selector, &out.Selector
|
in, out := &in.Selector, &out.Selector
|
||||||
*out = new(LabelSelector)
|
*out = new(LabelSelector)
|
||||||
@ -1214,6 +1215,7 @@ func DeepCopy_v1beta1_ReplicaSetStatus(in interface{}, out interface{}, c *conve
|
|||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
||||||
out.ReadyReplicas = in.ReadyReplicas
|
out.ReadyReplicas = in.ReadyReplicas
|
||||||
|
out.AvailableReplicas = in.AvailableReplicas
|
||||||
out.ObservedGeneration = in.ObservedGeneration
|
out.ObservedGeneration = in.ObservedGeneration
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -491,6 +491,8 @@ func ValidateReplicaSetStatusUpdate(rs, oldRs *extensions.ReplicaSet) field.Erro
|
|||||||
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
|
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
|
||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.Replicas), field.NewPath("status", "replicas"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.Replicas), field.NewPath("status", "replicas"))...)
|
||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.FullyLabeledReplicas), field.NewPath("status", "fullyLabeledReplicas"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.FullyLabeledReplicas), field.NewPath("status", "fullyLabeledReplicas"))...)
|
||||||
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.ReadyReplicas), field.NewPath("status", "readyReplicas"))...)
|
||||||
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.AvailableReplicas), field.NewPath("status", "availableReplicas"))...)
|
||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.ObservedGeneration), field.NewPath("status", "observedGeneration"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.ObservedGeneration), field.NewPath("status", "observedGeneration"))...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -500,6 +502,7 @@ func ValidateReplicaSetSpec(spec *extensions.ReplicaSetSpec, fldPath *field.Path
|
|||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
||||||
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
|
||||||
|
|
||||||
if spec.Selector == nil {
|
if spec.Selector == nil {
|
||||||
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
|
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
|
||||||
|
@ -846,6 +846,7 @@ func DeepCopy_extensions_ReplicaSetSpec(in interface{}, out interface{}, c *conv
|
|||||||
in := in.(*ReplicaSetSpec)
|
in := in.(*ReplicaSetSpec)
|
||||||
out := out.(*ReplicaSetSpec)
|
out := out.(*ReplicaSetSpec)
|
||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
|
out.MinReadySeconds = in.MinReadySeconds
|
||||||
if in.Selector != nil {
|
if in.Selector != nil {
|
||||||
in, out := &in.Selector, &out.Selector
|
in, out := &in.Selector, &out.Selector
|
||||||
*out = new(unversioned.LabelSelector)
|
*out = new(unversioned.LabelSelector)
|
||||||
@ -869,6 +870,7 @@ func DeepCopy_extensions_ReplicaSetStatus(in interface{}, out interface{}, c *co
|
|||||||
out.Replicas = in.Replicas
|
out.Replicas = in.Replicas
|
||||||
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
out.FullyLabeledReplicas = in.FullyLabeledReplicas
|
||||||
out.ReadyReplicas = in.ReadyReplicas
|
out.ReadyReplicas = in.ReadyReplicas
|
||||||
|
out.AvailableReplicas = in.AvailableReplicas
|
||||||
out.ObservedGeneration = in.ObservedGeneration
|
out.ObservedGeneration = in.ObservedGeneration
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -640,6 +640,8 @@ func countAvailablePods(pods []api.Pod, minReadySeconds int32) int32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsPodAvailable return true if the pod is available.
|
// IsPodAvailable return true if the pod is available.
|
||||||
|
// TODO: Remove this once we start using replica set status for calculating available pods
|
||||||
|
// for a deployment.
|
||||||
func IsPodAvailable(pod *api.Pod, minReadySeconds int32, now time.Time) bool {
|
func IsPodAvailable(pod *api.Pod, minReadySeconds int32, now time.Time) bool {
|
||||||
if !controller.IsPodActive(pod) {
|
if !controller.IsPodActive(pod) {
|
||||||
return false
|
return false
|
||||||
|
@ -658,6 +658,7 @@ func (rsc *ReplicaSetController) syncReplicaSet(key string) error {
|
|||||||
// part of the filteredPods.
|
// part of the filteredPods.
|
||||||
fullyLabeledReplicasCount := 0
|
fullyLabeledReplicasCount := 0
|
||||||
readyReplicasCount := 0
|
readyReplicasCount := 0
|
||||||
|
availableReplicasCount := 0
|
||||||
templateLabel := labels.Set(rs.Spec.Template.Labels).AsSelectorPreValidated()
|
templateLabel := labels.Set(rs.Spec.Template.Labels).AsSelectorPreValidated()
|
||||||
for _, pod := range filteredPods {
|
for _, pod := range filteredPods {
|
||||||
if templateLabel.Matches(labels.Set(pod.Labels)) {
|
if templateLabel.Matches(labels.Set(pod.Labels)) {
|
||||||
@ -665,11 +666,21 @@ func (rsc *ReplicaSetController) syncReplicaSet(key string) error {
|
|||||||
}
|
}
|
||||||
if api.IsPodReady(pod) {
|
if api.IsPodReady(pod) {
|
||||||
readyReplicasCount++
|
readyReplicasCount++
|
||||||
|
if api.IsPodAvailable(pod, rs.Spec.MinReadySeconds, unversioned.Now()) {
|
||||||
|
availableReplicasCount++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always updates status as pods come up or die.
|
// Always updates status as pods come up or die.
|
||||||
if err := updateReplicaCount(rsc.kubeClient.Extensions().ReplicaSets(rs.Namespace), rs, len(filteredPods), fullyLabeledReplicasCount, readyReplicasCount); err != nil {
|
if err := updateReplicaCount(
|
||||||
|
rsc.kubeClient.Extensions().ReplicaSets(rs.Namespace),
|
||||||
|
rs,
|
||||||
|
len(filteredPods),
|
||||||
|
fullyLabeledReplicasCount,
|
||||||
|
readyReplicasCount,
|
||||||
|
availableReplicasCount,
|
||||||
|
); err != nil {
|
||||||
// Multiple things could lead to this update failing. Requeuing the replica set ensures
|
// Multiple things could lead to this update failing. Requeuing the replica set ensures
|
||||||
// we retry with some fairness.
|
// we retry with some fairness.
|
||||||
glog.V(2).Infof("Failed to update replica count for controller %v/%v; requeuing; error: %v", rs.Namespace, rs.Name, err)
|
glog.V(2).Infof("Failed to update replica count for controller %v/%v; requeuing; error: %v", rs.Namespace, rs.Name, err)
|
||||||
|
@ -97,10 +97,14 @@ func newReplicaSet(replicas int, selectorMap map[string]string) *extensions.Repl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a pod with the given phase for the given rs (same selectors and namespace)
|
// create a pod with the given phase for the given rs (same selectors and namespace)
|
||||||
func newPod(name string, rs *extensions.ReplicaSet, status api.PodPhase) *api.Pod {
|
func newPod(name string, rs *extensions.ReplicaSet, status api.PodPhase, lastTransitionTime *unversioned.Time) *api.Pod {
|
||||||
var conditions []api.PodCondition
|
var conditions []api.PodCondition
|
||||||
if status == api.PodRunning {
|
if status == api.PodRunning {
|
||||||
conditions = append(conditions, api.PodCondition{Type: api.PodReady, Status: api.ConditionTrue})
|
condition := api.PodCondition{Type: api.PodReady, Status: api.ConditionTrue}
|
||||||
|
if lastTransitionTime != nil {
|
||||||
|
condition.LastTransitionTime = *lastTransitionTime
|
||||||
|
}
|
||||||
|
conditions = append(conditions, condition)
|
||||||
}
|
}
|
||||||
return &api.Pod{
|
return &api.Pod{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
@ -118,7 +122,7 @@ func newPodList(store cache.Store, count int, status api.PodPhase, labelMap map[
|
|||||||
var trueVar = true
|
var trueVar = true
|
||||||
controllerReference := api.OwnerReference{UID: rs.UID, APIVersion: "v1beta1", Kind: "ReplicaSet", Name: rs.Name, Controller: &trueVar}
|
controllerReference := api.OwnerReference{UID: rs.UID, APIVersion: "v1beta1", Kind: "ReplicaSet", Name: rs.Name, Controller: &trueVar}
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
pod := newPod(fmt.Sprintf("%s%d", name, i), rs, status)
|
pod := newPod(fmt.Sprintf("%s%d", name, i), rs, status, nil)
|
||||||
pod.ObjectMeta.Labels = labelMap
|
pod.ObjectMeta.Labels = labelMap
|
||||||
pod.OwnerReferences = []api.OwnerReference{controllerReference}
|
pod.OwnerReferences = []api.OwnerReference{controllerReference}
|
||||||
if store != nil {
|
if store != nil {
|
||||||
@ -253,7 +257,7 @@ func TestStatusUpdatesWithoutReplicasChange(t *testing.T) {
|
|||||||
labelMap := map[string]string{"foo": "bar"}
|
labelMap := map[string]string{"foo": "bar"}
|
||||||
rs := newReplicaSet(activePods, labelMap)
|
rs := newReplicaSet(activePods, labelMap)
|
||||||
manager.rsStore.Store.Add(rs)
|
manager.rsStore.Store.Add(rs)
|
||||||
rs.Status = extensions.ReplicaSetStatus{Replicas: int32(activePods), ReadyReplicas: int32(activePods)}
|
rs.Status = extensions.ReplicaSetStatus{Replicas: int32(activePods), ReadyReplicas: int32(activePods), AvailableReplicas: int32(activePods)}
|
||||||
newPodList(manager.podStore.Indexer, activePods, api.PodRunning, labelMap, rs, "pod")
|
newPodList(manager.podStore.Indexer, activePods, api.PodRunning, labelMap, rs, "pod")
|
||||||
|
|
||||||
fakePodControl := controller.FakePodControl{}
|
fakePodControl := controller.FakePodControl{}
|
||||||
@ -298,7 +302,7 @@ func TestControllerUpdateReplicas(t *testing.T) {
|
|||||||
rs := newReplicaSet(5, labelMap)
|
rs := newReplicaSet(5, labelMap)
|
||||||
rs.Spec.Template.Labels = extraLabelMap
|
rs.Spec.Template.Labels = extraLabelMap
|
||||||
manager.rsStore.Store.Add(rs)
|
manager.rsStore.Store.Add(rs)
|
||||||
rs.Status = extensions.ReplicaSetStatus{Replicas: 2, FullyLabeledReplicas: 6, ReadyReplicas: 2, ObservedGeneration: 0}
|
rs.Status = extensions.ReplicaSetStatus{Replicas: 2, FullyLabeledReplicas: 6, ReadyReplicas: 2, AvailableReplicas: 2, ObservedGeneration: 0}
|
||||||
rs.Generation = 1
|
rs.Generation = 1
|
||||||
newPodList(manager.podStore.Indexer, 2, api.PodRunning, labelMap, rs, "pod")
|
newPodList(manager.podStore.Indexer, 2, api.PodRunning, labelMap, rs, "pod")
|
||||||
newPodList(manager.podStore.Indexer, 2, api.PodRunning, extraLabelMap, rs, "podWithExtraLabel")
|
newPodList(manager.podStore.Indexer, 2, api.PodRunning, extraLabelMap, rs, "podWithExtraLabel")
|
||||||
@ -316,7 +320,7 @@ func TestControllerUpdateReplicas(t *testing.T) {
|
|||||||
// 2. Status.FullyLabeledReplicas should equal to the number of pods that
|
// 2. Status.FullyLabeledReplicas should equal to the number of pods that
|
||||||
// has the extra labels, i.e., 2.
|
// has the extra labels, i.e., 2.
|
||||||
// 3. Every update to the status should include the Generation of the spec.
|
// 3. Every update to the status should include the Generation of the spec.
|
||||||
rs.Status = extensions.ReplicaSetStatus{Replicas: 4, FullyLabeledReplicas: 2, ReadyReplicas: 4, ObservedGeneration: 1}
|
rs.Status = extensions.ReplicaSetStatus{Replicas: 4, FullyLabeledReplicas: 2, ReadyReplicas: 4, AvailableReplicas: 4, ObservedGeneration: 1}
|
||||||
|
|
||||||
decRc := runtime.EncodeOrDie(testapi.Extensions.Codec(), rs)
|
decRc := runtime.EncodeOrDie(testapi.Extensions.Codec(), rs)
|
||||||
fakeHandler.ValidateRequest(t, testapi.Extensions.ResourcePath(replicaSetResourceName(), rs.Namespace, rs.Name)+"/status", "PUT", &decRc)
|
fakeHandler.ValidateRequest(t, testapi.Extensions.ResourcePath(replicaSetResourceName(), rs.Namespace, rs.Name)+"/status", "PUT", &decRc)
|
||||||
@ -346,12 +350,14 @@ func TestSyncReplicaSetDormancy(t *testing.T) {
|
|||||||
// Creates a replica and sets expectations
|
// Creates a replica and sets expectations
|
||||||
rsSpec.Status.Replicas = 1
|
rsSpec.Status.Replicas = 1
|
||||||
rsSpec.Status.ReadyReplicas = 1
|
rsSpec.Status.ReadyReplicas = 1
|
||||||
|
rsSpec.Status.AvailableReplicas = 1
|
||||||
manager.syncReplicaSet(getKey(rsSpec, t))
|
manager.syncReplicaSet(getKey(rsSpec, t))
|
||||||
validateSyncReplicaSet(t, &fakePodControl, 1, 0, 0)
|
validateSyncReplicaSet(t, &fakePodControl, 1, 0, 0)
|
||||||
|
|
||||||
// Expectations prevents replicas but not an update on status
|
// Expectations prevents replicas but not an update on status
|
||||||
rsSpec.Status.Replicas = 0
|
rsSpec.Status.Replicas = 0
|
||||||
rsSpec.Status.ReadyReplicas = 0
|
rsSpec.Status.ReadyReplicas = 0
|
||||||
|
rsSpec.Status.AvailableReplicas = 0
|
||||||
fakePodControl.Clear()
|
fakePodControl.Clear()
|
||||||
manager.syncReplicaSet(getKey(rsSpec, t))
|
manager.syncReplicaSet(getKey(rsSpec, t))
|
||||||
validateSyncReplicaSet(t, &fakePodControl, 0, 0, 0)
|
validateSyncReplicaSet(t, &fakePodControl, 0, 0, 0)
|
||||||
@ -367,6 +373,7 @@ func TestSyncReplicaSetDormancy(t *testing.T) {
|
|||||||
manager.expectations.CreationObserved(rsKey)
|
manager.expectations.CreationObserved(rsKey)
|
||||||
rsSpec.Status.Replicas = 1
|
rsSpec.Status.Replicas = 1
|
||||||
rsSpec.Status.ReadyReplicas = 1
|
rsSpec.Status.ReadyReplicas = 1
|
||||||
|
rsSpec.Status.AvailableReplicas = 1
|
||||||
fakePodControl.Clear()
|
fakePodControl.Clear()
|
||||||
fakePodControl.Err = fmt.Errorf("Fake Error")
|
fakePodControl.Err = fmt.Errorf("Fake Error")
|
||||||
|
|
||||||
@ -662,7 +669,7 @@ func TestControllerUpdateStatusWithFailure(t *testing.T) {
|
|||||||
})
|
})
|
||||||
fakeRSClient := fakeClient.Extensions().ReplicaSets("default")
|
fakeRSClient := fakeClient.Extensions().ReplicaSets("default")
|
||||||
numReplicas := 10
|
numReplicas := 10
|
||||||
updateReplicaCount(fakeRSClient, *rs, numReplicas, 0, 0)
|
updateReplicaCount(fakeRSClient, *rs, numReplicas, 0, 0, 0)
|
||||||
updates, gets := 0, 0
|
updates, gets := 0, 0
|
||||||
for _, a := range fakeClient.Actions() {
|
for _, a := range fakeClient.Actions() {
|
||||||
if a.GetResource().Resource != "replicasets" {
|
if a.GetResource().Resource != "replicasets" {
|
||||||
@ -1098,7 +1105,7 @@ func TestDoNotPatchPodWithOtherControlRef(t *testing.T) {
|
|||||||
var trueVar = true
|
var trueVar = true
|
||||||
otherControllerReference := api.OwnerReference{UID: uuid.NewUUID(), APIVersion: "v1beta1", Kind: "ReplicaSet", Name: "AnotherRS", Controller: &trueVar}
|
otherControllerReference := api.OwnerReference{UID: uuid.NewUUID(), APIVersion: "v1beta1", Kind: "ReplicaSet", Name: "AnotherRS", Controller: &trueVar}
|
||||||
// add to podStore a matching Pod controlled by another controller. Expect no patch.
|
// add to podStore a matching Pod controlled by another controller. Expect no patch.
|
||||||
pod := newPod("pod", rs, api.PodRunning)
|
pod := newPod("pod", rs, api.PodRunning, nil)
|
||||||
pod.OwnerReferences = []api.OwnerReference{otherControllerReference}
|
pod.OwnerReferences = []api.OwnerReference{otherControllerReference}
|
||||||
manager.podStore.Indexer.Add(pod)
|
manager.podStore.Indexer.Add(pod)
|
||||||
err := manager.syncReplicaSet(getKey(rs, t))
|
err := manager.syncReplicaSet(getKey(rs, t))
|
||||||
@ -1118,7 +1125,7 @@ func TestPatchPodWithOtherOwnerRef(t *testing.T) {
|
|||||||
// ref, but has an owner ref pointing to other object. Expect a patch to
|
// ref, but has an owner ref pointing to other object. Expect a patch to
|
||||||
// take control of it.
|
// take control of it.
|
||||||
unrelatedOwnerReference := api.OwnerReference{UID: uuid.NewUUID(), APIVersion: "batch/v1", Kind: "Job", Name: "Job"}
|
unrelatedOwnerReference := api.OwnerReference{UID: uuid.NewUUID(), APIVersion: "batch/v1", Kind: "Job", Name: "Job"}
|
||||||
pod := newPod("pod", rs, api.PodRunning)
|
pod := newPod("pod", rs, api.PodRunning, nil)
|
||||||
pod.OwnerReferences = []api.OwnerReference{unrelatedOwnerReference}
|
pod.OwnerReferences = []api.OwnerReference{unrelatedOwnerReference}
|
||||||
manager.podStore.Indexer.Add(pod)
|
manager.podStore.Indexer.Add(pod)
|
||||||
|
|
||||||
@ -1138,7 +1145,7 @@ func TestPatchPodWithCorrectOwnerRef(t *testing.T) {
|
|||||||
// add to podStore a matching pod that has an ownerRef pointing to the rs,
|
// add to podStore a matching pod that has an ownerRef pointing to the rs,
|
||||||
// but ownerRef.Controller is false. Expect a patch to take control it.
|
// but ownerRef.Controller is false. Expect a patch to take control it.
|
||||||
rsOwnerReference := api.OwnerReference{UID: rs.UID, APIVersion: "v1", Kind: "ReplicaSet", Name: rs.Name}
|
rsOwnerReference := api.OwnerReference{UID: rs.UID, APIVersion: "v1", Kind: "ReplicaSet", Name: rs.Name}
|
||||||
pod := newPod("pod", rs, api.PodRunning)
|
pod := newPod("pod", rs, api.PodRunning, nil)
|
||||||
pod.OwnerReferences = []api.OwnerReference{rsOwnerReference}
|
pod.OwnerReferences = []api.OwnerReference{rsOwnerReference}
|
||||||
manager.podStore.Indexer.Add(pod)
|
manager.podStore.Indexer.Add(pod)
|
||||||
|
|
||||||
@ -1157,8 +1164,8 @@ func TestPatchPodFails(t *testing.T) {
|
|||||||
manager.rsStore.Store.Add(rs)
|
manager.rsStore.Store.Add(rs)
|
||||||
// add to podStore two matching pods. Expect two patches to take control
|
// add to podStore two matching pods. Expect two patches to take control
|
||||||
// them.
|
// them.
|
||||||
manager.podStore.Indexer.Add(newPod("pod1", rs, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod1", rs, api.PodRunning, nil))
|
||||||
manager.podStore.Indexer.Add(newPod("pod2", rs, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod2", rs, api.PodRunning, nil))
|
||||||
// let both patches fail. The rs controller will assume it fails to take
|
// let both patches fail. The rs controller will assume it fails to take
|
||||||
// control of the pods and create new ones.
|
// control of the pods and create new ones.
|
||||||
fakePodControl.Err = fmt.Errorf("Fake Error")
|
fakePodControl.Err = fmt.Errorf("Fake Error")
|
||||||
@ -1177,9 +1184,9 @@ func TestPatchExtraPodsThenDelete(t *testing.T) {
|
|||||||
manager.rsStore.Store.Add(rs)
|
manager.rsStore.Store.Add(rs)
|
||||||
// add to podStore three matching pods. Expect three patches to take control
|
// add to podStore three matching pods. Expect three patches to take control
|
||||||
// them, and later delete one of them.
|
// them, and later delete one of them.
|
||||||
manager.podStore.Indexer.Add(newPod("pod1", rs, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod1", rs, api.PodRunning, nil))
|
||||||
manager.podStore.Indexer.Add(newPod("pod2", rs, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod2", rs, api.PodRunning, nil))
|
||||||
manager.podStore.Indexer.Add(newPod("pod3", rs, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod3", rs, api.PodRunning, nil))
|
||||||
err := manager.syncReplicaSet(getKey(rs, t))
|
err := manager.syncReplicaSet(getKey(rs, t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -1194,7 +1201,7 @@ func TestUpdateLabelsRemoveControllerRef(t *testing.T) {
|
|||||||
rs := newReplicaSet(2, labelMap)
|
rs := newReplicaSet(2, labelMap)
|
||||||
manager.rsStore.Store.Add(rs)
|
manager.rsStore.Store.Add(rs)
|
||||||
// put one pod in the podStore
|
// put one pod in the podStore
|
||||||
pod := newPod("pod", rs, api.PodRunning)
|
pod := newPod("pod", rs, api.PodRunning, nil)
|
||||||
pod.ResourceVersion = "1"
|
pod.ResourceVersion = "1"
|
||||||
var trueVar = true
|
var trueVar = true
|
||||||
rsOwnerReference := api.OwnerReference{UID: rs.UID, APIVersion: "v1beta1", Kind: "ReplicaSet", Name: rs.Name, Controller: &trueVar}
|
rsOwnerReference := api.OwnerReference{UID: rs.UID, APIVersion: "v1beta1", Kind: "ReplicaSet", Name: rs.Name, Controller: &trueVar}
|
||||||
@ -1268,7 +1275,7 @@ func TestDoNotAdoptOrCreateIfBeingDeleted(t *testing.T) {
|
|||||||
now := unversioned.Now()
|
now := unversioned.Now()
|
||||||
rs.DeletionTimestamp = &now
|
rs.DeletionTimestamp = &now
|
||||||
manager.rsStore.Store.Add(rs)
|
manager.rsStore.Store.Add(rs)
|
||||||
pod1 := newPod("pod1", rs, api.PodRunning)
|
pod1 := newPod("pod1", rs, api.PodRunning, nil)
|
||||||
manager.podStore.Indexer.Add(pod1)
|
manager.podStore.Indexer.Add(pod1)
|
||||||
|
|
||||||
// no patch, no create
|
// no patch, no create
|
||||||
@ -1295,7 +1302,7 @@ func TestReadyReplicas(t *testing.T) {
|
|||||||
// Status.Replica should update to match number of pods in system, 1 new pod should be created.
|
// Status.Replica should update to match number of pods in system, 1 new pod should be created.
|
||||||
labelMap := map[string]string{"foo": "bar"}
|
labelMap := map[string]string{"foo": "bar"}
|
||||||
rs := newReplicaSet(2, labelMap)
|
rs := newReplicaSet(2, labelMap)
|
||||||
rs.Status = extensions.ReplicaSetStatus{Replicas: 2, ReadyReplicas: 0, ObservedGeneration: 1}
|
rs.Status = extensions.ReplicaSetStatus{Replicas: 2, ReadyReplicas: 0, AvailableReplicas: 0, ObservedGeneration: 1}
|
||||||
rs.Generation = 1
|
rs.Generation = 1
|
||||||
manager.rsStore.Store.Add(rs)
|
manager.rsStore.Store.Add(rs)
|
||||||
|
|
||||||
@ -1312,7 +1319,56 @@ func TestReadyReplicas(t *testing.T) {
|
|||||||
manager.syncReplicaSet(getKey(rs, t))
|
manager.syncReplicaSet(getKey(rs, t))
|
||||||
|
|
||||||
// ReadyReplicas should go from 0 to 2.
|
// ReadyReplicas should go from 0 to 2.
|
||||||
rs.Status = extensions.ReplicaSetStatus{Replicas: 2, ReadyReplicas: 2, ObservedGeneration: 1}
|
rs.Status = extensions.ReplicaSetStatus{Replicas: 2, ReadyReplicas: 2, AvailableReplicas: 2, ObservedGeneration: 1}
|
||||||
|
|
||||||
|
decRs := runtime.EncodeOrDie(testapi.Extensions.Codec(), rs)
|
||||||
|
fakeHandler.ValidateRequest(t, testapi.Extensions.ResourcePath(replicaSetResourceName(), rs.Namespace, rs.Name)+"/status", "PUT", &decRs)
|
||||||
|
validateSyncReplicaSet(t, &fakePodControl, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAvailableReplicas(t *testing.T) {
|
||||||
|
// This is a happy server just to record the PUT request we expect for status.Replicas
|
||||||
|
fakeHandler := utiltesting.FakeHandler{
|
||||||
|
StatusCode: 200,
|
||||||
|
ResponseBody: "{}",
|
||||||
|
}
|
||||||
|
testServer := httptest.NewServer(&fakeHandler)
|
||||||
|
defer testServer.Close()
|
||||||
|
|
||||||
|
client := clientset.NewForConfigOrDie(&restclient.Config{Host: testServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}})
|
||||||
|
manager := NewReplicaSetControllerFromClient(client, controller.NoResyncPeriodFunc, BurstReplicas, 0)
|
||||||
|
manager.podStoreSynced = alwaysReady
|
||||||
|
|
||||||
|
// Status.Replica should update to match number of pods in system, 1 new pod should be created.
|
||||||
|
labelMap := map[string]string{"foo": "bar"}
|
||||||
|
rs := newReplicaSet(2, labelMap)
|
||||||
|
rs.Status = extensions.ReplicaSetStatus{Replicas: 2, ReadyReplicas: 0, AvailableReplicas: 0, ObservedGeneration: 1}
|
||||||
|
rs.Generation = 1
|
||||||
|
// minReadySeconds set to 15s
|
||||||
|
rs.Spec.MinReadySeconds = 15
|
||||||
|
manager.rsStore.Store.Add(rs)
|
||||||
|
|
||||||
|
// First pod becomes ready 20s ago
|
||||||
|
moment := unversioned.Time{Time: time.Now().Add(-2e10)}
|
||||||
|
pod := newPod("pod", rs, api.PodRunning, &moment)
|
||||||
|
manager.podStore.Indexer.Add(pod)
|
||||||
|
|
||||||
|
// Second pod becomes ready now
|
||||||
|
otherMoment := unversioned.Now()
|
||||||
|
otherPod := newPod("otherPod", rs, api.PodRunning, &otherMoment)
|
||||||
|
manager.podStore.Indexer.Add(otherPod)
|
||||||
|
|
||||||
|
// This response body is just so we don't err out decoding the http response
|
||||||
|
response := runtime.EncodeOrDie(testapi.Extensions.Codec(), &extensions.ReplicaSet{})
|
||||||
|
fakeHandler.ResponseBody = response
|
||||||
|
|
||||||
|
fakePodControl := controller.FakePodControl{}
|
||||||
|
manager.podControl = &fakePodControl
|
||||||
|
|
||||||
|
// The controller should see only one available pod.
|
||||||
|
manager.syncReplicaSet(getKey(rs, t))
|
||||||
|
|
||||||
|
rs.Status = extensions.ReplicaSetStatus{Replicas: 2, ReadyReplicas: 2, AvailableReplicas: 1, ObservedGeneration: 1}
|
||||||
|
|
||||||
decRs := runtime.EncodeOrDie(testapi.Extensions.Codec(), rs)
|
decRs := runtime.EncodeOrDie(testapi.Extensions.Codec(), rs)
|
||||||
fakeHandler.ValidateRequest(t, testapi.Extensions.ResourcePath(replicaSetResourceName(), rs.Namespace, rs.Name)+"/status", "PUT", &decRs)
|
fakeHandler.ValidateRequest(t, testapi.Extensions.ResourcePath(replicaSetResourceName(), rs.Namespace, rs.Name)+"/status", "PUT", &decRs)
|
||||||
|
@ -27,13 +27,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// updateReplicaCount attempts to update the Status.Replicas of the given ReplicaSet, with a single GET/PUT retry.
|
// updateReplicaCount attempts to update the Status.Replicas of the given ReplicaSet, with a single GET/PUT retry.
|
||||||
func updateReplicaCount(rsClient client.ReplicaSetInterface, rs extensions.ReplicaSet, numReplicas, numFullyLabeledReplicas, numReadyReplicas int) (updateErr error) {
|
func updateReplicaCount(rsClient client.ReplicaSetInterface, rs extensions.ReplicaSet, numReplicas, numFullyLabeledReplicas, numReadyReplicas, numAvailableReplicas int) (updateErr error) {
|
||||||
// This is the steady state. It happens when the ReplicaSet doesn't have any expectations, since
|
// This is the steady state. It happens when the ReplicaSet doesn't have any expectations, since
|
||||||
// we do a periodic relist every 30s. If the generations differ but the replicas are
|
// we do a periodic relist every 30s. If the generations differ but the replicas are
|
||||||
// the same, a caller might've resized to the same replica count.
|
// the same, a caller might've resized to the same replica count.
|
||||||
if int(rs.Status.Replicas) == numReplicas &&
|
if int(rs.Status.Replicas) == numReplicas &&
|
||||||
int(rs.Status.FullyLabeledReplicas) == numFullyLabeledReplicas &&
|
int(rs.Status.FullyLabeledReplicas) == numFullyLabeledReplicas &&
|
||||||
int(rs.Status.ReadyReplicas) == numReadyReplicas &&
|
int(rs.Status.ReadyReplicas) == numReadyReplicas &&
|
||||||
|
int(rs.Status.AvailableReplicas) == numAvailableReplicas &&
|
||||||
rs.Generation == rs.Status.ObservedGeneration {
|
rs.Generation == rs.Status.ObservedGeneration {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -49,12 +50,14 @@ func updateReplicaCount(rsClient client.ReplicaSetInterface, rs extensions.Repli
|
|||||||
fmt.Sprintf("replicas %d->%d (need %d), ", rs.Status.Replicas, numReplicas, rs.Spec.Replicas) +
|
fmt.Sprintf("replicas %d->%d (need %d), ", rs.Status.Replicas, numReplicas, rs.Spec.Replicas) +
|
||||||
fmt.Sprintf("fullyLabeledReplicas %d->%d, ", rs.Status.FullyLabeledReplicas, numFullyLabeledReplicas) +
|
fmt.Sprintf("fullyLabeledReplicas %d->%d, ", rs.Status.FullyLabeledReplicas, numFullyLabeledReplicas) +
|
||||||
fmt.Sprintf("readyReplicas %d->%d, ", rs.Status.ReadyReplicas, numReadyReplicas) +
|
fmt.Sprintf("readyReplicas %d->%d, ", rs.Status.ReadyReplicas, numReadyReplicas) +
|
||||||
|
fmt.Sprintf("availableReplicas %d->%d, ", rs.Status.AvailableReplicas, numAvailableReplicas) +
|
||||||
fmt.Sprintf("sequence No: %v->%v", rs.Status.ObservedGeneration, generation))
|
fmt.Sprintf("sequence No: %v->%v", rs.Status.ObservedGeneration, generation))
|
||||||
|
|
||||||
rs.Status = extensions.ReplicaSetStatus{
|
rs.Status = extensions.ReplicaSetStatus{
|
||||||
Replicas: int32(numReplicas),
|
Replicas: int32(numReplicas),
|
||||||
FullyLabeledReplicas: int32(numFullyLabeledReplicas),
|
FullyLabeledReplicas: int32(numFullyLabeledReplicas),
|
||||||
ReadyReplicas: int32(numReadyReplicas),
|
ReadyReplicas: int32(numReadyReplicas),
|
||||||
|
AvailableReplicas: int32(numAvailableReplicas),
|
||||||
ObservedGeneration: generation,
|
ObservedGeneration: generation,
|
||||||
}
|
}
|
||||||
_, updateErr = rsClient.UpdateStatus(rs)
|
_, updateErr = rsClient.UpdateStatus(rs)
|
||||||
|
@ -717,6 +717,7 @@ func (rm *ReplicationManager) syncReplicationController(key string) error {
|
|||||||
// matching pods must be part of the filteredPods.
|
// matching pods must be part of the filteredPods.
|
||||||
fullyLabeledReplicasCount := 0
|
fullyLabeledReplicasCount := 0
|
||||||
readyReplicasCount := 0
|
readyReplicasCount := 0
|
||||||
|
availableReplicasCount := 0
|
||||||
templateLabel := labels.Set(rc.Spec.Template.Labels).AsSelectorPreValidated()
|
templateLabel := labels.Set(rc.Spec.Template.Labels).AsSelectorPreValidated()
|
||||||
for _, pod := range filteredPods {
|
for _, pod := range filteredPods {
|
||||||
if templateLabel.Matches(labels.Set(pod.Labels)) {
|
if templateLabel.Matches(labels.Set(pod.Labels)) {
|
||||||
@ -724,11 +725,21 @@ func (rm *ReplicationManager) syncReplicationController(key string) error {
|
|||||||
}
|
}
|
||||||
if api.IsPodReady(pod) {
|
if api.IsPodReady(pod) {
|
||||||
readyReplicasCount++
|
readyReplicasCount++
|
||||||
|
if api.IsPodAvailable(pod, rc.Spec.MinReadySeconds, unversioned.Now()) {
|
||||||
|
availableReplicasCount++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always updates status as pods come up or die.
|
// Always updates status as pods come up or die.
|
||||||
if err := updateReplicaCount(rm.kubeClient.Core().ReplicationControllers(rc.Namespace), rc, len(filteredPods), fullyLabeledReplicasCount, readyReplicasCount); err != nil {
|
if err := updateReplicaCount(
|
||||||
|
rm.kubeClient.Core().ReplicationControllers(rc.Namespace),
|
||||||
|
rc,
|
||||||
|
len(filteredPods),
|
||||||
|
fullyLabeledReplicasCount,
|
||||||
|
readyReplicasCount,
|
||||||
|
availableReplicasCount,
|
||||||
|
); err != nil {
|
||||||
// Multiple things could lead to this update failing. Returning an error causes a requeue without forcing a hotloop
|
// Multiple things could lead to this update failing. Returning an error causes a requeue without forcing a hotloop
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -97,10 +97,14 @@ func newReplicationController(replicas int) *api.ReplicationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a pod with the given phase for the given rc (same selectors and namespace).
|
// create a pod with the given phase for the given rc (same selectors and namespace).
|
||||||
func newPod(name string, rc *api.ReplicationController, status api.PodPhase) *api.Pod {
|
func newPod(name string, rc *api.ReplicationController, status api.PodPhase, lastTransitionTime *unversioned.Time) *api.Pod {
|
||||||
var conditions []api.PodCondition
|
var conditions []api.PodCondition
|
||||||
if status == api.PodRunning {
|
if status == api.PodRunning {
|
||||||
conditions = append(conditions, api.PodCondition{Type: api.PodReady, Status: api.ConditionTrue})
|
condition := api.PodCondition{Type: api.PodReady, Status: api.ConditionTrue}
|
||||||
|
if lastTransitionTime != nil {
|
||||||
|
condition.LastTransitionTime = *lastTransitionTime
|
||||||
|
}
|
||||||
|
conditions = append(conditions, condition)
|
||||||
}
|
}
|
||||||
return &api.Pod{
|
return &api.Pod{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
@ -118,7 +122,7 @@ func newPodList(store cache.Store, count int, status api.PodPhase, rc *api.Repli
|
|||||||
var trueVar = true
|
var trueVar = true
|
||||||
controllerReference := api.OwnerReference{UID: rc.UID, APIVersion: "v1", Kind: "ReplicationController", Name: rc.Name, Controller: &trueVar}
|
controllerReference := api.OwnerReference{UID: rc.UID, APIVersion: "v1", Kind: "ReplicationController", Name: rc.Name, Controller: &trueVar}
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
pod := newPod(fmt.Sprintf("%s%d", name, i), rc, status)
|
pod := newPod(fmt.Sprintf("%s%d", name, i), rc, status, nil)
|
||||||
pod.OwnerReferences = []api.OwnerReference{controllerReference}
|
pod.OwnerReferences = []api.OwnerReference{controllerReference}
|
||||||
if store != nil {
|
if store != nil {
|
||||||
store.Add(pod)
|
store.Add(pod)
|
||||||
@ -247,7 +251,7 @@ func TestStatusUpdatesWithoutReplicasChange(t *testing.T) {
|
|||||||
activePods := 5
|
activePods := 5
|
||||||
rc := newReplicationController(activePods)
|
rc := newReplicationController(activePods)
|
||||||
manager.rcStore.Indexer.Add(rc)
|
manager.rcStore.Indexer.Add(rc)
|
||||||
rc.Status = api.ReplicationControllerStatus{Replicas: int32(activePods), ReadyReplicas: int32(activePods)}
|
rc.Status = api.ReplicationControllerStatus{Replicas: int32(activePods), ReadyReplicas: int32(activePods), AvailableReplicas: int32(activePods)}
|
||||||
newPodList(manager.podStore.Indexer, activePods, api.PodRunning, rc, "pod")
|
newPodList(manager.podStore.Indexer, activePods, api.PodRunning, rc, "pod")
|
||||||
|
|
||||||
fakePodControl := controller.FakePodControl{}
|
fakePodControl := controller.FakePodControl{}
|
||||||
@ -288,7 +292,7 @@ func TestControllerUpdateReplicas(t *testing.T) {
|
|||||||
// Status.Replica should update to match number of pods in system, 1 new pod should be created.
|
// Status.Replica should update to match number of pods in system, 1 new pod should be created.
|
||||||
rc := newReplicationController(5)
|
rc := newReplicationController(5)
|
||||||
manager.rcStore.Indexer.Add(rc)
|
manager.rcStore.Indexer.Add(rc)
|
||||||
rc.Status = api.ReplicationControllerStatus{Replicas: 2, FullyLabeledReplicas: 6, ReadyReplicas: 2, ObservedGeneration: 0}
|
rc.Status = api.ReplicationControllerStatus{Replicas: 2, FullyLabeledReplicas: 6, ReadyReplicas: 2, AvailableReplicas: 2, ObservedGeneration: 0}
|
||||||
rc.Generation = 1
|
rc.Generation = 1
|
||||||
newPodList(manager.podStore.Indexer, 2, api.PodRunning, rc, "pod")
|
newPodList(manager.podStore.Indexer, 2, api.PodRunning, rc, "pod")
|
||||||
rcCopy := *rc
|
rcCopy := *rc
|
||||||
@ -309,7 +313,7 @@ func TestControllerUpdateReplicas(t *testing.T) {
|
|||||||
// 2. Status.FullyLabeledReplicas should equal to the number of pods that
|
// 2. Status.FullyLabeledReplicas should equal to the number of pods that
|
||||||
// has the extra labels, i.e., 2.
|
// has the extra labels, i.e., 2.
|
||||||
// 3. Every update to the status should include the Generation of the spec.
|
// 3. Every update to the status should include the Generation of the spec.
|
||||||
rc.Status = api.ReplicationControllerStatus{Replicas: 4, ReadyReplicas: 4, ObservedGeneration: 1}
|
rc.Status = api.ReplicationControllerStatus{Replicas: 4, ReadyReplicas: 4, AvailableReplicas: 4, ObservedGeneration: 1}
|
||||||
|
|
||||||
decRc := runtime.EncodeOrDie(testapi.Default.Codec(), rc)
|
decRc := runtime.EncodeOrDie(testapi.Default.Codec(), rc)
|
||||||
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name)+"/status", "PUT", &decRc)
|
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name)+"/status", "PUT", &decRc)
|
||||||
@ -337,12 +341,14 @@ func TestSyncReplicationControllerDormancy(t *testing.T) {
|
|||||||
// Creates a replica and sets expectations
|
// Creates a replica and sets expectations
|
||||||
controllerSpec.Status.Replicas = 1
|
controllerSpec.Status.Replicas = 1
|
||||||
controllerSpec.Status.ReadyReplicas = 1
|
controllerSpec.Status.ReadyReplicas = 1
|
||||||
|
controllerSpec.Status.AvailableReplicas = 1
|
||||||
manager.syncReplicationController(getKey(controllerSpec, t))
|
manager.syncReplicationController(getKey(controllerSpec, t))
|
||||||
validateSyncReplication(t, &fakePodControl, 1, 0, 0)
|
validateSyncReplication(t, &fakePodControl, 1, 0, 0)
|
||||||
|
|
||||||
// Expectations prevents replicas but not an update on status
|
// Expectations prevents replicas but not an update on status
|
||||||
controllerSpec.Status.Replicas = 0
|
controllerSpec.Status.Replicas = 0
|
||||||
controllerSpec.Status.ReadyReplicas = 0
|
controllerSpec.Status.ReadyReplicas = 0
|
||||||
|
controllerSpec.Status.AvailableReplicas = 0
|
||||||
fakePodControl.Clear()
|
fakePodControl.Clear()
|
||||||
manager.syncReplicationController(getKey(controllerSpec, t))
|
manager.syncReplicationController(getKey(controllerSpec, t))
|
||||||
validateSyncReplication(t, &fakePodControl, 0, 0, 0)
|
validateSyncReplication(t, &fakePodControl, 0, 0, 0)
|
||||||
@ -358,6 +364,7 @@ func TestSyncReplicationControllerDormancy(t *testing.T) {
|
|||||||
manager.expectations.CreationObserved(rcKey)
|
manager.expectations.CreationObserved(rcKey)
|
||||||
controllerSpec.Status.Replicas = 1
|
controllerSpec.Status.Replicas = 1
|
||||||
controllerSpec.Status.ReadyReplicas = 1
|
controllerSpec.Status.ReadyReplicas = 1
|
||||||
|
controllerSpec.Status.AvailableReplicas = 1
|
||||||
fakePodControl.Clear()
|
fakePodControl.Clear()
|
||||||
fakePodControl.Err = fmt.Errorf("Fake Error")
|
fakePodControl.Err = fmt.Errorf("Fake Error")
|
||||||
|
|
||||||
@ -634,7 +641,7 @@ func TestControllerUpdateStatusWithFailure(t *testing.T) {
|
|||||||
})
|
})
|
||||||
fakeRCClient := c.Core().ReplicationControllers("default")
|
fakeRCClient := c.Core().ReplicationControllers("default")
|
||||||
numReplicas := 10
|
numReplicas := 10
|
||||||
updateReplicaCount(fakeRCClient, *rc, numReplicas, 0, 0)
|
updateReplicaCount(fakeRCClient, *rc, numReplicas, 0, 0, 0)
|
||||||
updates, gets := 0, 0
|
updates, gets := 0, 0
|
||||||
for _, a := range c.Actions() {
|
for _, a := range c.Actions() {
|
||||||
if a.GetResource().Resource != "replicationcontrollers" {
|
if a.GetResource().Resource != "replicationcontrollers" {
|
||||||
@ -1149,7 +1156,7 @@ func TestDoNotPatchPodWithOtherControlRef(t *testing.T) {
|
|||||||
var trueVar = true
|
var trueVar = true
|
||||||
otherControllerReference := api.OwnerReference{UID: uuid.NewUUID(), APIVersion: "v1", Kind: "ReplicationController", Name: "AnotherRC", Controller: &trueVar}
|
otherControllerReference := api.OwnerReference{UID: uuid.NewUUID(), APIVersion: "v1", Kind: "ReplicationController", Name: "AnotherRC", Controller: &trueVar}
|
||||||
// add to podStore a matching Pod controlled by another controller. Expect no patch.
|
// add to podStore a matching Pod controlled by another controller. Expect no patch.
|
||||||
pod := newPod("pod", rc, api.PodRunning)
|
pod := newPod("pod", rc, api.PodRunning, nil)
|
||||||
pod.OwnerReferences = []api.OwnerReference{otherControllerReference}
|
pod.OwnerReferences = []api.OwnerReference{otherControllerReference}
|
||||||
manager.podStore.Indexer.Add(pod)
|
manager.podStore.Indexer.Add(pod)
|
||||||
err := manager.syncReplicationController(getKey(rc, t))
|
err := manager.syncReplicationController(getKey(rc, t))
|
||||||
@ -1168,7 +1175,7 @@ func TestPatchPodWithOtherOwnerRef(t *testing.T) {
|
|||||||
// ref, but has an owner ref pointing to other object. Expect a patch to
|
// ref, but has an owner ref pointing to other object. Expect a patch to
|
||||||
// take control of it.
|
// take control of it.
|
||||||
unrelatedOwnerReference := api.OwnerReference{UID: uuid.NewUUID(), APIVersion: "batch/v1", Kind: "Job", Name: "Job"}
|
unrelatedOwnerReference := api.OwnerReference{UID: uuid.NewUUID(), APIVersion: "batch/v1", Kind: "Job", Name: "Job"}
|
||||||
pod := newPod("pod", rc, api.PodRunning)
|
pod := newPod("pod", rc, api.PodRunning, nil)
|
||||||
pod.OwnerReferences = []api.OwnerReference{unrelatedOwnerReference}
|
pod.OwnerReferences = []api.OwnerReference{unrelatedOwnerReference}
|
||||||
manager.podStore.Indexer.Add(pod)
|
manager.podStore.Indexer.Add(pod)
|
||||||
|
|
||||||
@ -1187,7 +1194,7 @@ func TestPatchPodWithCorrectOwnerRef(t *testing.T) {
|
|||||||
// add to podStore a matching pod that has an ownerRef pointing to the rc,
|
// add to podStore a matching pod that has an ownerRef pointing to the rc,
|
||||||
// but ownerRef.Controller is false. Expect a patch to take control it.
|
// but ownerRef.Controller is false. Expect a patch to take control it.
|
||||||
rcOwnerReference := api.OwnerReference{UID: rc.UID, APIVersion: "v1", Kind: "ReplicationController", Name: rc.Name}
|
rcOwnerReference := api.OwnerReference{UID: rc.UID, APIVersion: "v1", Kind: "ReplicationController", Name: rc.Name}
|
||||||
pod := newPod("pod", rc, api.PodRunning)
|
pod := newPod("pod", rc, api.PodRunning, nil)
|
||||||
pod.OwnerReferences = []api.OwnerReference{rcOwnerReference}
|
pod.OwnerReferences = []api.OwnerReference{rcOwnerReference}
|
||||||
manager.podStore.Indexer.Add(pod)
|
manager.podStore.Indexer.Add(pod)
|
||||||
|
|
||||||
@ -1205,8 +1212,8 @@ func TestPatchPodFails(t *testing.T) {
|
|||||||
manager.rcStore.Indexer.Add(rc)
|
manager.rcStore.Indexer.Add(rc)
|
||||||
// add to podStore two matching pods. Expect two patches to take control
|
// add to podStore two matching pods. Expect two patches to take control
|
||||||
// them.
|
// them.
|
||||||
manager.podStore.Indexer.Add(newPod("pod1", rc, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod1", rc, api.PodRunning, nil))
|
||||||
manager.podStore.Indexer.Add(newPod("pod2", rc, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod2", rc, api.PodRunning, nil))
|
||||||
// let both patches fail. The rc manager will assume it fails to take
|
// let both patches fail. The rc manager will assume it fails to take
|
||||||
// control of the pods and create new ones.
|
// control of the pods and create new ones.
|
||||||
fakePodControl.Err = fmt.Errorf("Fake Error")
|
fakePodControl.Err = fmt.Errorf("Fake Error")
|
||||||
@ -1224,9 +1231,9 @@ func TestPatchExtraPodsThenDelete(t *testing.T) {
|
|||||||
manager.rcStore.Indexer.Add(rc)
|
manager.rcStore.Indexer.Add(rc)
|
||||||
// add to podStore three matching pods. Expect three patches to take control
|
// add to podStore three matching pods. Expect three patches to take control
|
||||||
// them, and later delete one of them.
|
// them, and later delete one of them.
|
||||||
manager.podStore.Indexer.Add(newPod("pod1", rc, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod1", rc, api.PodRunning, nil))
|
||||||
manager.podStore.Indexer.Add(newPod("pod2", rc, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod2", rc, api.PodRunning, nil))
|
||||||
manager.podStore.Indexer.Add(newPod("pod3", rc, api.PodRunning))
|
manager.podStore.Indexer.Add(newPod("pod3", rc, api.PodRunning, nil))
|
||||||
err := manager.syncReplicationController(getKey(rc, t))
|
err := manager.syncReplicationController(getKey(rc, t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -1240,7 +1247,7 @@ func TestUpdateLabelsRemoveControllerRef(t *testing.T) {
|
|||||||
rc := newReplicationController(2)
|
rc := newReplicationController(2)
|
||||||
manager.rcStore.Indexer.Add(rc)
|
manager.rcStore.Indexer.Add(rc)
|
||||||
// put one pod in the podStore
|
// put one pod in the podStore
|
||||||
pod := newPod("pod", rc, api.PodRunning)
|
pod := newPod("pod", rc, api.PodRunning, nil)
|
||||||
pod.ResourceVersion = "1"
|
pod.ResourceVersion = "1"
|
||||||
var trueVar = true
|
var trueVar = true
|
||||||
rcOwnerReference := api.OwnerReference{UID: rc.UID, APIVersion: "v1", Kind: "ReplicationController", Name: rc.Name, Controller: &trueVar}
|
rcOwnerReference := api.OwnerReference{UID: rc.UID, APIVersion: "v1", Kind: "ReplicationController", Name: rc.Name, Controller: &trueVar}
|
||||||
@ -1312,7 +1319,7 @@ func TestDoNotAdoptOrCreateIfBeingDeleted(t *testing.T) {
|
|||||||
now := unversioned.Now()
|
now := unversioned.Now()
|
||||||
rc.DeletionTimestamp = &now
|
rc.DeletionTimestamp = &now
|
||||||
manager.rcStore.Indexer.Add(rc)
|
manager.rcStore.Indexer.Add(rc)
|
||||||
pod1 := newPod("pod1", rc, api.PodRunning)
|
pod1 := newPod("pod1", rc, api.PodRunning, nil)
|
||||||
manager.podStore.Indexer.Add(pod1)
|
manager.podStore.Indexer.Add(pod1)
|
||||||
|
|
||||||
// no patch, no create
|
// no patch, no create
|
||||||
@ -1338,7 +1345,7 @@ func TestReadyReplicas(t *testing.T) {
|
|||||||
|
|
||||||
// Status.Replica should update to match number of pods in system, 1 new pod should be created.
|
// Status.Replica should update to match number of pods in system, 1 new pod should be created.
|
||||||
rc := newReplicationController(2)
|
rc := newReplicationController(2)
|
||||||
rc.Status = api.ReplicationControllerStatus{Replicas: 2, ReadyReplicas: 0, ObservedGeneration: 1}
|
rc.Status = api.ReplicationControllerStatus{Replicas: 2, ReadyReplicas: 0, AvailableReplicas: 0, ObservedGeneration: 1}
|
||||||
rc.Generation = 1
|
rc.Generation = 1
|
||||||
manager.rcStore.Indexer.Add(rc)
|
manager.rcStore.Indexer.Add(rc)
|
||||||
|
|
||||||
@ -1355,7 +1362,55 @@ func TestReadyReplicas(t *testing.T) {
|
|||||||
manager.syncReplicationController(getKey(rc, t))
|
manager.syncReplicationController(getKey(rc, t))
|
||||||
|
|
||||||
// ReadyReplicas should go from 0 to 2.
|
// ReadyReplicas should go from 0 to 2.
|
||||||
rc.Status = api.ReplicationControllerStatus{Replicas: 2, ReadyReplicas: 2, ObservedGeneration: 1}
|
rc.Status = api.ReplicationControllerStatus{Replicas: 2, ReadyReplicas: 2, AvailableReplicas: 2, ObservedGeneration: 1}
|
||||||
|
|
||||||
|
decRc := runtime.EncodeOrDie(testapi.Default.Codec(), rc)
|
||||||
|
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name)+"/status", "PUT", &decRc)
|
||||||
|
validateSyncReplication(t, &fakePodControl, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAvailableReplicas(t *testing.T) {
|
||||||
|
// This is a happy server just to record the PUT request we expect for status.Replicas
|
||||||
|
fakeHandler := utiltesting.FakeHandler{
|
||||||
|
StatusCode: 200,
|
||||||
|
ResponseBody: "{}",
|
||||||
|
}
|
||||||
|
testServer := httptest.NewServer(&fakeHandler)
|
||||||
|
defer testServer.Close()
|
||||||
|
|
||||||
|
c := clientset.NewForConfigOrDie(&restclient.Config{Host: testServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}})
|
||||||
|
manager := NewReplicationManagerFromClient(c, controller.NoResyncPeriodFunc, BurstReplicas, 0)
|
||||||
|
manager.podStoreSynced = alwaysReady
|
||||||
|
|
||||||
|
// Status.Replica should update to match number of pods in system, 1 new pod should be created.
|
||||||
|
rc := newReplicationController(2)
|
||||||
|
rc.Status = api.ReplicationControllerStatus{Replicas: 2, ReadyReplicas: 0, ObservedGeneration: 1}
|
||||||
|
rc.Generation = 1
|
||||||
|
// minReadySeconds set to 15s
|
||||||
|
rc.Spec.MinReadySeconds = 15
|
||||||
|
manager.rcStore.Indexer.Add(rc)
|
||||||
|
|
||||||
|
// First pod becomes ready 20s ago
|
||||||
|
moment := unversioned.Time{Time: time.Now().Add(-2e10)}
|
||||||
|
pod := newPod("pod", rc, api.PodRunning, &moment)
|
||||||
|
manager.podStore.Indexer.Add(pod)
|
||||||
|
|
||||||
|
// Second pod becomes ready now
|
||||||
|
otherMoment := unversioned.Now()
|
||||||
|
otherPod := newPod("otherPod", rc, api.PodRunning, &otherMoment)
|
||||||
|
manager.podStore.Indexer.Add(otherPod)
|
||||||
|
|
||||||
|
// This response body is just so we don't err out decoding the http response
|
||||||
|
response := runtime.EncodeOrDie(testapi.Default.Codec(), &api.ReplicationController{})
|
||||||
|
fakeHandler.ResponseBody = response
|
||||||
|
|
||||||
|
fakePodControl := controller.FakePodControl{}
|
||||||
|
manager.podControl = &fakePodControl
|
||||||
|
|
||||||
|
// The controller should see only one available pod.
|
||||||
|
manager.syncReplicationController(getKey(rc, t))
|
||||||
|
|
||||||
|
rc.Status = api.ReplicationControllerStatus{Replicas: 2, ReadyReplicas: 2, AvailableReplicas: 1, ObservedGeneration: 1}
|
||||||
|
|
||||||
decRc := runtime.EncodeOrDie(testapi.Default.Codec(), rc)
|
decRc := runtime.EncodeOrDie(testapi.Default.Codec(), rc)
|
||||||
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name)+"/status", "PUT", &decRc)
|
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name)+"/status", "PUT", &decRc)
|
||||||
|
@ -27,13 +27,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// updateReplicaCount attempts to update the Status.Replicas of the given controller, with a single GET/PUT retry.
|
// updateReplicaCount attempts to update the Status.Replicas of the given controller, with a single GET/PUT retry.
|
||||||
func updateReplicaCount(rcClient unversionedcore.ReplicationControllerInterface, controller api.ReplicationController, numReplicas, numFullyLabeledReplicas, numReadyReplicas int) (updateErr error) {
|
func updateReplicaCount(rcClient unversionedcore.ReplicationControllerInterface, controller api.ReplicationController, numReplicas, numFullyLabeledReplicas, numReadyReplicas, numAvailableReplicas int) (updateErr error) {
|
||||||
// This is the steady state. It happens when the rc doesn't have any expectations, since
|
// This is the steady state. It happens when the rc doesn't have any expectations, since
|
||||||
// we do a periodic relist every 30s. If the generations differ but the replicas are
|
// we do a periodic relist every 30s. If the generations differ but the replicas are
|
||||||
// the same, a caller might've resized to the same replica count.
|
// the same, a caller might've resized to the same replica count.
|
||||||
if int(controller.Status.Replicas) == numReplicas &&
|
if int(controller.Status.Replicas) == numReplicas &&
|
||||||
int(controller.Status.FullyLabeledReplicas) == numFullyLabeledReplicas &&
|
int(controller.Status.FullyLabeledReplicas) == numFullyLabeledReplicas &&
|
||||||
int(controller.Status.ReadyReplicas) == numReadyReplicas &&
|
int(controller.Status.ReadyReplicas) == numReadyReplicas &&
|
||||||
|
int(controller.Status.AvailableReplicas) == numAvailableReplicas &&
|
||||||
controller.Generation == controller.Status.ObservedGeneration {
|
controller.Generation == controller.Status.ObservedGeneration {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -49,12 +50,14 @@ func updateReplicaCount(rcClient unversionedcore.ReplicationControllerInterface,
|
|||||||
fmt.Sprintf("replicas %d->%d (need %d), ", controller.Status.Replicas, numReplicas, controller.Spec.Replicas) +
|
fmt.Sprintf("replicas %d->%d (need %d), ", controller.Status.Replicas, numReplicas, controller.Spec.Replicas) +
|
||||||
fmt.Sprintf("fullyLabeledReplicas %d->%d, ", controller.Status.FullyLabeledReplicas, numFullyLabeledReplicas) +
|
fmt.Sprintf("fullyLabeledReplicas %d->%d, ", controller.Status.FullyLabeledReplicas, numFullyLabeledReplicas) +
|
||||||
fmt.Sprintf("readyReplicas %d->%d, ", controller.Status.ReadyReplicas, numReadyReplicas) +
|
fmt.Sprintf("readyReplicas %d->%d, ", controller.Status.ReadyReplicas, numReadyReplicas) +
|
||||||
|
fmt.Sprintf("availableReplicas %d->%d, ", controller.Status.AvailableReplicas, numAvailableReplicas) +
|
||||||
fmt.Sprintf("sequence No: %v->%v", controller.Status.ObservedGeneration, generation))
|
fmt.Sprintf("sequence No: %v->%v", controller.Status.ObservedGeneration, generation))
|
||||||
|
|
||||||
rc.Status = api.ReplicationControllerStatus{
|
rc.Status = api.ReplicationControllerStatus{
|
||||||
Replicas: int32(numReplicas),
|
Replicas: int32(numReplicas),
|
||||||
FullyLabeledReplicas: int32(numFullyLabeledReplicas),
|
FullyLabeledReplicas: int32(numFullyLabeledReplicas),
|
||||||
ReadyReplicas: int32(numReadyReplicas),
|
ReadyReplicas: int32(numReadyReplicas),
|
||||||
|
AvailableReplicas: int32(numAvailableReplicas),
|
||||||
ObservedGeneration: generation,
|
ObservedGeneration: generation,
|
||||||
}
|
}
|
||||||
_, updateErr = rcClient.UpdateStatus(rc)
|
_, updateErr = rcClient.UpdateStatus(rc)
|
||||||
|
Loading…
Reference in New Issue
Block a user