Skip to content

Commit

Permalink
pkg/operator: fix progressing state of cluster operator
Browse files Browse the repository at this point in the history
Currently, we always set the progressing state of the cluster operator
status.

The desired behavior is to set the cluster operator status progressing
status only if the operator is not available or if the available status
has not been set at all (unknown state).

Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1669258
  • Loading branch information
s-urbaniak committed Feb 26, 2019
1 parent ebb0183 commit af9df6e
Show file tree
Hide file tree
Showing 5 changed files with 463 additions and 111 deletions.
90 changes: 21 additions & 69 deletions pkg/client/clusteroperator.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,24 @@ func (r *StatusReporter) SetDone() error {

time := metav1.Now()

conditions := ensureConditionsInitialized(co.Status.Conditions, time)
conditions = setCondition(conditions, v1.OperatorAvailable, v1.ConditionTrue, "Successfully rolled out the stack.", time)
conditions = setCondition(conditions, v1.OperatorProgressing, v1.ConditionFalse, "", time)
conditions = setCondition(conditions, v1.OperatorFailing, v1.ConditionFalse, "", time)
co.Status.Conditions = conditions
conditions := newConditions(co.Status.Conditions, time)
conditions.setCondition(v1.OperatorAvailable, v1.ConditionTrue, "Successfully rolled out the stack.", time)
conditions.setCondition(v1.OperatorProgressing, v1.ConditionFalse, "", time)
conditions.setCondition(v1.OperatorFailing, v1.ConditionFalse, "", time)
co.Status.Conditions = conditions.entries()
//co.Status.Version = r.version

_, err = r.client.UpdateStatus(co)
return err
}

// SetInProgress sets the OperatorProgressing condition to true, either:
// 1. If there has been no previous status yet
// 2. If the previous ClusterOperator OperatorAvailable condition was false
//
// This will ensure that the progressing state will be only set initially or in case of failure.
// Once controller operator versions are available, an additional check will be introduced that toggles
// the OperatorProgressing state in case of version upgrades.
func (r *StatusReporter) SetInProgress() error {
co, err := r.client.Get(r.clusterOperatorName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
Expand All @@ -58,9 +65,9 @@ func (r *StatusReporter) SetInProgress() error {

time := metav1.Now()

conditions := ensureConditionsInitialized(co.Status.Conditions, time)
conditions = setCondition(conditions, v1.OperatorProgressing, v1.ConditionTrue, "Rolling out the stack.", time)
co.Status.Conditions = conditions
conditions := newConditions(co.Status.Conditions, time)
conditions.setCondition(v1.OperatorProgressing, v1.ConditionTrue, "Rolling out the stack.", time)
co.Status.Conditions = conditions.entries()
//co.Status.Version = r.version

_, err = r.client.UpdateStatus(co)
Expand All @@ -79,11 +86,11 @@ func (r *StatusReporter) SetFailed(statusErr error) error {

time := metav1.Now()

conditions := ensureConditionsInitialized(co.Status.Conditions, time)
conditions = setCondition(conditions, v1.OperatorAvailable, v1.ConditionFalse, "", time)
conditions = setCondition(conditions, v1.OperatorProgressing, v1.ConditionFalse, "", time)
conditions = setCondition(conditions, v1.OperatorFailing, v1.ConditionTrue, fmt.Sprintf("Failed to rollout the stack. Error: %v", statusErr), time)
co.Status.Conditions = conditions
conditions := newConditions(co.Status.Conditions, time)
conditions.setCondition(v1.OperatorAvailable, v1.ConditionFalse, "", time)
conditions.setCondition(v1.OperatorProgressing, v1.ConditionFalse, "", time)
conditions.setCondition(v1.OperatorFailing, v1.ConditionTrue, fmt.Sprintf("Failed to rollout the stack. Error: %v", statusErr), time)
co.Status.Conditions = conditions.entries()
//co.Status.Version = r.version

_, err = r.client.UpdateStatus(co)
Expand All @@ -103,62 +110,7 @@ func (r *StatusReporter) newClusterOperator() *v1.ClusterOperator {
Spec: v1.ClusterOperatorSpec{},
Status: v1.ClusterOperatorStatus{},
}
co.Status.Conditions = ensureConditionsInitialized(co.Status.Conditions, time)
co.Status.Conditions = newConditions(co.Status.Conditions, time).entries()

return co
}

func ensureConditionsInitialized(conditions []v1.ClusterOperatorStatusCondition, time metav1.Time) []v1.ClusterOperatorStatusCondition {
if len(conditions) == 0 {
return []v1.ClusterOperatorStatusCondition{
{
Type: v1.OperatorAvailable,
Status: v1.ConditionUnknown,
LastTransitionTime: time,
},
{
Type: v1.OperatorProgressing,
Status: v1.ConditionUnknown,
LastTransitionTime: time,
},
{
Type: v1.OperatorFailing,
Status: v1.ConditionUnknown,
LastTransitionTime: time,
},
}
}

return conditions
}

func setCondition(conditions []v1.ClusterOperatorStatusCondition, condition v1.ClusterStatusConditionType, status v1.ConditionStatus, message string, time metav1.Time) []v1.ClusterOperatorStatusCondition {
newConditions := []v1.ClusterOperatorStatusCondition{}
found := false
for _, c := range conditions {
if c.Type == condition {
found = true

if c.Status != status || c.Message != message {
newConditions = append(newConditions, v1.ClusterOperatorStatusCondition{
Type: condition,
Status: status,
LastTransitionTime: time,
Message: message,
})
continue
}
}
newConditions = append(newConditions, c)
}
if !found {
newConditions = append(newConditions, v1.ClusterOperatorStatusCondition{
Type: condition,
Status: status,
LastTransitionTime: time,
Message: message,
})
}

return newConditions
}
42 changes: 0 additions & 42 deletions pkg/client/clusteroperator_test.go

This file was deleted.

94 changes: 94 additions & 0 deletions pkg/client/condition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2018 The Cluster Monitoring Operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package client

import (
v1 "github.com/openshift/api/config/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type conditions struct {
entryMap map[v1.ClusterStatusConditionType]v1.ClusterOperatorStatusCondition
}

func newConditions(cs []v1.ClusterOperatorStatusCondition, time metav1.Time) *conditions {
entries := map[v1.ClusterStatusConditionType]v1.ClusterOperatorStatusCondition{
v1.OperatorAvailable: {
Type: v1.OperatorAvailable,
Status: v1.ConditionUnknown,
LastTransitionTime: time,
},
v1.OperatorProgressing: {
Type: v1.OperatorProgressing,
Status: v1.ConditionUnknown,
LastTransitionTime: time,
},
v1.OperatorFailing: {
Type: v1.OperatorFailing,
Status: v1.ConditionUnknown,
LastTransitionTime: time,
},
}

for _, c := range cs {
entries[c.Type] = c
}

return &conditions{
entryMap: entries,
}
}

func (cs *conditions) setCondition(condition v1.ClusterStatusConditionType, status v1.ConditionStatus, message string, time metav1.Time) {
entries := make(map[v1.ClusterStatusConditionType]v1.ClusterOperatorStatusCondition)
for k, v := range cs.entryMap {
entries[k] = v
}

c, ok := cs.entryMap[condition]

if !ok || c.Status != status || c.Message != message {
entries[condition] = v1.ClusterOperatorStatusCondition{
Type: condition,
Status: status,
LastTransitionTime: time,
Message: message,
}
}

// If the operator is already available, we don't set it into progressing state again.
if condition == v1.OperatorProgressing && status == v1.ConditionTrue {
available, ok := cs.entryMap[v1.OperatorAvailable]
if ok && available.Status == v1.ConditionTrue {
return
}
}

cs.entryMap = entries
}

func (cs *conditions) entries() []v1.ClusterOperatorStatusCondition {
var res []v1.ClusterOperatorStatusCondition
for _, v := range cs.entryMap {
res = append(res, v)
}
return res
}

type byType []v1.ClusterOperatorStatusCondition

func (b byType) Len() int { return len(b) }
func (b byType) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byType) Less(i, j int) bool { return b[i].Type < b[j].Type }
Loading

0 comments on commit af9df6e

Please sign in to comment.