Skip to content

Commit

Permalink
Adds Degraded and Progressing status conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
danehans committed May 1, 2019
1 parent 5eac177 commit f05a840
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 124 deletions.
13 changes: 6 additions & 7 deletions pkg/operator/controller/controller.go
Expand Up @@ -11,6 +11,7 @@ import (
operatorclient "github.com/openshift/cluster-ingress-operator/pkg/operator/client"
"github.com/openshift/cluster-ingress-operator/pkg/util/slice"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/record"

Expand Down Expand Up @@ -195,13 +196,11 @@ func (r *reconciler) enforceEffectiveIngressDomain(ic *operatorv1.IngressControl
}
if !unique {
log.Info("domain not unique, not setting status domain for IngressController", "namespace", ic.Namespace, "name", ic.Name)
availableCondition := &operatorv1.OperatorCondition{
Type: operatorv1.IngressControllerAvailableConditionType,
Status: operatorv1.ConditionFalse,
Reason: "InvalidDomain",
Message: fmt.Sprintf("domain %q is already in use by another IngressController", domain),
var conditions []operatorv1.OperatorCondition
conditions = computeIngressStatusConditions(conditions, &appsv1.Deployment{}, false)
for _, c := range conditions {
updated.Status.Conditions = append(updated.Status.Conditions, c)
}
updated.Status.Conditions = setIngressStatusCondition(updated.Status.Conditions, availableCondition)
} else {
updated.Status.Domain = domain
}
Expand Down Expand Up @@ -402,7 +401,7 @@ func (r *reconciler) ensureIngressController(ci *operatorv1.IngressController, d
errs = append(errs, fmt.Errorf("failed to integrate metrics with openshift-monitoring for ingresscontroller %s: %v", ci.Name, err))
}

if err := r.syncIngressControllerStatus(deployment, ci); err != nil {
if err := r.syncIngressControllerStatus(deployment, ci, true); err != nil {
errs = append(errs, fmt.Errorf("failed to sync ingresscontroller status: %v", err))
}
}
Expand Down
162 changes: 122 additions & 40 deletions pkg/operator/controller/ingress_status.go
Expand Up @@ -15,7 +15,8 @@ import (

// syncIngressControllerStatus computes the current status of ic and
// updates status upon any changes since last sync.
func (r *reconciler) syncIngressControllerStatus(deployment *appsv1.Deployment, ic *operatorv1.IngressController) error {
func (r *reconciler) syncIngressControllerStatus(deployment *appsv1.Deployment, ic *operatorv1.IngressController,
uniqueDomain bool) error {
selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
if err != nil {
return fmt.Errorf("deployment has invalid spec.selector: %v", err)
Expand All @@ -24,7 +25,7 @@ func (r *reconciler) syncIngressControllerStatus(deployment *appsv1.Deployment,
updated := ic.DeepCopy()
updated.Status.AvailableReplicas = deployment.Status.AvailableReplicas
updated.Status.Selector = selector.String()
updated.Status.Conditions = computeIngressStatusConditions(updated.Status.Conditions, deployment)
updated.Status.Conditions = computeIngressStatusConditions(ic.Status.Conditions, deployment, uniqueDomain)
if !ingressStatusesEqual(updated.Status, ic.Status) {
if err := r.client.Status().Update(context.TODO(), updated); err != nil {
return fmt.Errorf("failed to update ingresscontroller status: %v", err)
Expand All @@ -35,64 +36,145 @@ func (r *reconciler) syncIngressControllerStatus(deployment *appsv1.Deployment,
}

// computeIngressStatusConditions computes the ingress controller's current state.
func computeIngressStatusConditions(conditions []operatorv1.OperatorCondition, deployment *appsv1.Deployment) []operatorv1.OperatorCondition {
func computeIngressStatusConditions(oldConditions []operatorv1.OperatorCondition, deployment *appsv1.Deployment,
uniqueDomain bool) []operatorv1.OperatorCondition {
var oldDegradedCondition, oldProgressingCondition, oldAvailableCondition *operatorv1.OperatorCondition
for i := range oldConditions {
switch oldConditions[i].Type {
case operatorv1.OperatorStatusTypeDegraded:
oldDegradedCondition = &oldConditions[i]
case operatorv1.OperatorStatusTypeProgressing:
oldProgressingCondition = &oldConditions[i]
case operatorv1.OperatorStatusTypeAvailable:
oldAvailableCondition = &oldConditions[i]
}
}

conditions := []operatorv1.OperatorCondition{
computeIngressDegradedCondition(oldDegradedCondition, deployment, uniqueDomain),
computeIngressProgressingCondition(oldProgressingCondition, deployment, uniqueDomain),
computeIngressAvailableCondition(oldAvailableCondition, deployment, uniqueDomain),
}

return conditions
}

// computeIngressDegradedCondition computes the ingress controller's Degraded
// status condition based on the status of deploy.
func computeIngressDegradedCondition(oldCondition *operatorv1.OperatorCondition, deployment *appsv1.Deployment,
uniqueDomain bool) operatorv1.OperatorCondition {
degradedCondition := &operatorv1.OperatorCondition{
Type: operatorv1.OperatorStatusTypeDegraded,
}
switch {
case !uniqueDomain:
degradedCondition.Status = operatorv1.ConditionTrue
degradedCondition.Reason = "DomainNotSet"
degradedCondition.Message = "IngressController status domain not set"
case deployment.Status.UnavailableReplicas != 0:
degradedCondition.Status = operatorv1.ConditionTrue
degradedCondition.Reason = "DeploymentUnavailable"
degradedCondition.Message = "Deployment contains an unavailable replica"
case deployment.Status.AvailableReplicas == 0:
degradedCondition.Status = operatorv1.ConditionTrue
degradedCondition.Reason = "DeploymentDegraded"
degradedCondition.Message = "Not all Deployment replicas available"
case deployment.Status.AvailableReplicas != deployment.Status.Replicas:
degradedCondition.Status = operatorv1.ConditionTrue
degradedCondition.Reason = "DeploymentDegraded"
degradedCondition.Message = "Not all Deployment replicas available"
default:
degradedCondition.Status = operatorv1.ConditionFalse
degradedCondition.Reason = "AsExpected"
degradedCondition.Message = "All Deployment replicas available"
}

return setIngressLastTransitionTime(degradedCondition, oldCondition)
}

// computeIngressProgressingCondition computes the ingress controller's Progressing
// status condition based on the status of deploy.
func computeIngressProgressingCondition(oldCondition *operatorv1.OperatorCondition, deployment *appsv1.Deployment,
uniqueDomain bool) operatorv1.OperatorCondition {
progressingCondition := &operatorv1.OperatorCondition{
Type: operatorv1.OperatorStatusTypeProgressing,
}
switch {
case !uniqueDomain:
progressingCondition.Status = operatorv1.ConditionFalse
progressingCondition.Reason = "DomainNotSet"
progressingCondition.Message = "IngressController status domain not set"
case deployment.Status.UnavailableReplicas != 0:
progressingCondition.Status = operatorv1.ConditionTrue
progressingCondition.Reason = "DeploymentUnavailable"
progressingCondition.Message = "Deployment contains an unavailable replica"
case deployment.Status.AvailableReplicas != deployment.Status.Replicas:
progressingCondition.Status = operatorv1.ConditionTrue
progressingCondition.Reason = "Reconciling"
progressingCondition.Message = fmt.Sprintf("%d Deployment replicas available, want %d",
deployment.Status.AvailableReplicas, deployment.Status.Replicas)
default:
progressingCondition.Status = operatorv1.ConditionFalse
progressingCondition.Reason = "AsExpected"
progressingCondition.Message = "All expected Deployment replicas are available"
}

return setIngressLastTransitionTime(progressingCondition, oldCondition)
}

// computeIngressAvailableCondition computes the ingress controller's Available
// status condition based on the status of deploy.
func computeIngressAvailableCondition(oldCondition *operatorv1.OperatorCondition, deploy *appsv1.Deployment,
uniqueDomain bool) operatorv1.OperatorCondition {
availableCondition := &operatorv1.OperatorCondition{
Type: operatorv1.IngressControllerAvailableConditionType,
Status: operatorv1.ConditionUnknown,
Type: operatorv1.OperatorStatusTypeAvailable,
}
if deployment.Status.AvailableReplicas > 0 {
availableCondition.Status = operatorv1.ConditionTrue
} else {
switch {
case !uniqueDomain:
availableCondition.Status = operatorv1.ConditionFalse
availableCondition.Reason = "DomainNotSet"
availableCondition.Message = "IngressController status domain not set"
case deploy.Status.AvailableReplicas == 0:
availableCondition.Status = operatorv1.ConditionFalse
availableCondition.Reason = "DeploymentUnavailable"
availableCondition.Message = "no Deployment replicas available"
availableCondition.Message = "No Deployment replicas available"
default:
availableCondition.Status = operatorv1.ConditionTrue
availableCondition.Reason = "AsExpected"
availableCondition.Message = "Minimum number of Deployment replicas available"
}
conditions = setIngressStatusCondition(conditions, availableCondition)

return conditions
return setIngressLastTransitionTime(availableCondition, oldCondition)
}

// setIngressStatusCondition returns the IngressController condition result
// of setting the specified condition in the given slice of conditions.
func setIngressStatusCondition(oldConditions []operatorv1.OperatorCondition, condition *operatorv1.OperatorCondition) []operatorv1.OperatorCondition {
condition.LastTransitionTime = metav1.Now()

var newConditions []operatorv1.OperatorCondition

found := false
for _, c := range oldConditions {
if condition.Type == c.Type {
if condition.Status == c.Status &&
condition.Reason == c.Reason &&
condition.Message == c.Message {
// The ingresscontroller status condition has not changed.
return oldConditions
}

found = true
newConditions = append(newConditions, *condition)
} else {
newConditions = append(newConditions, c)
}
}
if !found {
newConditions = append(newConditions, *condition)
// setIngressLastTransitionTime sets LastTransitionTime for the given condition.
// If the condition has changed, it will assign a new timestamp otherwise keeps the old timestamp.
func setIngressLastTransitionTime(condition, oldCondition *operatorv1.OperatorCondition) operatorv1.OperatorCondition {
if oldCondition != nil && condition.Status == oldCondition.Status &&
condition.Reason == oldCondition.Reason && condition.Message == oldCondition.Message {
condition.LastTransitionTime = oldCondition.LastTransitionTime
} else {
condition.LastTransitionTime = metav1.Now()
}

return newConditions
return *condition
}

// ingressStatusesEqual compares two IngressControllerStatus values. Returns true
// if the provided values should be considered equal for the purpose of determining
// whether an update is necessary, false otherwise.
func ingressStatusesEqual(a, b operatorv1.IngressControllerStatus) bool {
conditionCmpOpts := []cmp.Option{
cmpopts.IgnoreFields(operatorv1.OperatorCondition{}, "LastTransitionTime"),
cmpopts.EquateEmpty(),
cmpopts.SortSlices(func(a, b operatorv1.OperatorCondition) bool { return a.Type < b.Type }),
}
if !cmp.Equal(a.Conditions, b.Conditions, conditionCmpOpts...) || a.AvailableReplicas != b.AvailableReplicas ||
a.Selector != b.Selector {
if !cmp.Equal(a.Conditions, b.Conditions, conditionCmpOpts...) {
return false
}
if a.AvailableReplicas != b.AvailableReplicas {
return false
}
if a.Selector != b.Selector {
return false
}

Expand Down

0 comments on commit f05a840

Please sign in to comment.