-
Notifications
You must be signed in to change notification settings - Fork 577
OCPEDGE-2084: Add PacemakerStatus CRD for two-node fencing #2544
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
@jaypoulz: This pull request references OCPEDGE-2084 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
Hello @jaypoulz! Some important instructions when contributing to openshift/api: |
|
@jaypoulz: This pull request references OCPEDGE-2084 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
58218ce to
96e327f
Compare
2ba442d to
29b9fec
Compare
|
@jaypoulz thank you for the PR, do you mind making the CI happy? |
29b9fec to
26f7821
Compare
|
Hi @saschagrunert :) Working on it! :D |
|
A few open questions I have:
That said, it doesn't work like a normal config - there's no spec and it shouldn't be created during bootstrap. The CRD just needs to be present when the CEO runs an cronjob to post an update to it.
|
b0ff230 to
1b57b09
Compare
b9b727f to
fdd53e9
Compare
|
Yeah, I'll ignore the CI failures for now, running
I'm new to API review, but my gut feeling tells me that a dedicated
You can also try to run it in a container by
Do you mind elaborating on that? Do you mean generating the code for the unions? API docs ref: https://github.com/openshift/enhancements/blob/master/dev-guide/api-conventions.md#writing-a-union-in-go @jaypoulz is there an OpenShift enhancement available for this change? |
etcd/tnf/v1alpha1/tests/pacemakerstatuses.tnf.etcd.openshift.io/DualReplica.yaml
Outdated
Show resolved
Hide resolved
|
/retest |
3e02535 to
e6b5c99
Compare
d29f516 to
cf53006
Compare
saschagrunert
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM from an API Shadow review perspective.
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: saschagrunert The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
/retest |
|
Since @saschagrunert has said this is good from his side, I'll now take over the API review. Since it's shift week, I'm not expecting to pick this up until Monday |
|
Sounds good to me! :) |
8513003 to
8c8680a
Compare
c3cea74 to
5b40d00
Compare
b7b94f8 to
5b40d00
Compare
|
/retest-required |
1 similar comment
|
/retest-required |
Introduces etcd.openshift.io/v1alpha1 API group with a PacemakerCluster custom resource. This provides visibility into Pacemaker cluster health for Two Node Fencing (TNF) etcd deployments. The status-only resource is populated by a privileged controller and consumed by the cluster-etcd-operator healthcheck controller. This API is not explicitly gated because it's only created by CEO once the transition to an ExternalEtcd has occured. This means that it is naturally gated by the TNF topology.
5b40d00 to
1d41200
Compare
|
@jaypoulz: The following test failed, say
Full PR test history. Your PR dashboard. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
|
/retest-required |
| kind: PacemakerCluster | ||
| metadata: | ||
| name: cluster | ||
| spec: {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally we try to avoid allowing an empty spec to be valid. What would this object achieve if it has no spec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It only exists to reflect status. It's not configuration, not is it trying to modify behavior or configure the cluster in any way. This is one of the reasons I wasn't sure if this really belonged in API.
| // +k8s:openapi-gen=true | ||
| // +openshift:featuregated-schema-gen=true | ||
|
|
||
| // +kubebuilder:validation:Optional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't do this (I know this exists on other APIs but it's not right)
This changes the default behaviour for optionality of a field and has bitten many people where they thought they were making fields required and weren't
| .PHONY: verify-with-container | ||
| verify-with-container: | ||
| $(MAKE) -f ../../Makefile $@ | ||
|
|
||
| .PHONY: update-with-container | ||
| update-with-container: | ||
| $(MAKE) -f ../../Makefile $@ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this actually work? I wouldn't expect it to since we don't generally maintain the update-with-container targets, and some of them require context of the entire API
Also, we would usually have a test target at this level of makefile, can you please include that
|
|
||
| ### Feature Gate | ||
|
|
||
| - **Feature Gate**: None - this CRD is gated by cluster-etcd-operator start-up. It will only be created once a TNF cluster has transitioned to external etcd. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All APIs must start behind a feature gate, even in v1alpha1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can throw it behind the DualReplica feature gate because it's already blocked by that gate. I don't think it needs its own gate since TNF and this monitor are tightly coupled. This makes more sense now that it'll be TP in 4.21.
|
|
||
| The API follows a "Design Principle: Act on Deterministic Information" approach: | ||
| - Almost all fields are optional except `lastUpdated` | ||
| - Missing data means "unknown" not "error" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We generally prefer to populate all data with explicit unknown rather than have it omitted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean we should default to required for fields wherever possible?
| // mode indicates if the node is in active or standby mode | ||
| // NodeModeType can be one of the following values: | ||
| // - Active - the node is in active mode | ||
| // - Standby - the node is in standby mode | ||
| // When present, it must be a valid NodeModeType. | ||
| // When not present, the node mode is unknown. This likely indicates that there is an error parsing the raw XML output. | ||
| // +optional | ||
| Mode NodeModeType `json:"mode,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also better as a condition
| // - Started - the resource is started | ||
| // - Stopped - the resource is stopped | ||
| // We don't use promoted and unpromoted, so resources in those roles would omit the role field. | ||
| // When present, it must be a valid ResourceRoleType. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is a valid ResourceRoleType?
| // node is the node where the resource is running | ||
| // When present, it must be a valid string between 1 and 256 characters long. | ||
| // When not present, the resource is not assigned to a node. This typically indicates a stopped or unscheduled resource. It could also imply an error parsing the raw XML output. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| // +kubebuilder:validation:MaxLength=256 | ||
| // +optional | ||
| Node string `json:"node,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not put the resources under the node status so it's clear which node they are running on?
| // PacemakerNodeHistoryEntry represents a single operation history entry from node_history | ||
| type PacemakerNodeHistoryEntry struct { | ||
| // node is the node where the operation occurred | ||
| // It must be a valid string between 1 and 256 characters long and cannot be empty. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| // +kubebuilder:validation:MaxLength=256 | ||
| // +required | ||
| Node string `json:"node,omitempty"` | ||
|
|
||
| // resource is the resource that was operated on | ||
| // It must be a valid string between 1 and 256 characters long and cannot be empty. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| // +kubebuilder:validation:MaxLength=256 | ||
| // +required | ||
| Resource string `json:"resource,omitempty"` | ||
|
|
||
| // operation is the operation that was performed (e.g., "monitor", "start", "stop") | ||
| // Unlike other fields, this is not an enum because while "monitor", "start" and "stop" | ||
| // are the most common, resource agents can define their own operations. | ||
| // It must be a valid string between 1 and 32 characters long and cannot be empty. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| // +kubebuilder:validation:MaxLength=32 | ||
| // +required | ||
| Operation string `json:"operation,omitempty"` | ||
|
|
||
| // rc is the return code from the operation | ||
| // When present, it must be a valid integer between 0 and 2147483647 (max 32-bit int) inclusive. | ||
| // When not present, the return code is unknown. This likely indicates that there is an error parsing the raw XML output. | ||
| // +kubebuilder:validation:Minimum=0 | ||
| // +kubebuilder:validation:Maximum=2147483647 | ||
| // +optional | ||
| RC *int32 `json:"rc,omitempty"` | ||
|
|
||
| // rcText is the human-readable return code text (e.g., "ok", "error", "not running") | ||
| // When present, it must be a valid string between 1 and 32 characters long. This is a human-readable string and is not validated against any specific format. | ||
| // When not present, the return code text is unknown. This likely indicates that there is an error parsing the raw XML output. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| // +kubebuilder:validation:MaxLength=32 | ||
| // +optional | ||
| RCText string `json:"rcText,omitempty"` | ||
|
|
||
| // lastRCChange is the timestamp when the RC last changed | ||
| // It must be a valid timestamp in RFC3339 format and cannot be empty. | ||
| // +kubebuilder:validation:Format=RFC3339 | ||
| // +required | ||
| LastRCChange metav1.Time `json:"lastRCChange,omitempty"` | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like it would be better represented as an emitted corev1.Event
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That what we're using this for :)
One thing that might not be clear - why do we even need this? Can't CEO collect the status updates, produce the events, update its conditions, etc. without introducing a CRD for this?
It could for sure. But there are 2 reasons for this:
- We need some of this status to persist (e.g. node IPs for node replacement events)
- The source of pacemakercluster status updates could be external to the cluster entirely. (In a future update, we'd like to do pacemaker alert-agent based reporting). We could set up some kind of service account on the nodes to create multiple internal types - events, pacemakercluster, etc. but it felt cleaner to have pacemaker just give CEO it's relevant updates as a single status and have the operator decide if any events were noteworthy enough to have events.
| // PacemakerFencingEvent represents a single fencing event from fence history | ||
| type PacemakerFencingEvent struct { | ||
| // target is the node that was fenced | ||
| // It must be a valid string between 1 and 256 characters long and cannot be empty. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| // +kubebuilder:validation:MaxLength=256 | ||
| // +required | ||
| Target string `json:"target,omitempty"` | ||
|
|
||
| // action is the fencing action performed | ||
| // FencingActionType can be one of the following values: | ||
| // - reboot - the node was rebooted | ||
| // - off - the node was turned off | ||
| // - on - the node was turned on | ||
| // When present, it must be a valid FencingActionType. | ||
| // When not present, the fencing action is unknown. This likely indicates that there is an error parsing the raw XML output. | ||
| // +optional | ||
| Action FencingActionType `json:"action,omitempty"` | ||
|
|
||
| // status is the status of the fencing operation | ||
| // FencingStatusType can be one of the following values: | ||
| // - success - the fencing event was successful | ||
| // - failed - the fencing event failed | ||
| // - pending - the fencing event is pending | ||
| // When present, it must be a valid FencingStatusType. | ||
| // When not present, the fencing status is unknown. This likely indicates that there is an error parsing the raw XML output. | ||
| // +optional | ||
| Status FencingStatusType `json:"status,omitempty"` | ||
|
|
||
| // completed is the timestamp when the fencing event was completed | ||
| // It must be a valid timestamp in RFC3339 format and cannot be empty. | ||
| // +kubebuilder:validation:Format=RFC3339 | ||
| // +required | ||
| Completed metav1.Time `json:"completed,omitempty"` | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, why not use corev1.Event to represent these events in time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduces tnf.etcd.openshift.io/v1alpha1 API group with PacemakerStatus custom resource. This provides visibility into Pacemaker cluster health for dual-replica etcd deployments. The status-only resource is populated by a privileged controller and consumed by the cluster-etcd-operator healthcheck controller. Not gated because it's only used by CEO when two-node has transitioned.
Works in conjunction with openshift/cluster-etcd-operator#1487