Skip to content

Commit

Permalink
api: rework Status sub-resource
Browse files Browse the repository at this point in the history
Previously, each child object such as `VMUser`, `VMRule` etc had own status with unique fields. It was hard to manage and required manually copy-pasted code.
More over previous doesn't follow Kubernetes API convention:
 https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

 Which has shared `conditions` status field.

 This commit adds `conditions` and extends it with optional lastUpdateTime field. It simplifies resource management, provides better feedback for CRD users.
It also, allows to track resource life-cycle with operator.

 In addition, this commit adds more e2e tests for child objects.

Related issue:
#1155

Signed-off-by: f41gh7 <[email protected]>
  • Loading branch information
f41gh7 committed Dec 17, 2024
1 parent 95606b3 commit 6186134
Show file tree
Hide file tree
Showing 30 changed files with 2,516 additions and 399 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ test: manifests generate fmt vet envtest ## Run tests.
.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up.
test-e2e: load-kind ginkgo-install
$(GINKGO_BIN) -procs=$(E2E_TESTS_CONCURRENCY) -timeout=20m ./test/e2e/
$(GINKGO_BIN) -procs=$(E2E_TESTS_CONCURRENCY) -timeout=20m ./test/e2e/childobjects/
$(GINKGO_BIN) -procs=$(E2E_TESTS_CONCURRENCY) -timeout=20m ./test/e2e/watchnamespace/
$(GINKGO_BIN) -procs=$(E2E_TESTS_CONCURRENCY) -timeout=20m ./test/e2e/deploy/

Expand Down
21 changes: 10 additions & 11 deletions api/operator/v1beta1/vmalertmanagerconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,26 +93,25 @@ type TimeRange struct {
EndTime string `json:"end_time" yaml:"end_time"`
}

// GetStatusMetadata implements reconcile.objectWithStatus interface
func (amc *VMAlertmanagerConfig) GetStatusMetadata() *StatusMetadata {
return &amc.Status.StatusMetadata
}

// VMAlertmanagerConfigStatus defines the observed state of VMAlertmanagerConfig
type VMAlertmanagerConfigStatus struct {
// Status defines CRD processing status
Status UpdateStatus `json:"status,omitempty"`
// LastSyncError contains error message for unsuccessful config generation
LastSyncError string `json:"lastSyncError,omitempty"`
// ObservedGeneration defines current generation picked by operator for the
// reconcile
StatusMetadata `json:",inline"`
LastErrorParentAlertmanagerName string `json:"lastErrorParentAlertmanagerName,omitempty"`
// LastSyncErrorTimestamp defines time when error occured
LastSyncErrorTimestamp int64 `json:"lastSyncErrorTimestamp,omitempty"`
// CurrentSyncError holds an error occured during reconcile loop
CurrentSyncError string `json:"-"`
}

// VMAlertmanagerConfig is the Schema for the vmalertmanagerconfigs API
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="VMAlertmanager Error",type="string",JSONPath=".status.lastErrorParentAlertmanagerName"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
// +k8s:openapi-gen=true
type VMAlertmanagerConfig struct {
Expand Down
77 changes: 67 additions & 10 deletions api/operator/v1beta1/vmextra_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ const (
RelabelingConfigDir = "/etc/vm/relabeling"
)

const (
// ConditionParsingReason defines reason for child objects
ConditionParsingReason = "ConfigParsedAndApplied"
// ConditionDomainTypeAppliedSuffix defines type suffix for ConditionParsingReason reason
ConditionDomainTypeAppliedSuffix = ".victoriametrics.com/Applied"
)

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: "operator.victoriametrics.com", Version: "v1beta1"}

Expand Down Expand Up @@ -1039,15 +1046,7 @@ type TLSClientConfig struct {

// ScrapeObjectStatus defines the observed state of ScrapeObjects
type ScrapeObjectStatus struct {
// ObservedGeneration defines current generation picked by operator for the
// reconcile
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// Status defines update status of resource
Status UpdateStatus `json:"status,omitempty"`
// LastSyncError contains error message for unsuccessful config generation
LastSyncError string `json:"lastSyncError,omitempty"`
// CurrentSyncError holds an error occured during reconcile loop
CurrentSyncError string `json:"-"`
StatusMetadata `json:",inline"`
}

type objectWithLastAppliedState[T, ST any] interface {
Expand Down Expand Up @@ -1410,12 +1409,22 @@ type ExternalConfig struct {
// +k8s:openapi-gen=true
type StatusMetadata struct {
// UpdateStatus defines a status for update rollout
//
UpdateStatus UpdateStatus `json:"updateStatus,omitempty"`
// Reason defines fail reason for reconcile process
// Reason defines human readadble error reason
//
Reason string `json:"reason,omitempty"`
// ObservedGeneration defines current generation picked by operator for the
// reconcile
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// CurrentSyncError holds an error occured during reconcile loop
CurrentSyncError string `json:"-"`
// Known .status.conditions.type are: "Available", "Progressing", and "Degraded"
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []Condition `json:"conditions,omitempty"`
}

// ManagedObjectsMetadata contains Labels and Annotations
Expand All @@ -1441,3 +1450,51 @@ type Image struct {
// PullPolicy describes how to pull docker image
PullPolicy v1.PullPolicy `json:"pullPolicy,omitempty"`
}

// Condition defines status condition of the resource
type Condition struct {
// Type of condition in CamelCase or in name.namespace.resource.victoriametrics.com/CamelCase.
// +required
// +kubebuilder:validation:MaxLength=316
Type string `json:"type"`
// status of the condition, one of True, False, Unknown.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=True;False;Unknown
Status metav1.ConditionStatus `json:"status"`
// observedGeneration represents the .metadata.generation that the condition was set based upon.
// For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
// with respect to the current state of the instance.
// +optional
// +kubebuilder:validation:Minimum=0
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
// lastTransitionTime is the last time the condition transitioned from one status to another.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Format=date-time
LastTransitionTime metav1.Time `json:"lastTransitionTime"`
// LastUpdateTime is the last time of given type update.
// This value is used for status TTL update and removal
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Format=date-time
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`

// reason contains a programmatic identifier indicating the reason for the condition's last transition.
// Producers of specific condition types may define expected values and meanings for this field,
// and whether the values are considered a guaranteed API.
// The value should be a CamelCase string.
// This field may not be empty.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=1024
// +kubebuilder:validation:MinLength=1
Reason string `json:"reason"`
// message is a human readable message indicating details about the transition.
// This may be an empty string.
// +optional
// +kubebuilder:validation:MaxLength=32768
Message string `json:"message,omitempty"`
}
10 changes: 5 additions & 5 deletions api/operator/v1beta1/vmnodescrape_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ type VMNodeScrapeSpec struct {
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
type VMNodeScrape struct {
metav1.TypeMeta `json:",inline"`
Expand Down Expand Up @@ -66,9 +66,9 @@ func (cr VMNodeScrape) AsMapKey() string {
return fmt.Sprintf("nodeScrape/%s/%s", cr.Namespace, cr.Name)
}

// GetStatus returns scrape object status
func (cr *VMNodeScrape) GetStatus() *ScrapeObjectStatus {
return &cr.Status
// GetStatusMetadata implements reconcile.objectWithStatus interface
func (cr *VMNodeScrape) GetStatusMetadata() *StatusMetadata {
return &cr.Status.StatusMetadata
}

func init() {
Expand Down
10 changes: 5 additions & 5 deletions api/operator/v1beta1/vmpodscrape_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ type VMPodScrapeSpec struct {
// +kubebuilder:resource:path=vmpodscrapes,scope=Namespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
type VMPodScrape struct {
metav1.TypeMeta `json:",inline"`
Expand Down Expand Up @@ -113,9 +113,9 @@ func (cr *VMPodScrape) AsMapKey(i int) string {
return fmt.Sprintf("podScrape/%s/%s/%d", cr.Namespace, cr.Name, i)
}

// GetStatus returns scrape object status
func (cr *VMPodScrape) GetStatus() *ScrapeObjectStatus {
return &cr.Status
// GetStatusMetadata implements reconcile.objectWithStatus interface
func (cr *VMPodScrape) GetStatusMetadata() *StatusMetadata {
return &cr.Status.StatusMetadata
}

func init() {
Expand Down
10 changes: 5 additions & 5 deletions api/operator/v1beta1/vmprobe_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ type VMProberSpec struct {
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
// +k8s:openapi-gen=true
type VMProbe struct {
Expand Down Expand Up @@ -127,9 +127,9 @@ func (cr VMProbe) AsMapKey() string {
return fmt.Sprintf("probeScrape/%s/%s", cr.Namespace, cr.Name)
}

// GetStatus returns scrape object status
func (cr *VMProbe) GetStatus() *ScrapeObjectStatus {
return &cr.Status
// GetStatusMetadata implements reconcile.objectWithStatus interface
func (cr *VMProbe) GetStatusMetadata() *StatusMetadata {
return &cr.Status.StatusMetadata
}

func init() {
Expand Down
16 changes: 8 additions & 8 deletions api/operator/v1beta1/vmrule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,12 @@ type Rule struct {

// VMRuleStatus defines the observed state of VMRule
type VMRuleStatus struct {
// Status defines CRD processing status
Status UpdateStatus `json:"status,omitempty"`
// LastSyncError contains error message for unsuccessful config generation
LastSyncError string `json:"lastSyncError,omitempty"`
// CurrentSyncError holds an error occured during reconcile loop
CurrentSyncError string `json:"-"`
StatusMetadata `json:",inline"`
}

// GetStatusMetadata implements reconcile.objectWithStatus interface
func (cr *VMRule) GetStatusMetadata() *StatusMetadata {
return &cr.Status.StatusMetadata
}

// VMRule defines rule records for vmalert application
Expand All @@ -140,8 +140,8 @@ type VMRuleStatus struct {
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=vmrules,scope=Namespaced
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type VMRule struct {
Expand Down
10 changes: 5 additions & 5 deletions api/operator/v1beta1/vmscrapeconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import (
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=vmscrapeconfigs,scope=Namespaced
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
type VMScrapeConfig struct {
metav1.TypeMeta `json:",inline"`
Expand Down Expand Up @@ -509,9 +509,9 @@ func (cr VMScrapeConfig) AsMapKey(prefix string, i int) string {
return fmt.Sprintf("scrapeConfig/%s/%s/%s/%d", cr.Namespace, cr.Name, prefix, i)
}

// GetStatus returns scrape object status
func (cr *VMScrapeConfig) GetStatus() *ScrapeObjectStatus {
return &cr.Status
// GetStatusMetadata implements reconcile.objectWithStatus interface
func (cr *VMScrapeConfig) GetStatusMetadata() *StatusMetadata {
return &cr.Status.StatusMetadata
}

func init() {
Expand Down
10 changes: 5 additions & 5 deletions api/operator/v1beta1/vmservicescrape_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ type VMServiceScrapeSpec struct {
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=vmservicescrapes,scope=Namespaced
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
type VMServiceScrape struct {
metav1.TypeMeta `json:",inline"`
Expand Down Expand Up @@ -140,9 +140,9 @@ func (cr VMServiceScrape) AsMapKey(i int) string {
return fmt.Sprintf("serviceScrape/%s/%s/%d", cr.Namespace, cr.Name, i)
}

// GetStatus returns scrape object status
func (cr *VMServiceScrape) GetStatus() *ScrapeObjectStatus {
return &cr.Status
// GetStatusMetadata implements reconcile.objectWithStatus interface
func (cr *VMServiceScrape) GetStatusMetadata() *StatusMetadata {
return &cr.Status.StatusMetadata
}

func init() {
Expand Down
10 changes: 5 additions & 5 deletions api/operator/v1beta1/vmstaticscrape_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ type TargetEndpoint struct {
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
type VMStaticScrape struct {
metav1.TypeMeta `json:",inline"`
Expand Down Expand Up @@ -67,9 +67,9 @@ func (cr VMStaticScrape) AsMapKey(i int) string {
return fmt.Sprintf("staticScrape/%s/%s/%d", cr.Namespace, cr.Name, i)
}

// GetStatus returns scrape object status
func (cr *VMStaticScrape) GetStatus() *ScrapeObjectStatus {
return &cr.Status
// GetStatusMetadata implements reconcile.objectWithStatus interface
func (cr *VMStaticScrape) GetStatusMetadata() *StatusMetadata {
return &cr.Status.StatusMetadata
}

func init() {
Expand Down
17 changes: 8 additions & 9 deletions api/operator/v1beta1/vmuser_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,15 @@ type TargetRefBasicAuth struct {

// VMUserStatus defines the observed state of VMUser
type VMUserStatus struct {
// Status defines update status of resource
Status UpdateStatus `json:"status,omitempty"`
// LastSyncError contains error message for unsuccessful config generation
// for given user
LastSyncError string `json:"lastSyncError,omitempty"`
// CurrentSyncError holds an error occured during reconcile loop
CurrentSyncError string `json:"-"`
StatusMetadata `json:",inline"`
}

// VMUser is the Schema for the vmusers API
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.lastSyncError"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.updateStatus"
// +kubebuilder:printcolumn:name="Sync Error",type="string",JSONPath=".status.reason"
// +genclient
type VMUser struct {
metav1.TypeMeta `json:",inline"`
Expand Down Expand Up @@ -227,6 +221,11 @@ func (cr VMUser) AllLabels() map[string]string {
return labels
}

// GetStatusMetadata implements reconcile.objectWithStatus interface
func (cr *VMUser) GetStatusMetadata() *StatusMetadata {
return &cr.Status.StatusMetadata
}

func init() {
SchemeBuilder.Register(&VMUser{}, &VMUserList{})
}
Loading

0 comments on commit 6186134

Please sign in to comment.