Commit 5af1b252 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #32771 from kargakis/minReadySecondsForRS

Automatic merge from submit-queue MinReadySeconds / AvailableReplicas for ReplicaSets This PR adds minReadySeconds and availableReplicas for replica sets / replication controllers Partially addresses https://github.com/kubernetes/kubernetes/issues/28381 cc: @mfojtik @bgrant0607 for the api changes, @janetkuo for controller changes
parents ada3600f d8dc2aac
......@@ -10302,6 +10302,11 @@
"format": "int32",
"description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller"
},
"minReadySeconds": {
"type": "integer",
"format": "int32",
"description": "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)"
},
"selector": {
"$ref": "v1beta1.LabelSelector",
"description": "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. Label keys and values that must match in order to be controlled by this replica set. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors"
......@@ -10334,6 +10339,11 @@
"format": "int32",
"description": "The number of ready replicas for this replica set."
},
"availableReplicas": {
"type": "integer",
"format": "int32",
"description": "The number of available replicas (ready for at least minReadySeconds) for this replica set."
},
"observedGeneration": {
"type": "integer",
"format": "int64",
......
......@@ -19089,6 +19089,11 @@
"format": "int32",
"description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller"
},
"minReadySeconds": {
"type": "integer",
"format": "int32",
"description": "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)"
},
"selector": {
"type": "object",
"description": "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. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors"
......@@ -19121,6 +19126,11 @@
"format": "int32",
"description": "The number of ready replicas for this replication controller."
},
"availableReplicas": {
"type": "integer",
"format": "int32",
"description": "The number of available replicas (ready for at least minReadySeconds) for this replication controller."
},
"observedGeneration": {
"type": "integer",
"format": "int64",
......
......@@ -1769,6 +1769,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">minReadySeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">selector</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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. Label keys and values that must match in order to be controlled by this replica set. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors">http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
......@@ -5455,6 +5462,13 @@ Both these may change in the future. Incoming requests are matched against the h
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">availableReplicas</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The number of available replicas (ready for at least minReadySeconds) for this replica set.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">observedGeneration</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">ObservedGeneration reflects the generation of the most recently observed ReplicaSet.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
......@@ -6597,7 +6611,7 @@ Both these may change in the future. Incoming requests are matched against the h
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-09-24 08:32:15 UTC
Last updated 2016-09-28 09:24:38 UTC
</div>
</div>
</body>
......
......@@ -4732,6 +4732,13 @@ The resulting set of endpoints can be viewed as:<br>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">minReadySeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">selector</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors">http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
......@@ -6171,6 +6178,13 @@ The resulting set of endpoints can be viewed as:<br>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">availableReplicas</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The number of available replicas (ready for at least minReadySeconds) for this replication controller.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">observedGeneration</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">ObservedGeneration reflects the generation of the most recently observed replication controller.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
......@@ -8475,7 +8489,7 @@ The resulting set of endpoints can be viewed as:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-09-24 08:31:51 UTC
Last updated 2016-09-28 09:23:57 UTC
</div>
</div>
</body>
......
......@@ -17,6 +17,8 @@ limitations under the License.
package api
import (
"time"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
)
......@@ -74,6 +76,24 @@ func GetExistingContainerStatus(statuses []ContainerStatus, name string) Contain
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.
func IsPodReady(pod *Pod) bool {
return IsPodReadyConditionTrue(pod.Status)
......
......@@ -18,8 +18,10 @@ package api
import (
"testing"
"time"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
)
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)
}
}
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)
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -1717,6 +1717,11 @@ type ReplicationControllerSpec struct {
// Replicas is the number of desired 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 map[string]string `json:"selector"`
......@@ -1743,6 +1748,9 @@ type ReplicationControllerStatus struct {
// The number of ready replicas for this replication controller.
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 int64 `json:"observedGeneration,omitempty"`
}
......
......@@ -309,6 +309,8 @@ func Convert_v1_ReplicationControllerStatus_to_extensions_ReplicaSetStatus(in *R
}
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
......@@ -345,6 +347,7 @@ func Convert_extensions_ReplicaSetSpec_to_v1_ReplicationControllerSpec(in *exten
}
out.Replicas = new(int32)
*out.Replicas = in.Replicas
out.MinReadySeconds = in.MinReadySeconds
var invalidErr error
if in.Selector != nil {
invalidErr = api.Convert_unversioned_LabelSelector_to_map(in.Selector, &out.Selector, s)
......@@ -362,12 +365,15 @@ func Convert_extensions_ReplicaSetStatus_to_v1_ReplicationControllerStatus(in *e
}
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
func Convert_api_ReplicationControllerSpec_To_v1_ReplicationControllerSpec(in *api.ReplicationControllerSpec, out *ReplicationControllerSpec, s conversion.Scope) error {
out.Replicas = &in.Replicas
out.MinReadySeconds = in.MinReadySeconds
out.Selector = in.Selector
if in.Template != nil {
out.Template = new(PodTemplateSpec)
......@@ -382,6 +388,7 @@ func Convert_api_ReplicationControllerSpec_To_v1_ReplicationControllerSpec(in *a
func Convert_v1_ReplicationControllerSpec_To_api_ReplicationControllerSpec(in *ReplicationControllerSpec, out *api.ReplicationControllerSpec, s conversion.Scope) error {
out.Replicas = *in.Replicas
out.MinReadySeconds = in.MinReadySeconds
out.Selector = in.Selector
if in.Template != nil {
out.Template = new(api.PodTemplateSpec)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2479,6 +2479,11 @@ message ReplicationControllerSpec {
// More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller
optional int32 replicas = 1;
// 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)
optional int32 minReadySeconds = 4;
// 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.
// Label keys and values that must match in order to be controlled by this replication
......@@ -2505,6 +2510,9 @@ message ReplicationControllerStatus {
// The number of ready replicas for this replication controller.
optional int32 readyReplicas = 4;
// The number of available replicas (ready for at least minReadySeconds) for this replication controller.
optional int32 availableReplicas = 5;
// ObservedGeneration reflects the generation of the most recently observed replication controller.
optional int64 observedGeneration = 3;
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -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
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" protobuf:"varint,4,opt,name=minReadySeconds"`
// 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.
// 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.
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" protobuf:"varint,5,opt,name=availableReplicas"`
// ObservedGeneration reflects the generation of the most recently observed replication controller.
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
}
......
......@@ -1454,10 +1454,11 @@ func (ReplicationControllerList) SwaggerDoc() map[string]string {
}
var map_ReplicationControllerSpec = map[string]string{
"": "ReplicationControllerSpec is the specification of a replication controller.",
"replicas": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller",
"selector": "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. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
"template": "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template",
"": "ReplicationControllerSpec is the specification of a replication controller.",
"replicas": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller",
"minReadySeconds": "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)",
"selector": "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. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
"template": "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template",
}
func (ReplicationControllerSpec) SwaggerDoc() map[string]string {
......@@ -1469,6 +1470,7 @@ var map_ReplicationControllerStatus = map[string]string{
"replicas": "Replicas is the most recently oberved number of replicas. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller",
"fullyLabeledReplicas": "The number of pods that have labels matching the labels of the pod template of the replication controller.",
"readyReplicas": "The number of ready replicas for this replication controller.",
"availableReplicas": "The number of available replicas (ready for at least minReadySeconds) for this replication controller.",
"observedGeneration": "ObservedGeneration reflects the generation of the most recently observed replication controller.",
}
......
......@@ -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 {
return err
}
out.MinReadySeconds = in.MinReadySeconds
out.Selector = in.Selector
if in.Template != nil {
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 {
return err
}
out.MinReadySeconds = in.MinReadySeconds
out.Selector = in.Selector
if in.Template != nil {
in, out := &in.Template, &out.Template
......@@ -5689,6 +5691,7 @@ func autoConvert_v1_ReplicationControllerStatus_To_api_ReplicationControllerStat
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
......@@ -5701,6 +5704,7 @@ func autoConvert_api_ReplicationControllerStatus_To_v1_ReplicationControllerStat
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
......
......@@ -2942,6 +2942,7 @@ func DeepCopy_v1_ReplicationControllerSpec(in interface{}, out interface{}, c *c
} else {
out.Replicas = nil
}
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = make(map[string]string)
......@@ -2971,6 +2972,7 @@ func DeepCopy_v1_ReplicationControllerStatus(in interface{}, out interface{}, c
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
......
......@@ -2606,6 +2606,8 @@ func ValidateReplicationControllerStatusUpdate(controller, oldController *api.Re
statusPath := field.NewPath("status")
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.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"))...)
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.
func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec, fldPath *field.Path) 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, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
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)
out := out.(*ReplicationControllerSpec)
out.Replicas = in.Replicas
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = make(map[string]string)
......@@ -3023,6 +3024,7 @@ func DeepCopy_api_ReplicationControllerStatus(in interface{}, out interface{}, c
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -610,6 +610,11 @@ type ReplicaSetSpec struct {
// Replicas is the number of desired 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.
// Must match in order to be controlled.
// If empty, defaulted to labels on pod template.
......@@ -632,6 +637,9 @@ type ReplicaSetStatus struct {
// The number of ready replicas for this replica set.
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 int64 `json:"observedGeneration,omitempty"`
}
......
......@@ -249,6 +249,7 @@ func Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployme
func Convert_extensions_ReplicaSetSpec_To_v1beta1_ReplicaSetSpec(in *extensions.ReplicaSetSpec, out *ReplicaSetSpec, s conversion.Scope) error {
out.Replicas = new(int32)
*out.Replicas = int32(in.Replicas)
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
out.Selector = new(LabelSelector)
if err := Convert_unversioned_LabelSelector_To_v1beta1_LabelSelector(in.Selector, out.Selector, s); err != nil {
......@@ -268,6 +269,7 @@ func Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec(in *ReplicaSetS
if in.Replicas != nil {
out.Replicas = *in.Replicas
}
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
out.Selector = new(unversioned.LabelSelector)
if err := Convert_v1beta1_LabelSelector_To_unversioned_LabelSelector(in.Selector, out.Selector, s); err != nil {
......
......@@ -826,6 +826,11 @@ message ReplicaSetSpec {
// More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller
optional int32 replicas = 1;
// 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)
optional int32 minReadySeconds = 4;
// 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.
// Label keys and values that must match in order to be controlled by this replica set.
......@@ -850,6 +855,9 @@ message ReplicaSetStatus {
// The number of ready replicas for this replica set.
optional int32 readyReplicas = 4;
// The number of available replicas (ready for at least minReadySeconds) for this replica set.
optional int32 availableReplicas = 5;
// ObservedGeneration reflects the generation of the most recently observed ReplicaSet.
optional int64 observedGeneration = 3;
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -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
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" protobuf:"varint,4,opt,name=minReadySeconds"`
// 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.
// 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.
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" protobuf:"varint,5,opt,name=availableReplicas"`
// ObservedGeneration reflects the generation of the most recently observed ReplicaSet.
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
}
......
......@@ -576,10 +576,11 @@ func (ReplicaSetList) SwaggerDoc() map[string]string {
}
var map_ReplicaSetSpec = map[string]string{
"": "ReplicaSetSpec is the specification of a ReplicaSet.",
"replicas": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller",
"selector": "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. Label keys and values that must match in order to be controlled by this replica set. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
"template": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template",
"": "ReplicaSetSpec is the specification of a ReplicaSet.",
"replicas": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller",
"minReadySeconds": "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)",
"selector": "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. Label keys and values that must match in order to be controlled by this replica set. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
"template": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template",
}
func (ReplicaSetSpec) SwaggerDoc() map[string]string {
......@@ -591,6 +592,7 @@ var map_ReplicaSetStatus = map[string]string{
"replicas": "Replicas is the most recently oberved number of replicas. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller",
"fullyLabeledReplicas": "The number of pods that have labels matching the labels of the pod template of the replicaset.",
"readyReplicas": "The number of ready replicas for this replica set.",
"availableReplicas": "The number of available replicas (ready for at least minReadySeconds) for this replica set.",
"observedGeneration": "ObservedGeneration reflects the generation of the most recently observed ReplicaSet.",
}
......
......@@ -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 {
return err
}
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*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 {
return err
}
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(LabelSelector)
......@@ -2278,6 +2280,7 @@ func autoConvert_v1beta1_ReplicaSetStatus_To_extensions_ReplicaSetStatus(in *Rep
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
......@@ -2290,6 +2293,7 @@ func autoConvert_extensions_ReplicaSetStatus_To_v1beta1_ReplicaSetStatus(in *ext
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
......
......@@ -1191,6 +1191,7 @@ func DeepCopy_v1beta1_ReplicaSetSpec(in interface{}, out interface{}, c *convers
} else {
out.Replicas = nil
}
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(LabelSelector)
......@@ -1214,6 +1215,7 @@ func DeepCopy_v1beta1_ReplicaSetStatus(in interface{}, out interface{}, c *conve
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
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.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.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"))...)
return allErrs
}
......@@ -500,6 +502,7 @@ func ValidateReplicaSetSpec(spec *extensions.ReplicaSetSpec, fldPath *field.Path
allErrs := field.ErrorList{}
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 {
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)
out := out.(*ReplicaSetSpec)
out.Replicas = in.Replicas
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(unversioned.LabelSelector)
......@@ -869,6 +870,7 @@ func DeepCopy_extensions_ReplicaSetStatus(in interface{}, out interface{}, c *co
out.Replicas = in.Replicas
out.FullyLabeledReplicas = in.FullyLabeledReplicas
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
return nil
}
......
......@@ -640,6 +640,8 @@ func countAvailablePods(pods []api.Pod, minReadySeconds int32) int32 {
}
// 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 {
if !controller.IsPodActive(pod) {
return false
......
......@@ -658,6 +658,7 @@ func (rsc *ReplicaSetController) syncReplicaSet(key string) error {
// part of the filteredPods.
fullyLabeledReplicasCount := 0
readyReplicasCount := 0
availableReplicasCount := 0
templateLabel := labels.Set(rs.Spec.Template.Labels).AsSelectorPreValidated()
for _, pod := range filteredPods {
if templateLabel.Matches(labels.Set(pod.Labels)) {
......@@ -665,11 +666,21 @@ func (rsc *ReplicaSetController) syncReplicaSet(key string) error {
}
if api.IsPodReady(pod) {
readyReplicasCount++
if api.IsPodAvailable(pod, rs.Spec.MinReadySeconds, unversioned.Now()) {
availableReplicasCount++
}
}
}
// 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
// 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)
......
......@@ -27,13 +27,14 @@ import (
)
// 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
// 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.
if int(rs.Status.Replicas) == numReplicas &&
int(rs.Status.FullyLabeledReplicas) == numFullyLabeledReplicas &&
int(rs.Status.ReadyReplicas) == numReadyReplicas &&
int(rs.Status.AvailableReplicas) == numAvailableReplicas &&
rs.Generation == rs.Status.ObservedGeneration {
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("fullyLabeledReplicas %d->%d, ", rs.Status.FullyLabeledReplicas, numFullyLabeledReplicas) +
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))
rs.Status = extensions.ReplicaSetStatus{
Replicas: int32(numReplicas),
FullyLabeledReplicas: int32(numFullyLabeledReplicas),
ReadyReplicas: int32(numReadyReplicas),
AvailableReplicas: int32(numAvailableReplicas),
ObservedGeneration: generation,
}
_, updateErr = rsClient.UpdateStatus(rs)
......
......@@ -717,6 +717,7 @@ func (rm *ReplicationManager) syncReplicationController(key string) error {
// matching pods must be part of the filteredPods.
fullyLabeledReplicasCount := 0
readyReplicasCount := 0
availableReplicasCount := 0
templateLabel := labels.Set(rc.Spec.Template.Labels).AsSelectorPreValidated()
for _, pod := range filteredPods {
if templateLabel.Matches(labels.Set(pod.Labels)) {
......@@ -724,11 +725,21 @@ func (rm *ReplicationManager) syncReplicationController(key string) error {
}
if api.IsPodReady(pod) {
readyReplicasCount++
if api.IsPodAvailable(pod, rc.Spec.MinReadySeconds, unversioned.Now()) {
availableReplicasCount++
}
}
}
// 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
return err
}
......
......@@ -27,13 +27,14 @@ import (
)
// 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
// 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.
if int(controller.Status.Replicas) == numReplicas &&
int(controller.Status.FullyLabeledReplicas) == numFullyLabeledReplicas &&
int(controller.Status.ReadyReplicas) == numReadyReplicas &&
int(controller.Status.AvailableReplicas) == numAvailableReplicas &&
controller.Generation == controller.Status.ObservedGeneration {
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("fullyLabeledReplicas %d->%d, ", controller.Status.FullyLabeledReplicas, numFullyLabeledReplicas) +
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))
rc.Status = api.ReplicationControllerStatus{
Replicas: int32(numReplicas),
FullyLabeledReplicas: int32(numFullyLabeledReplicas),
ReadyReplicas: int32(numReadyReplicas),
AvailableReplicas: int32(numAvailableReplicas),
ObservedGeneration: generation,
}
_, updateErr = rcClient.UpdateStatus(rc)
......
......@@ -4261,6 +4261,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "int32",
},
},
"minReadySeconds": {
SchemaProps: spec.SchemaProps{
Description: "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)",
Type: []string{"integer"},
Format: "int32",
},
},
"selector": {
SchemaProps: spec.SchemaProps{
Description: "Selector is a label query over pods that should match the replica count. Must match in order to be controlled. If empty, defaulted to labels on pod template. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
......@@ -4306,6 +4313,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "int32",
},
},
"availableReplicas": {
SchemaProps: spec.SchemaProps{
Description: "The number of available replicas (ready for at least minReadySeconds) for this replica set.",
Type: []string{"integer"},
Format: "int32",
},
},
"observedGeneration": {
SchemaProps: spec.SchemaProps{
Description: "ObservedGeneration is the most recent generation observed by the controller.",
......@@ -11520,6 +11534,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "int32",
},
},
"minReadySeconds": {
SchemaProps: spec.SchemaProps{
Description: "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)",
Type: []string{"integer"},
Format: "int32",
},
},
"selector": {
SchemaProps: spec.SchemaProps{
Description: "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. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
......@@ -11572,6 +11593,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "int32",
},
},
"availableReplicas": {
SchemaProps: spec.SchemaProps{
Description: "The number of available replicas (ready for at least minReadySeconds) for this replication controller.",
Type: []string{"integer"},
Format: "int32",
},
},
"observedGeneration": {
SchemaProps: spec.SchemaProps{
Description: "ObservedGeneration reflects the generation of the most recently observed replication controller.",
......@@ -16896,6 +16924,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "int32",
},
},
"minReadySeconds": {
SchemaProps: spec.SchemaProps{
Description: "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)",
Type: []string{"integer"},
Format: "int32",
},
},
"selector": {
SchemaProps: spec.SchemaProps{
Description: "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. Label keys and values that must match in order to be controlled by this replica set. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
......@@ -16940,6 +16975,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "int32",
},
},
"availableReplicas": {
SchemaProps: spec.SchemaProps{
Description: "The number of available replicas (ready for at least minReadySeconds) for this replica set.",
Type: []string{"integer"},
Format: "int32",
},
},
"observedGeneration": {
SchemaProps: spec.SchemaProps{
Description: "ObservedGeneration reflects the generation of the most recently observed ReplicaSet.",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment