Skip to content

Commit

Permalink
operator: Add warning for old schema configuration (grafana#11158)
Browse files Browse the repository at this point in the history
Co-authored-by: Robert Jacob <[email protected]>
Co-authored-by: Periklis Tsirakidis <[email protected]>
  • Loading branch information
3 people authored and rhnasc committed Apr 12, 2024
1 parent e9a97b6 commit 70ff95d
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 237 deletions.
1 change: 1 addition & 0 deletions operator/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Main

- [11158](https://github.com/grafana/loki/pull/11158) **btaani**: operator: Add warning for old schema configuration
- [11473](https://github.com/grafana/loki/pull/11473) **JoaoBraveCoding**: Adds structured metadata dashboards
- [11448](https://github.com/grafana/loki/pull/11448) **periklis**: Update Loki operand to v2.9.3
- [11357](https://github.com/grafana/loki/pull/11357) **periklis**: Fix storing authentication credentials in the Loki ConfigMap
Expand Down
6 changes: 6 additions & 0 deletions operator/apis/loki/v1/lokistack_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,10 @@ const (
// ConditionDegraded defines the condition that some or all components in the Loki deployment
// are degraded or the cluster cannot connect to object storage.
ConditionDegraded LokiStackConditionType = "Degraded"

// ConditionWarning is used for configurations that are not recommended, but don't currently cause
// issues. There can be multiple warning conditions active at a time.
ConditionWarning LokiStackConditionType = "Warning"
)

// LokiStackConditionReason defines the type for valid reasons of a Loki deployment conditions.
Expand Down Expand Up @@ -1097,6 +1101,8 @@ const (
ReasonZoneAwareNodesMissing LokiStackConditionReason = "ReasonZoneAwareNodesMissing"
// ReasonZoneAwareEmptyLabel when the node-label used for zone-awareness has an empty value.
ReasonZoneAwareEmptyLabel LokiStackConditionReason = "ReasonZoneAwareEmptyLabel"
// ReasonStorageNeedsSchemaUpdate when the object storage schema version is older than V13
ReasonStorageNeedsSchemaUpdate LokiStackConditionReason = "StorageNeedsSchemaUpdate"
)

// PodStatusMap defines the type for mapping pod status to pod name.
Expand Down
42 changes: 21 additions & 21 deletions operator/controllers/loki/lokistack_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"time"

"github.com/ViaQ/logerr/v2/kverrors"
"github.com/go-logr/logr"
"github.com/google/go-cmp/cmp"
openshiftconfigv1 "github.com/openshift/api/config/v1"
Expand Down Expand Up @@ -150,40 +149,41 @@ func (r *LokiStackReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, nil
}

if r.FeatureGates.BuiltInCertManagement.Enabled {
err = handlers.CreateOrRotateCertificates(ctx, r.Log, req, r.Client, r.Scheme, r.FeatureGates)
if err != nil {
return handleDegradedError(ctx, r.Client, req, err)
}
var degraded *status.DegradedError
err = r.updateResources(ctx, req)
switch {
case errors.As(err, &degraded):
// degraded errors are handled by status.Refresh below
case err != nil:
return ctrl.Result{}, err
}

err = handlers.CreateOrUpdateLokiStack(ctx, r.Log, req, r.Client, r.Scheme, r.FeatureGates)
err = status.Refresh(ctx, r.Client, req, time.Now(), degraded)
if err != nil {
return handleDegradedError(ctx, r.Client, req, err)
return ctrl.Result{}, err
}

err = status.Refresh(ctx, r.Client, req, time.Now())
if err != nil {
return ctrl.Result{}, err
if degraded != nil {
return ctrl.Result{
Requeue: degraded.Requeue,
}, nil
}

return ctrl.Result{}, nil
}

func handleDegradedError(ctx context.Context, c client.Client, req ctrl.Request, err error) (ctrl.Result, error) {
var degraded *status.DegradedError
if errors.As(err, &degraded) {
err = status.SetDegradedCondition(ctx, c, req, degraded.Message, degraded.Reason)
if err != nil {
return ctrl.Result{}, kverrors.Wrap(err, "error setting degraded condition")
func (r *LokiStackReconciler) updateResources(ctx context.Context, req ctrl.Request) error {
if r.FeatureGates.BuiltInCertManagement.Enabled {
if err := handlers.CreateOrRotateCertificates(ctx, r.Log, req, r.Client, r.Scheme, r.FeatureGates); err != nil {
return err
}
}

return ctrl.Result{
Requeue: degraded.Requeue,
}, nil
if err := handlers.CreateOrUpdateLokiStack(ctx, r.Log, req, r.Client, r.Scheme, r.FeatureGates); err != nil {
return err
}

return ctrl.Result{}, err
return nil
}

// SetupWithManager sets up the controller with the Manager.
Expand Down
7 changes: 7 additions & 0 deletions operator/docs/operator/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,9 @@ for the ruler is missing.</p>
</tr><tr><td><p>&#34;ReadyComponents&#34;</p></td>
<td><p>ReasonReadyComponents when all LokiStack components are ready to serve traffic.</p>
</td>
</tr><tr><td><p>&#34;StorageNeedsSchemaUpdate&#34;</p></td>
<td><p>ReasonStorageNeedsSchemaUpdate when the object storage schema version is older than V13</p>
</td>
</tr><tr><td><p>&#34;ReasonZoneAwareEmptyLabel&#34;</p></td>
<td><p>ReasonZoneAwareEmptyLabel when the node-label used for zone-awareness has an empty value.</p>
</td>
Expand Down Expand Up @@ -1814,6 +1817,10 @@ are degraded or the cluster cannot connect to object storage.</p>
</tr><tr><td><p>&#34;Ready&#34;</p></td>
<td><p>ConditionReady defines the condition that all components in the Loki deployment are ready.</p>
</td>
</tr><tr><td><p>&#34;Warning&#34;</p></td>
<td><p>ConditionWarning is used for configurations that are not recommended, but don&rsquo;t currently cause
issues. There can be multiple warning conditions active at a time.</p>
</td>
</tr></tbody>
</table>

Expand Down
37 changes: 37 additions & 0 deletions operator/internal/status/conditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package status

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

func mergeConditions(old, active []metav1.Condition, now metav1.Time) []metav1.Condition {
merged := make([]metav1.Condition, 0, len(old)+len(active))
for len(old) > 0 {
c := old[0]
found := -1
for i, ac := range active {
if c.Type == ac.Type && c.Reason == ac.Reason {
found = i
break
}
}

if found != -1 {
c = active[found]
active = append(active[:found], active[found+1:]...)

c.Status = metav1.ConditionTrue
} else {
c.Status = metav1.ConditionFalse
}

c.LastTransitionTime = now
merged = append(merged, c)
old = old[1:]
}

for _, c := range active {
c.Status = metav1.ConditionTrue
c.LastTransitionTime = now
merged = append(merged, c)
}
return merged
}
139 changes: 139 additions & 0 deletions operator/internal/status/conditions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package status

import (
"github.com/google/go-cmp/cmp"
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"testing"
"time"
)

func TestMergeConditions(t *testing.T) {
now := metav1.NewTime(time.Unix(0, 0))
tt := []struct {
desc string
old []metav1.Condition
active []metav1.Condition
wantMerged []metav1.Condition
}{
{
desc: "set status and time",
old: []metav1.Condition{},
active: []metav1.Condition{
conditionReady,
},
wantMerged: []metav1.Condition{
{
Type: conditionReady.Type,
Status: metav1.ConditionTrue,
LastTransitionTime: now,
Reason: conditionReady.Reason,
Message: conditionReady.Message,
},
},
},
{
desc: "reset old condition",
old: []metav1.Condition{
conditionPending,
},
active: []metav1.Condition{
conditionReady,
},
wantMerged: []metav1.Condition{
{
Type: conditionPending.Type,
Status: metav1.ConditionFalse,
LastTransitionTime: now,
Reason: conditionPending.Reason,
Message: conditionPending.Message,
},
{
Type: conditionReady.Type,
Status: metav1.ConditionTrue,
LastTransitionTime: now,
Reason: conditionReady.Reason,
Message: conditionReady.Message,
},
},
},
{
desc: "keep active conditions",
old: []metav1.Condition{
{
Type: conditionReady.Type,
Status: metav1.ConditionTrue,
LastTransitionTime: now,
Reason: conditionReady.Reason,
Message: conditionReady.Message,
},
{
Type: conditionPending.Type,
Status: metav1.ConditionFalse,
LastTransitionTime: now,
Reason: conditionPending.Reason,
Message: conditionPending.Message,
},
},
active: []metav1.Condition{
conditionReady,
{
Type: string(lokiv1.ConditionWarning),
Reason: "test-warning",
Message: "test-warning-message",
},
},
wantMerged: []metav1.Condition{
{
Type: conditionReady.Type,
Status: metav1.ConditionTrue,
LastTransitionTime: now,
Reason: conditionReady.Reason,
Message: conditionReady.Message,
},
{
Type: conditionPending.Type,
Status: metav1.ConditionFalse,
LastTransitionTime: now,
Reason: conditionPending.Reason,
Message: conditionPending.Message,
},
{
Type: string(lokiv1.ConditionWarning),
Status: metav1.ConditionTrue,
LastTransitionTime: now,
Reason: "test-warning",
Message: "test-warning-message",
},
},
},
}

for _, tc := range tt {
tc := tc

t.Run(tc.desc, func(t *testing.T) {
t.Parallel()

beforeLenOld := len(tc.old)
beforeLenActive := len(tc.active)

merged := mergeConditions(tc.old, tc.active, now)

afterLenOld := len(tc.old)
afterLenActive := len(tc.active)

if diff := cmp.Diff(merged, tc.wantMerged); diff != "" {
t.Errorf("Merged conditions differ: -got+want\n%s", diff)
}

if beforeLenOld != afterLenOld {
t.Errorf("old length differs: got %v, want %v", afterLenOld, beforeLenOld)
}

if beforeLenActive != afterLenActive {
t.Errorf("active length differs: got %v, want %v", afterLenActive, beforeLenActive)
}
})
}
}
Loading

0 comments on commit 70ff95d

Please sign in to comment.