Skip to content

Commit

Permalink
api: updates status definition for apps
Browse files Browse the repository at this point in the history
Previously, applications had different json tags for `updataStatus` fields.
clusterStatus for VMCluster for instance. Print fields for status pointed to the different fields.
There were unused fields at status definitions. Code was copy-pasted for status updates.

 This commit addresses this issue with the following changes:
* copy-pasted code replaced with generic implementation
* after status patch, operator updates object version. This must resolve issues with conflicts during status update requests.
* unused fields marked as deprecated an will be removed at upcoming releases
* now all app components uses the same field `updateStatus`, that holds information about current app state
* In addition new field `observedGeneration` was added, in the same way as it used at Kubernetes API. This change
 must help track CRD objects changes for 3rd party clients.

 Related issue:

#1155

Signed-off-by: f41gh7 <nik@victoriametrics.com>
  • Loading branch information
f41gh7 authored Nov 20, 2024
1 parent c2b7a5a commit b3e19a2
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 306 deletions.
56 changes: 16 additions & 40 deletions api/operator/v1beta1/vlogs_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"strings"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -35,9 +34,6 @@ import (

// VLogsSpec defines the desired state of VLogs
// +k8s:openapi-gen=true
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version",description="The version of VLogs"
// +kubebuilder:printcolumn:name="RetentionPeriod",type="string",JSONPath=".spec.RetentionPeriod",description="The desired RetentionPeriod for VLogs"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
type VLogsSpec struct {
// ParsingError contents error with context if operator was failed to parse json object from kubernetes api server
ParsingError string `json:"-" yaml:"-"`
Expand Down Expand Up @@ -97,19 +93,20 @@ type VLogsSpec struct {

// VLogsStatus defines the observed state of VLogs
type VLogsStatus struct {
// ReplicaCount Total number of non-terminated pods targeted by this VLogs.
// deprecated
Replicas int32 `json:"replicas,omitempty"`
// UpdatedReplicas Total number of non-terminated pods targeted by this VLogs.
// deprecated
UpdatedReplicas int32 `json:"updatedReplicas,omitempty"`
// AvailableReplicas Total number of available pods (ready for at least minReadySeconds) targeted by this VLogs.
// deprecated
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
// UnavailableReplicas Total number of unavailable pods targeted by this VLogs.
// deprecated
UnavailableReplicas int32 `json:"unavailableReplicas,omitempty"`
StatusMetadata `json:",inline"`
}

// UpdateStatus defines a status of vlogs instance rollout
UpdateStatus UpdateStatus `json:"status,omitempty"`
// Reason defines a reason in case of update failure
Reason string `json:"reason,omitempty"`
// GetStatusMetadata returns metadata for object status
func (cr *VLogsStatus) GetStatusMetadata() *StatusMetadata {
return &cr.StatusMetadata
}

// VLogs is fast, cost-effective and scalable logs database.
Expand All @@ -124,7 +121,7 @@ type VLogsStatus struct {
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=vlogs,scope=Namespaced
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status",description="Current status of logs instance update process"

// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// VLogs is the Schema for the vlogs API
type VLogs struct {
metav1.TypeMeta `json:",inline"`
Expand Down Expand Up @@ -323,33 +320,12 @@ func (r *VLogs) Paused() bool {

// SetStatusTo changes update status with optional reason of fail
func (r *VLogs) SetUpdateStatusTo(ctx context.Context, c client.Client, status UpdateStatus, maybeErr error) error {
currentStatus := r.Status.UpdateStatus
prevStatus := r.Status.DeepCopy()
switch status {
case UpdateStatusExpanding:
// keep failed status until success reconcile
if currentStatus == UpdateStatusFailed {
return nil
}
case UpdateStatusFailed:
if maybeErr != nil {
r.Status.Reason = maybeErr.Error()
}
case UpdateStatusOperational:
r.Status.Reason = ""
case UpdateStatusPaused:
if currentStatus == status {
return nil
}
default:
panic(fmt.Sprintf("BUG: not expected status=%q", status))
}
if equality.Semantic.DeepEqual(&r.Status, prevStatus) && currentStatus == status {
return nil
}
r.Status.UpdateStatus = status

return statusPatch(ctx, c, r.DeepCopy(), r.Status)
return updateObjectStatus(ctx, c, &patchStatusOpts[*VLogs, *VLogsStatus]{
actualStatus: status,
cr: r,
crStatus: &r.Status,
maybeErr: maybeErr,
})
}

// GetAdditionalService returns AdditionalServiceSpec settings
Expand Down
76 changes: 29 additions & 47 deletions api/operator/v1beta1/vmagent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -45,9 +44,6 @@ type VMAgentSecurityEnforcements struct {

// VMAgentSpec defines the desired state of VMAgent
// +k8s:openapi-gen=true
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version",description="The version of VMAgent"
// +kubebuilder:printcolumn:name="ReplicaCount",type="integer",JSONPath=".spec.replicas",description="The desired replicas number of VMAgent"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
type VMAgentSpec struct {
// ParsingError contents error with context if operator was failed to parse json object from kubernetes api server
ParsingError string `json:"-" yaml:"-"`
Expand Down Expand Up @@ -436,18 +432,18 @@ type VMAgentStatus struct {
Selector string `json:"selector,omitempty"`
// ReplicaCount Total number of pods targeted by this VMAgent
Replicas int32 `json:"replicas,omitempty"`
// UpdatedReplicas Total number of non-terminated pods targeted by this VMAgent
// cluster that have the desired version spec.
// Deprecated
UpdatedReplicas int32 `json:"updatedReplicas,omitempty"`
// AvailableReplicas Total number of available pods (ready for at least minReadySeconds)
// targeted by this VMAlert cluster.
// Deprecated
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
// UnavailableReplicas Total number of unavailable pods targeted by this VMAgent cluster.
// Deprecated
UnavailableReplicas int32 `json:"unavailableReplicas,omitempty"`
// UpdateStatus defines a status for update rollout, effective only for statefulMode
UpdateStatus UpdateStatus `json:"updateStatus,omitempty"`
// Reason defines fail reason for update process, effective only for statefulMode
Reason string `json:"reason,omitempty"`
StatusMetadata `json:",inline"`
}

// GetStatusMetadata returns metadata for object status
func (cr *VMAgentStatus) GetStatusMetadata() *StatusMetadata {
return &cr.StatusMetadata
}

// +genclient
Expand All @@ -468,6 +464,7 @@ type VMAgentStatus struct {
// +kubebuilder:printcolumn:name="Shards Count",type="integer",JSONPath=".status.shards",description="current number of shards"
// +kubebuilder:printcolumn:name="Replica Count",type="integer",JSONPath=".status.replicas",description="current number of replicas"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus",description="Current status of update rollout"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
type VMAgent struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand Down Expand Up @@ -711,41 +708,26 @@ func (cr *VMAgent) HasAnyStreamAggrRule() bool {

// SetStatusTo changes update status with optional reason of fail
func (cr *VMAgent) SetUpdateStatusTo(ctx context.Context, r client.Client, status UpdateStatus, maybeErr error) error {
currentStatus := cr.Status.UpdateStatus
prevStatus := cr.Status.DeepCopy()
switch status {
case UpdateStatusExpanding:
case UpdateStatusFailed:
if maybeErr != nil {
cr.Status.Reason = maybeErr.Error()
}
case UpdateStatusOperational:
cr.Status.Reason = ""
case UpdateStatusPaused:
if currentStatus == status {
return nil
}
default:
panic(fmt.Sprintf("BUG: not expected status=%q", status))
}
replicaCount := int32(0)
if cr.Spec.ReplicaCount != nil {
replicaCount = *cr.Spec.ReplicaCount
}
var shardCnt int32
if cr.Spec.ShardCount != nil {
shardCnt = int32(*cr.Spec.ShardCount)
}
cr.Status.Replicas = replicaCount
cr.Status.Shards = shardCnt
cr.Status.Selector = labels.SelectorFromSet(cr.SelectorLabels()).String()

if equality.Semantic.DeepEqual(&cr.Status, prevStatus) && currentStatus == status {
return nil
}
cr.Status.UpdateStatus = status

return statusPatch(ctx, r, cr.DeepCopy(), cr.Status)
return updateObjectStatus(ctx, r, &patchStatusOpts[*VMAgent, *VMAgentStatus]{
actualStatus: status,
cr: cr,
crStatus: &cr.Status,
maybeErr: maybeErr,
mutateCurrentBeforeCompare: func(vs *VMAgentStatus) {
replicaCount := int32(0)
if cr.Spec.ReplicaCount != nil {
replicaCount = *cr.Spec.ReplicaCount
}
var shardCnt int32
if cr.Spec.ShardCount != nil {
shardCnt = int32(*cr.Spec.ShardCount)
}
vs.Replicas = replicaCount
vs.Shards = shardCnt
vs.Selector = labels.SelectorFromSet(cr.SelectorLabels()).String()
},
})
}

// GetAdditionalService returns AdditionalServiceSpec settings
Expand Down
55 changes: 18 additions & 37 deletions api/operator/v1beta1/vmalert_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -25,9 +24,6 @@ const (

// VMAlertSpec defines the desired state of VMAlert
// +k8s:openapi-gen=true
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version",description="The version of VMAlert"
// +kubebuilder:printcolumn:name="ReplicaCount",type="integer",JSONPath=".spec.replicas",description="The desired replicas number of VmAlerts"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
type VMAlertSpec struct {
// ParsingError contents error with context if operator was failed to parse json object from kubernetes api server
ParsingError string `json:"-" yaml:"-"`
Expand Down Expand Up @@ -246,21 +242,20 @@ type VMAlertRemoteWriteSpec struct {
// VMAlertStatus defines the observed state of VMAlert
// +k8s:openapi-gen=true
type VMAlertStatus struct {
// ReplicaCount Total number of non-terminated pods targeted by this VMAlert
// cluster (their labels match the selector).
// Deprecated
Replicas int32 `json:"replicas,omitempty"`
// UpdatedReplicas Total number of non-terminated pods targeted by this VMAlert
// cluster that have the desired version spec.
// Deprecated
UpdatedReplicas int32 `json:"updatedReplicas,omitempty"`
// AvailableReplicas Total number of available pods (ready for at least minReadySeconds)
// targeted by this VMAlert cluster.
// Deprecated
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
// UnavailableReplicas Total number of unavailable pods targeted by this VMAlert cluster.
// Deprecated
UnavailableReplicas int32 `json:"unavailableReplicas,omitempty"`
// UpdateStatus defines a status for update rollout, effective only for statefulMode
UpdateStatus UpdateStatus `json:"updateStatus,omitempty"`
// Reason defines fail reason for update process, effective only for statefulMode
Reason string `json:"reason,omitempty"`
StatusMetadata `json:",inline"`
}

// GetStatusMetadata returns metadata for object status
func (cr *VMAlertStatus) GetStatusMetadata() *StatusMetadata {
return &cr.StatusMetadata
}

// VMAlert executes a list of given alerting or recording rules against configured address.
Expand All @@ -275,6 +270,8 @@ type VMAlertStatus struct {
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=vmalerts,scope=Namespaced
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus",description="Current status of update rollout"
// +kubebuilder:printcolumn:name="ReplicaCount",type="integer",JSONPath=".spec.replicaCount",description="The desired replicas number of Alertmanagers"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
type VMAlert struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand Down Expand Up @@ -487,28 +484,12 @@ func (cr *VMAlert) Paused() bool {

// SetStatusTo changes update status with optional reason of fail
func (cr *VMAlert) SetUpdateStatusTo(ctx context.Context, r client.Client, status UpdateStatus, maybeErr error) error {
currentStatus := cr.Status.UpdateStatus
prevStatus := cr.Status.DeepCopy()
switch status {
case UpdateStatusExpanding:
case UpdateStatusFailed:
if maybeErr != nil {
cr.Status.Reason = maybeErr.Error()
}
case UpdateStatusOperational:
cr.Status.Reason = ""
case UpdateStatusPaused:
if currentStatus == status {
return nil
}
default:
panic(fmt.Sprintf("BUG: not expected status=%q", status))
}
if equality.Semantic.DeepEqual(&cr.Status, prevStatus) && currentStatus == status {
return nil
}
cr.Status.UpdateStatus = status
return statusPatch(ctx, r, cr.DeepCopy(), cr.Status)
return updateObjectStatus(ctx, r, &patchStatusOpts[*VMAlert, *VMAlertStatus]{
actualStatus: status,
cr: cr,
crStatus: &cr.Status,
maybeErr: maybeErr,
})
}

// GetAdditionalService returns AdditionalServiceSpec settings
Expand Down
44 changes: 12 additions & 32 deletions api/operator/v1beta1/vmalertmanager_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -28,7 +27,6 @@ import (
// +k8s:openapi-gen=true
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.image.tag",description="The version of VMAlertmanager"
// +kubebuilder:printcolumn:name="ReplicaCount",type="integer",JSONPath=".spec.replicaCount",description="The desired replicas number of Alertmanagers"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:resource:path=vmalertmanagers,scope=Namespaced,shortName=vma,singular=vmalertmanager
Expand Down Expand Up @@ -241,10 +239,12 @@ type VMAlertmanagerList struct {
// VMAlertmanagerStatus is the most recent observed status of the VMAlertmanager cluster
// Operator API itself. More info:
type VMAlertmanagerStatus struct {
// Status defines a status of object update
UpdateStatus UpdateStatus `json:"updateStatus,omitempty"`
// Reason has non empty reason for update failure
Reason string `json:"reason,omitempty"`
StatusMetadata `json:",inline"`
}

// GetStatusMetadata returns metadata for object status
func (cr *VMAlertmanagerStatus) GetStatusMetadata() *StatusMetadata {
return &cr.StatusMetadata
}

func (cr *VMAlertmanager) AsOwner() []metav1.OwnerReference {
Expand Down Expand Up @@ -476,32 +476,12 @@ func (cr *VMAlertmanager) Paused() bool {

// SetStatusTo changes update status with optional reason of fail
func (cr *VMAlertmanager) SetUpdateStatusTo(ctx context.Context, r client.Client, status UpdateStatus, maybeErr error) error {
currentStatus := cr.Status.UpdateStatus
prevStatus := cr.Status.DeepCopy()

switch status {
case UpdateStatusExpanding:
case UpdateStatusFailed:
if maybeErr != nil {
cr.Status.Reason = maybeErr.Error()
}
case UpdateStatusOperational:
cr.Status.Reason = ""
case UpdateStatusPaused:
if currentStatus == status {
return nil
}
default:
panic(fmt.Sprintf("BUG: not expected status=%q", status))
}
if equality.Semantic.DeepEqual(&cr.Status, prevStatus) && currentStatus == status {
return nil
}
cr.Status.UpdateStatus = status
if err := statusPatch(ctx, r, cr.DeepCopy(), cr.Status); err != nil {
return fmt.Errorf("cannot patch status: %w", err)
}
return nil
return updateObjectStatus(ctx, r, &patchStatusOpts[*VMAlertmanager, *VMAlertmanagerStatus]{
actualStatus: status,
cr: cr,
crStatus: &cr.Status,
maybeErr: maybeErr,
})
}

// AlertmanagerGossipConfig defines Gossip TLS configuration for alertmanager
Expand Down
Loading

0 comments on commit b3e19a2

Please sign in to comment.