Skip to content

Commit

Permalink
Merge pull request #17 from lburgazzoli/status
Browse files Browse the repository at this point in the history
add better status reporting by checking deployments
  • Loading branch information
salaboy authored Aug 31, 2023
2 parents a481c4a + 7c753c7 commit 6ef76d0
Show file tree
Hide file tree
Showing 12 changed files with 272 additions and 89 deletions.
6 changes: 4 additions & 2 deletions api/operator/v1alpha1/dapr_control_plane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ type DaprControlPlaneStatus struct {
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`,description="The phase"
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Reconcile")].status`,description="Ready"
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Reconcile")].reason`,description="Reason"
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`,description="Ready"
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="Reason"
// +kubebuilder:printcolumn:name="Reconciled",type=string,JSONPath=`.status.conditions[?(@.type=="Reconcile")].status`,description="Ready"
// +kubebuilder:printcolumn:name="Reconciled Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Reconcile")].reason`,description="Reason"
// +kubebuilder:resource:path=daprcontrolplanes,scope=Namespaced,shortName=dcp,categories=dapr

type DaprControlPlane struct {
Expand Down
12 changes: 10 additions & 2 deletions config/crd/bases/operator.dapr.io_daprcontrolplanes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@ spec:
name: Phase
type: string
- description: Ready
jsonPath: .status.conditions[?(@.type=="Reconcile")].status
jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- description: Reason
jsonPath: .status.conditions[?(@.type=="Reconcile")].reason
jsonPath: .status.conditions[?(@.type=="Ready")].reason
name: Reason
type: string
- description: Ready
jsonPath: .status.conditions[?(@.type=="Reconcile")].status
name: Reconciled
type: string
- description: Reason
jsonPath: .status.conditions[?(@.type=="Reconcile")].reason
name: Reconciled Reason
type: string
name: v1alpha1
schema:
openAPIV3Schema:
Expand Down
1 change: 1 addition & 0 deletions internal/controller/operator/dapr_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func NewReconciler(ctx context.Context, manager ctrlRt.Manager, o HelmOptions) (
}

rec.actions = append(rec.actions, NewApplyAction())
rec.actions = append(rec.actions, NewConditionsAction())

hc, err := loader.Load(o.ChartsDir)
if err != nil {
Expand Down
14 changes: 12 additions & 2 deletions internal/controller/operator/dapr_controller_action_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ func (a *ApplyAction) Run(ctx context.Context, rc *ReconciliationRequest) error
rc.Reconciler.EnqueueRequestForOwner(&daprApi.DaprControlPlane{}, handler.OnlyControllerOwner()),
dependantWithLabels(
a.watchForUpdates(gvk),
true),
true,
a.watchStatus(gvk)),
)

if err != nil {
Expand Down Expand Up @@ -146,7 +147,8 @@ func (a *ApplyAction) Run(ctx context.Context, rc *ReconciliationRequest) error
rc.Reconciler.EnqueueRequestsFromMapFunc(labelsToRequest),
dependantWithLabels(
a.watchForUpdates(gvk),
true),
true,
a.watchStatus(gvk)),
)

if err != nil {
Expand Down Expand Up @@ -276,6 +278,14 @@ func (a *ApplyAction) watchForUpdates(gvk schema.GroupVersionKind) bool {
return true
}

func (a *ApplyAction) watchStatus(gvk schema.GroupVersionKind) bool {
if gvk.Group == "apps" && gvk.Version == "v1" && gvk.Kind == "Deployment" {
return true
}

return false
}

func (a *ApplyAction) installOnly(gvk schema.GroupVersionKind) bool {
if gvk.Group == "" && gvk.Version == "v1" && gvk.Kind == "Secret" {
return true
Expand Down
100 changes: 100 additions & 0 deletions internal/controller/operator/dapr_controller_action_conditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package operator

import (
"context"
"fmt"

"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/conditions"
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/controller/gc"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"

"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/controller/client"
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/helm"
"github.com/go-logr/logr"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
)

func NewConditionsAction() Action {
return &ConditionsAction{
engine: helm.NewEngine(),
l: ctrl.Log.WithName("action").WithName("apply"),
subscriptions: make(map[string]struct{}),
gc: gc.New(),
}
}

type ConditionsAction struct {
engine *helm.Engine
gc *gc.GC
l logr.Logger
subscriptions map[string]struct{}
}

func (a *ConditionsAction) Configure(_ context.Context, _ *client.Client, b *builder.Builder) (*builder.Builder, error) {
return b, nil
}

func (a *ConditionsAction) Run(ctx context.Context, rc *ReconciliationRequest) error {
crs, err := CurrentReleaseSelector(rc)
if err != nil {
return errors.Wrap(err, "cannot compute current release selector")
}

deployments, err := rc.Client.AppsV1().Deployments(rc.Resource.Namespace).List(ctx, metav1.ListOptions{
LabelSelector: crs.String(),
})

if err != nil {
return errors.Wrap(err, "cannot list deployments")
}

ready := 0
for i := range deployments.Items {
if conditions.ConditionStatus(deployments.Items[i], appsv1.DeploymentAvailable) == corev1.ConditionTrue {
ready++
}
}

var readyCondition metav1.Condition

if len(deployments.Items) > 0 {
if ready == len(deployments.Items) {
readyCondition = metav1.Condition{
Type: DaprConditionReady,
Status: metav1.ConditionTrue,
Reason: "Ready",
Message: fmt.Sprintf("%d/%d deployments ready", ready, len(deployments.Items)),
ObservedGeneration: rc.Resource.Generation,
}
} else {
readyCondition = metav1.Condition{
Type: DaprConditionReady,
Status: metav1.ConditionFalse,
Reason: "InProgress",
Message: fmt.Sprintf("%d/%d deployments ready", ready, len(deployments.Items)),
ObservedGeneration: rc.Resource.Generation,
}
}
} else {
readyCondition = metav1.Condition{
Type: DaprConditionReady,
Status: metav1.ConditionFalse,
Reason: "InProgress",
Message: "no deployments",
ObservedGeneration: rc.Resource.Generation,
}
}

meta.SetStatusCondition(&rc.Resource.Status.Conditions, readyCondition)

return nil
}

func (a *ConditionsAction) Cleanup(_ context.Context, _ *ReconciliationRequest) error {
return nil
}
4 changes: 2 additions & 2 deletions internal/controller/operator/dapr_controller_reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
rr.Resource.Status.Phase = DaprPhaseError

meta.SetStatusCondition(&rr.Resource.Status.Conditions, metav1.Condition{
Type: DaprConditionReconcile,
Type: DaprConditionReconciled,
Status: metav1.ConditionFalse,
Reason: DaprConditionReasonUnsupportedConfiguration,
Message: fmt.Sprintf(
Expand Down Expand Up @@ -146,7 +146,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
//

reconcileCondition := metav1.Condition{
Type: DaprConditionReconcile,
Type: DaprConditionReconciled,
Status: metav1.ConditionTrue,
Reason: "Reconciled",
Message: "Reconciled",
Expand Down
39 changes: 38 additions & 1 deletion internal/controller/operator/dapr_controller_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func labelsToRequest(_ context.Context, object ctrlCli.Object) []reconcile.Reque
}}
}

func dependantWithLabels(watchUpdate bool, watchDelete bool) predicate.Predicate {
func dependantWithLabels(watchUpdate bool, watchDelete bool, watchStatus bool) predicate.Predicate {
return predicate.And(
&predicates.HasLabel{
Name: DaprReleaseName,
Expand All @@ -85,6 +85,7 @@ func dependantWithLabels(watchUpdate bool, watchDelete bool) predicate.Predicate
&predicates.DependentPredicate{
WatchUpdate: watchUpdate,
WatchDelete: watchDelete,
WatchStatus: watchStatus,
},
)
}
Expand All @@ -106,3 +107,39 @@ func ReleaseSelector() (labels.Selector, error) {

return selector, nil
}

func CurrentReleaseSelector(rc *ReconciliationRequest) (labels.Selector, error) {
namespace, err := labels.NewRequirement(
DaprReleaseNamespace,
selection.Equals,
[]string{rc.Resource.Namespace})

if err != nil {
return nil, errors.Wrap(err, "cannot determine release namespace requirement")
}

name, err := labels.NewRequirement(
DaprReleaseName,
selection.Equals,
[]string{rc.Resource.Name})

if err != nil {
return nil, errors.Wrap(err, "cannot determine release name requirement")
}

generation, err := labels.NewRequirement(
DaprReleaseGeneration,
selection.Equals,
[]string{strconv.FormatInt(rc.Resource.Generation, 10)})

if err != nil {
return nil, errors.Wrap(err, "cannot determine generation requirement")
}

selector := labels.NewSelector().
Add(*namespace).
Add(*name).
Add(*generation)

return selector, nil
}
3 changes: 2 additions & 1 deletion internal/controller/operator/dapr_controller_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ const (
DaprControlPlaneNamespaceDefault = "dapr-system"
DaprControlPlaneNamespaceEnv = "DAPR_CONTROL_PLANE_NAMESPACE"

DaprConditionReconcile = "Reconcile"
DaprConditionReconciled = "Reconcile"
DaprConditionReady = "Ready"
DaprPhaseError = "Error"
DaprPhaseReady = "Ready"
DaprConditionReasonUnsupportedConfiguration = "UnsupportedConfiguration"
Expand Down
89 changes: 89 additions & 0 deletions pkg/conditions/conditions.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package conditions

import (
daprApi "github.com/dapr-sandbox/dapr-kubernetes-operator/api/operator/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
Expand Down Expand Up @@ -35,3 +38,89 @@ func Get(from Getter, t ConditionType) *metav1.Condition {
}
return nil
}

type GenericConditionType interface {
~string
}

func ConditionStatus[T GenericConditionType](object any, conditionType T) corev1.ConditionStatus {
switch o := object.(type) {
case Getter:
if c := Get(o, ConditionType(conditionType)); c != nil {
return corev1.ConditionStatus(c.Status)
}
case *appsv1.Deployment:
if o != nil {
for i := range o.Status.Conditions {
if string(o.Status.Conditions[i].Type) == string(conditionType) {
return o.Status.Conditions[i].Status
}
}
}
case appsv1.Deployment:
for i := range o.Status.Conditions {
if string(o.Status.Conditions[i].Type) == string(conditionType) {
return o.Status.Conditions[i].Status
}
}
case *corev1.Pod:
if o != nil {
for i := range o.Status.Conditions {
if string(o.Status.Conditions[i].Type) == string(conditionType) {
return o.Status.Conditions[i].Status
}
}
}
case *daprApi.DaprControlPlane:
if o != nil {
for i := range o.Status.Conditions {
if o.Status.Conditions[i].Type == string(conditionType) {
return corev1.ConditionStatus(o.Status.Conditions[i].Status)
}
}
}
}

return corev1.ConditionUnknown
}

func ConditionReason[T GenericConditionType](object any, conditionType T) string {
switch o := object.(type) {
case Getter:
if c := Get(o, ConditionType(conditionType)); c != nil {
return c.Reason
}
case *appsv1.Deployment:
if o != nil {
for i := range o.Status.Conditions {
if string(o.Status.Conditions[i].Type) == string(conditionType) {
return o.Status.Conditions[i].Reason
}
}
}
case appsv1.Deployment:
for i := range o.Status.Conditions {
if string(o.Status.Conditions[i].Type) == string(conditionType) {
return o.Status.Conditions[i].Reason
}
}
case *corev1.Pod:
if o != nil {
for i := range o.Status.Conditions {
if string(o.Status.Conditions[i].Type) == string(conditionType) {
return o.Status.Conditions[i].Reason
}
}
}
case *daprApi.DaprControlPlane:
if o != nil {
for i := range o.Status.Conditions {
if o.Status.Conditions[i].Type == string(conditionType) {
return o.Status.Conditions[i].Reason
}
}
}
}

return ""
}
13 changes: 8 additions & 5 deletions pkg/controller/predicates/dependant.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var _ predicate.Predicate = DependentPredicate{}
type DependentPredicate struct {
WatchDelete bool
WatchUpdate bool
WatchStatus bool

predicate.Funcs
}
Expand Down Expand Up @@ -71,11 +72,13 @@ func (p DependentPredicate) Update(e event.UpdateEvent) bool {
oldObj = oldObj.DeepCopy()
newObj = newObj.DeepCopy()

// Update filters out events that change only the dependent resource
// status. It is not typical for the controller of a primary
// resource to write to the status of one its dependent resources.
delete(oldObj.Object, "status")
delete(newObj.Object, "status")
if !p.WatchStatus {
// Update filters out events that change only the dependent resource
// status. It is not typical for the controller of a primary
// resource to write to the status of one its dependent resources.
delete(oldObj.Object, "status")
delete(newObj.Object, "status")
}

// Reset field not meaningful for comparison
oldObj.SetResourceVersion("")
Expand Down
Loading

0 comments on commit 6ef76d0

Please sign in to comment.