diff --git a/etcd/README.md b/etcd/README.md new file mode 100644 index 00000000000..6b28737201a --- /dev/null +++ b/etcd/README.md @@ -0,0 +1,197 @@ +# etcd.openshift.io API Group + +This API group contains CRDs related to etcd cluster management in Two Node OpenShift with Fencing deployments. + +## API Versions + +### v1alpha1 + +Contains the `PacemakerCluster` custom resource for monitoring Pacemaker cluster health in Two Node OpenShift with Fencing deployments. + +#### PacemakerCluster + +- **Feature Gate**: `DualReplica` +- **Component**: `two-node-fencing` +- **Scope**: Cluster-scoped singleton resource (must be named "cluster") +- **Resource Path**: `pacemakerclusters.etcd.openshift.io` + +The `PacemakerCluster` resource provides visibility into the health and status of a Pacemaker-managed cluster. +It is periodically updated by the cluster-etcd-operator's status collector. + +### Status Subresource Design + +This resource uses the standard Kubernetes status subresource pattern (`+kubebuilder:subresource:status`). +The status collector creates the resource without status, then immediately populates it via the `/status` endpoint. + +**Why not atomic create-with-status?** + +We initially explored removing the status subresource to allow creating the resource with status in a single +atomic operation. This would ensure the resource is never observed in an incomplete state. However: + +1. The Kubernetes API server strips the `status` field from create requests when a status subresource is enabled +2. Without the subresource, we cannot use separate RBAC for spec vs status updates +3. The OpenShift API test framework assumes status subresource exists for status update tests + +The status collector performs a two-step operation: create resource, then immediately update status. +The brief window where status is empty is acceptable since the healthcheck controller handles missing status gracefully. + +### Pacemaker Resources + +A **pacemaker resource** is a unit of work managed by pacemaker. In pacemaker terminology, resources are services +or applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. + +For Two Node OpenShift with Fencing, we manage three resources: +- **Kubelet**: The Kubernetes node agent and a prerequisite for etcd +- **Etcd**: The distributed key-value store +- **FencingAgent**: Used to isolate failed nodes during a quorum loss event + +### Status Structure + +```yaml +status: # Optional on creation, populated via status subresource + conditions: # Cluster-level conditions (optional, but min 3 items when present) + - type: Healthy + - type: InService + - type: NodeCountAsExpected + lastUpdated: # When status was last updated (optional, cannot decrease once set) + nodes: # Per-node status (optional, 0-32 nodes, expects 2) + - name: # RFC 1123 subdomain name + addresses: # List of node addresses using corev1.NodeAddress + - type: InternalIP # Address type (InternalIP, ExternalIP, Hostname, etc.) + address: # First InternalIP address used for etcd peer URLs + conditions: # Node-level conditions (optional, but min 9 items when present) + - type: Healthy + - type: Online + - type: InService + - type: Active + - type: Ready + - type: Clean + - type: Member + - type: FencingAvailable + - type: FencingHealthy + resources: # Array of pacemaker resources scheduled on this node (optional, min 2) + - name: Kubelet # Both resources (Kubelet, Etcd) must be present + conditions: # Resource-level conditions (optional, but min 8 items when present) + - type: Healthy + - type: InService + - type: Managed + - type: Enabled + - type: Operational + - type: Active + - type: Started + - type: Schedulable + - name: Etcd + conditions: [] + fencingAgents: # Fencing agents that can fence THIS node (optional, 1-8 per node) + - name: _ # e.g., "master-0_redfish" + method: # Fencing method: redfish, ipmi, fence_aws, etc. + conditions: [] # Same 8 conditions as resources +``` + +### Fencing Agents + +Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. +Unlike regular pacemaker resources (Kubelet, Etcd), fencing agents are tracked separately because: + +1. **Mapping by target, not schedule**: Resources are mapped to the node where they are scheduled to run. + Fencing agents are mapped to the node they can *fence* (their target), regardless of which node + their monitoring operations are scheduled on. + +2. **Multiple agents per node**: A node can have multiple fencing agents for redundancy + (e.g., both Redfish and IPMI). Expected: 1 per node, supported: up to 8. + +3. **Health tracking via two node-level conditions**: + - **FencingAvailable**: True if at least one agent is healthy (fencing works), False if all agents unhealthy (degrades operator) + - **FencingHealthy**: True if all agents are healthy (ideal state), False if any agent is unhealthy (emits warning events) + +### Cluster-Level Conditions + +**Per API conventions, conditions are optional but when present must include all three types (enforced via MinItems=3 and XValidation rules).** + +| Condition | True | False | +|-----------|------|-------| +| `Healthy` | Cluster is healthy (`ClusterHealthy`) | Cluster has issues (`ClusterUnhealthy`) | +| `InService` | In service (`InService`) | In maintenance (`InMaintenance`) | +| `NodeCountAsExpected` | Node count is as expected (`AsExpected`) | Wrong count (`InsufficientNodes`, `ExcessiveNodes`) | + +### Node-Level Conditions + +**Per API conventions, conditions are optional but when present must include all nine types (enforced via MinItems=9 and XValidation rules).** + +| Condition | True | False | +|-----------|------|-------| +| `Healthy` | Node is healthy (`NodeHealthy`) | Node has issues (`NodeUnhealthy`) | +| `Online` | Node is online (`Online`) | Node is offline (`Offline`) | +| `InService` | In service (`InService`) | In maintenance (`InMaintenance`) | +| `Active` | Node is active (`Active`) | Node is in standby (`Standby`) | +| `Ready` | Node is ready (`Ready`) | Node is pending (`Pending`) | +| `Clean` | Node is clean (`Clean`) | Node is unclean (`Unclean`) | +| `Member` | Node is a member (`Member`) | Not a member (`NotMember`) | +| `FencingAvailable` | At least one agent healthy (`FencingAvailable`) | All agents unhealthy (`FencingUnavailable`) - degrades operator | +| `FencingHealthy` | All agents healthy (`FencingHealthy`) | Some agents unhealthy (`FencingUnhealthy`) - emits warnings | + +### Resource-Level Conditions + +Each resource in the `resources` array and each fencing agent in the `fencingAgents` array has its own conditions. **Per API conventions, conditions are optional but when present must include all eight types (enforced via MinItems=8 and XValidation rules).** + +| Condition | True | False | +|-----------|------|-------| +| `Healthy` | Resource is healthy (`ResourceHealthy`) | Resource has issues (`ResourceUnhealthy`) | +| `InService` | In service (`InService`) | In maintenance (`InMaintenance`) | +| `Managed` | Managed by pacemaker (`Managed`) | Not managed (`Unmanaged`) | +| `Enabled` | Resource is enabled (`Enabled`) | Resource is disabled (`Disabled`) | +| `Operational` | Resource is operational (`Operational`) | Resource has failed (`Failed`) | +| `Active` | Resource is active (`Active`) | Resource is not active (`Inactive`) | +| `Started` | Resource is started (`Started`) | Resource is stopped (`Stopped`) | +| `Schedulable` | Resource is schedulable (`Schedulable`) | Resource is not schedulable (`Unschedulable`) | + +### Validation Rules + +**Resource naming:** +- Resource name must be "cluster" (singleton) + +**Node name validation:** +- Must be a lowercase RFC 1123 subdomain name +- Consists of lowercase alphanumeric characters, '-' or '.' +- Must start and end with an alphanumeric character +- Maximum 253 characters + +**Node addresses:** +- Uses `corev1.NodeAddress` for consistency with Kubernetes Node API +- Pacemaker allows multiple addresses for Corosync communication between nodes (1-8 addresses) +- The first InternalIP address in the list is used for IP-based peer URLs for etcd membership +- Each address must be a valid global unicast IPv4 or IPv6 address in canonical form +- Excludes loopback, link-local, and multicast addresses + +**Timestamp validation:** +- `lastUpdated` is optional but once set cannot be removed +- Timestamps must always increase (prevents stale updates from overwriting newer data) + +**Status fields:** +- `status` - Optional on creation (pointer type), populated via status subresource +- `lastUpdated` - Optional timestamp for staleness detection +- `nodes` - Optional array of node statuses + +**Conditions validation (all levels):** +- Per Kubernetes API conventions, conditions fields are marked `+optional` +- However, MinItems and XValidation rules enforce that when conditions are present, they must include all required types +- Cluster-level: MinItems=3 (Healthy, InService, NodeCountAsExpected) +- Node-level: MinItems=9 (Healthy, Online, InService, Active, Ready, Clean, Member, FencingAvailable, FencingHealthy) +- Resource-level: MinItems=8 (Healthy, InService, Managed, Enabled, Operational, Active, Started, Schedulable) +- Fencing agent-level: MinItems=8 (same conditions as resources) + +**Resource names:** +- Valid values are: `Kubelet`, `Etcd` +- Both resources must be present in each node's `resources` array (MinItems=2) +- Fencing agents are tracked separately in the `fencingAgents` array + +**Fencing agent fields:** +- `name`: The pacemaker resource name (e.g., "master-0_redfish"), max 253 characters +- `method`: The fencing method (e.g., "redfish", "ipmi", "fence_aws"), max 63 characters +- `conditions`: Same 8 conditions as resources (optional, but min 8 items when present) + +### Usage + +The cluster-etcd-operator healthcheck controller watches this resource and updates operator conditions based on +the cluster state. The aggregate `Healthy` conditions at each level (cluster, node, resource) provide a quick +way to determine overall health. diff --git a/etcd/install.go b/etcd/install.go new file mode 100644 index 00000000000..7e7474152c4 --- /dev/null +++ b/etcd/install.go @@ -0,0 +1,26 @@ +package etcd + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + v1alpha1 "github.com/openshift/api/etcd/v1alpha1" +) + +const ( + GroupName = "etcd.openshift.io" +) + +var ( + schemeBuilder = runtime.NewSchemeBuilder(v1alpha1.Install) + // Install is a function which adds every version of this group to a scheme + Install = schemeBuilder.AddToScheme +) + +func Resource(resource string) schema.GroupResource { + return schema.GroupResource{Group: GroupName, Resource: resource} +} + +func Kind(kind string) schema.GroupKind { + return schema.GroupKind{Group: GroupName, Kind: kind} +} diff --git a/etcd/v1alpha1/Makefile b/etcd/v1alpha1/Makefile new file mode 100644 index 00000000000..3d019662af6 --- /dev/null +++ b/etcd/v1alpha1/Makefile @@ -0,0 +1,3 @@ +.PHONY: test +test: + make -C ../../tests test GINKGO_EXTRA_ARGS=--focus="etcd.openshift.io/v1alpha1" diff --git a/etcd/v1alpha1/doc.go b/etcd/v1alpha1/doc.go new file mode 100644 index 00000000000..aea92fb381a --- /dev/null +++ b/etcd/v1alpha1/doc.go @@ -0,0 +1,6 @@ +// +k8s:deepcopy-gen=package,register +// +k8s:defaulter-gen=TypeMeta +// +k8s:openapi-gen=true +// +openshift:featuregated-schema-gen=true +// +groupName=etcd.openshift.io +package v1alpha1 diff --git a/etcd/v1alpha1/register.go b/etcd/v1alpha1/register.go new file mode 100644 index 00000000000..1dc6482f832 --- /dev/null +++ b/etcd/v1alpha1/register.go @@ -0,0 +1,39 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + GroupName = "etcd.openshift.io" + GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + // Install is a function which adds this version to a scheme + Install = schemeBuilder.AddToScheme + + // SchemeGroupVersion generated code relies on this name + // Deprecated + SchemeGroupVersion = GroupVersion + // AddToScheme exists solely to keep the old generators creating valid code + // DEPRECATED + AddToScheme = schemeBuilder.AddToScheme +) + +// Resource generated code relies on this being here, but it logically belongs to the group +// DEPRECATED +func Resource(resource string) schema.GroupResource { + return schema.GroupResource{Group: GroupName, Resource: resource} +} + +func addKnownTypes(scheme *runtime.Scheme) error { + metav1.AddToGroupVersion(scheme, GroupVersion) + + scheme.AddKnownTypes(GroupVersion, + &PacemakerCluster{}, + &PacemakerClusterList{}, + ) + + return nil +} diff --git a/etcd/v1alpha1/tests/pacemakerclusters.etcd.openshift.io/DualReplica.yaml b/etcd/v1alpha1/tests/pacemakerclusters.etcd.openshift.io/DualReplica.yaml new file mode 100644 index 00000000000..67a361c9b5a --- /dev/null +++ b/etcd/v1alpha1/tests/pacemakerclusters.etcd.openshift.io/DualReplica.yaml @@ -0,0 +1,681 @@ +apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this +name: "PacemakerCluster" +crdName: pacemakerclusters.etcd.openshift.io +featureGate: DualReplica +tests: + onCreate: + - name: Should be able to create a minimal PacemakerCluster + initial: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + expected: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + - name: Should reject PacemakerCluster with wrong name + initial: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: wrong-name + expectedError: "PacemakerCluster must be named 'cluster'" + onUpdate: + # Full healthy cluster status test with fencingAgents + - name: Should accept full healthy cluster status with fencingAgents + initial: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + updated: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + status: + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ClusterHealthy + message: "Cluster is healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: NodeCountAsExpected + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: AsExpected + message: "Expected nodes present" + lastUpdated: "2024-01-01T00:00:01Z" + nodes: + - name: node1.example.com + addresses: + - type: InternalIP + address: "192.168.1.1" + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: NodeHealthy + message: "Node healthy" + - type: Online + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Online + message: "Online" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Ready + message: "Ready" + - type: Clean + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Clean + message: "Clean" + - type: Member + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Member + message: "Member" + - type: FencingAvailable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: FencingAvailable + message: "Fencing available" + - type: FencingHealthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: FencingHealthy + message: "All fencing agents healthy" + resources: + - name: Kubelet + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + - name: Etcd + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + fencingAgents: + - name: node1.example.com_redfish + method: redfish + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + expected: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + status: + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ClusterHealthy + message: "Cluster is healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: NodeCountAsExpected + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: AsExpected + message: "Expected nodes present" + lastUpdated: "2024-01-01T00:00:01Z" + nodes: + - name: node1.example.com + addresses: + - type: InternalIP + address: "192.168.1.1" + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: NodeHealthy + message: "Node healthy" + - type: Online + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Online + message: "Online" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Ready + message: "Ready" + - type: Clean + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Clean + message: "Clean" + - type: Member + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Member + message: "Member" + - type: FencingAvailable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: FencingAvailable + message: "Fencing available" + - type: FencingHealthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: FencingHealthy + message: "All fencing agents healthy" + resources: + - name: Kubelet + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + - name: Etcd + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + fencingAgents: + - name: node1.example.com_redfish + method: redfish + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + # Missing required conditions tests + - name: Should reject cluster status missing Healthy condition + initial: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + updated: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + status: + conditions: + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: NodeCountAsExpected + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: AsExpected + message: "Expected" + - type: Extra + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Extra + message: "Extra" + lastUpdated: "2024-01-01T00:00:01Z" + nodes: [] + expectedStatusError: "conditions must contain a condition of type Healthy" + # Missing FencingAvailable condition test + - name: Should reject node missing FencingAvailable condition + initial: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + updated: | + apiVersion: etcd.openshift.io/v1alpha1 + kind: PacemakerCluster + metadata: + name: cluster + status: + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ClusterHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: NodeCountAsExpected + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: AsExpected + message: "Expected" + lastUpdated: "2024-01-01T00:00:01Z" + nodes: + - name: node1.example.com + addresses: + - type: InternalIP + address: "192.168.1.1" + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: NodeHealthy + message: "Healthy" + - type: Online + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Online + message: "Online" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Ready + message: "Ready" + - type: Clean + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Clean + message: "Clean" + - type: Member + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Member + message: "Member" + - type: FencingHealthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: FencingHealthy + message: "All agents healthy" + - type: Extra + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Extra + message: "Placeholder" + resources: + - name: Kubelet + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + - name: Etcd + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + fencingAgents: + - name: node1.example.com_redfish + method: redfish + conditions: + - type: Healthy + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: ResourceHealthy + message: "Healthy" + - type: InService + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: InService + message: "In service" + - type: Managed + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Managed + message: "Managed" + - type: Enabled + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Enabled + message: "Enabled" + - type: Operational + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Operational + message: "Operational" + - type: Active + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Active + message: "Active" + - type: Started + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Started + message: "Started" + - type: Schedulable + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + reason: Schedulable + message: "Schedulable" + expectedStatusError: "conditions must contain a condition of type FencingAvailable" diff --git a/etcd/v1alpha1/types_pacemakercluster.go b/etcd/v1alpha1/types_pacemakercluster.go new file mode 100644 index 00000000000..0c2171e1a38 --- /dev/null +++ b/etcd/v1alpha1/types_pacemakercluster.go @@ -0,0 +1,685 @@ +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PacemakerCluster is used in Two Node OpenShift with Fencing deployments to monitor the health +// of etcd running under pacemaker. + +// Cluster-level condition types for PacemakerCluster.status.conditions +const ( + // ClusterHealthyConditionType tracks the overall health of the pacemaker cluster. + // This is an aggregate condition that reflects the health of all cluster-level conditions and node health. + // Specifically, it aggregates the following conditions: + // - ClusterInServiceConditionType + // - ClusterNodeCountAsExpectedConditionType + // - NodeHealthyConditionType (for each node) + // When True, the cluster is healthy with reason "ClusterHealthy". + // When False, the cluster is unhealthy with reason "ClusterUnhealthy". + ClusterHealthyConditionType = "Healthy" + + // ClusterInServiceConditionType tracks whether the cluster is in service (not in maintenance mode). + // Maintenance mode is a cluster-wide setting that prevents pacemaker from starting or stopping resources. + // When True, the cluster is in service with reason "InService". This is the normal operating state. + // When False, the cluster is in maintenance mode with reason "InMaintenance". This is an unexpected state. + ClusterInServiceConditionType = "InService" + + // ClusterNodeCountAsExpectedConditionType tracks whether the cluster has the expected number of nodes. + // For Two Node OpenShift with Fencing, we are expecting exactly 2 nodes. + // When True, the expected number of nodes are present with reason "AsExpected". + // When False, the node count is incorrect with reason "InsufficientNodes" or "ExcessiveNodes". + ClusterNodeCountAsExpectedConditionType = "NodeCountAsExpected" +) + +// ClusterHealthy condition reasons +const ( + // ClusterHealthyReasonHealthy means the pacemaker cluster is healthy and operating normally. + ClusterHealthyReasonHealthy = "ClusterHealthy" + + // ClusterHealthyReasonUnhealthy means the pacemaker cluster has issues that need investigation. + ClusterHealthyReasonUnhealthy = "ClusterUnhealthy" +) + +// ClusterInService condition reasons +const ( + // ClusterInServiceReasonInService means the cluster is in service (not in maintenance mode). + // This is the normal operating state. + ClusterInServiceReasonInService = "InService" + + // ClusterInServiceReasonInMaintenance means the cluster is in maintenance mode. + // In maintenance mode, pacemaker will not start or stop any resources. Entering and exiting this state requires + // manual user intervention, and is unexpected during normal cluster operation. + ClusterInServiceReasonInMaintenance = "InMaintenance" +) + +// ClusterNodeCountAsExpected condition reasons +const ( + // ClusterNodeCountAsExpectedReasonAsExpected means the expected number of nodes are present. + // For Two Node OpenShift with Fencing, we are expecting exactly 2 nodes. This is the expected healthy state. + ClusterNodeCountAsExpectedReasonAsExpected = "AsExpected" + + // ClusterNodeCountAsExpectedReasonInsufficientNodes means fewer nodes than expected are present. + // For Two Node OpenShift with Fencing, this means that less than 2 nodes are present. Under normal operation, this will only happen during + // a node replacement operation. It's also possible to enter this state with manual user intervention, but + // will also require user intervention to restore normal functionality. + ClusterNodeCountAsExpectedReasonInsufficientNodes = "InsufficientNodes" + + // ClusterNodeCountAsExpectedReasonExcessiveNodes means more nodes than expected are present. + // For Two Node OpenShift with Fencing, this means more than 2 nodes are present. This should be investigated as it is unexpected and should + // never happen during normal cluster operation. It is possible to enter this state with manual user intervention, + // but will also require user intervention to restore normal functionality. + ClusterNodeCountAsExpectedReasonExcessiveNodes = "ExcessiveNodes" +) + +// Node-level condition types for PacemakerCluster.status.nodes[].conditions +const ( + // NodeHealthyConditionType tracks the overall health of a node in the pacemaker cluster. + // This is an aggregate condition that reflects the health of all node-level conditions and resource health. + // Specifically, it aggregates the following conditions: + // - NodeOnlineConditionType + // - NodeInServiceConditionType + // - NodeActiveConditionType + // - NodeReadyConditionType + // - NodeCleanConditionType + // - NodeMemberConditionType + // - NodeFencingAvailableConditionType + // - NodeFencingHealthyConditionType + // - ResourceHealthyConditionType (for each resource in the node's resources list) + // When True, the node is healthy with reason "NodeHealthy". + // When False, the node is unhealthy with reason "NodeUnhealthy". + NodeHealthyConditionType = "Healthy" + + // NodeOnlineConditionType tracks whether a node is online. + // When True, the node is online with reason "Online". This is the normal operating state. + // When False, the node is offline with reason "Offline". This can occur during reboots, failures, maintenance, or replacement. + NodeOnlineConditionType = "Online" + + // NodeInServiceConditionType tracks whether a node is in service (not in maintenance mode). + // A node in maintenance mode is ignored by pacemaker while maintenance mode is active. + // When True, the node is in service with reason "InService". This is the normal operating state. + // When False, the node is in maintenance mode with reason "InMaintenance". This is an unexpected state. + NodeInServiceConditionType = "InService" + + // NodeActiveConditionType tracks whether a node is active (not in standby mode). + // When a node enters standby mode, pacemaker moves its resources to other nodes in the cluster. + // In Two Node OpenShift with Fencing, we do not use standby mode during normal operation. + // When True, the node is active with reason "Active". This is the normal operating state. + // When False, the node is in standby mode with reason "Standby". This is an unexpected state. + NodeActiveConditionType = "Active" + + // NodeReadyConditionType tracks whether a node is ready (not in a pending state). + // A node in a pending state is in the process of joining or leaving the cluster. + // When True, the node is ready with reason "Ready". This is the normal operating state. + // When False, the node is pending with reason "Pending". This is expected to be temporary. + NodeReadyConditionType = "Ready" + + // NodeCleanConditionType tracks whether a node is in a clean state. + // An unclean state means that pacemaker was unable to confirm the node's state, which signifies issues + // in fencing, communication, or configuration. + // When True, the node is clean with reason "Clean". This is the normal operating state. + // When False, the node is unclean with reason "Unclean". This is an unexpected state. + NodeCleanConditionType = "Clean" + + // NodeMemberConditionType tracks whether a node is a member of the cluster. + // Some configurations may use remote nodes or ping nodes, which are nodes that are not members. + // For Two Node OpenShift with Fencing, we expect both nodes to be members. + // When True, the node is a member with reason "Member". This is the normal operating state. + // When False, the node is not a member with reason "NotMember". This is an unexpected state. + NodeMemberConditionType = "Member" + + // NodeFencingAvailableConditionType tracks whether a node can be fenced by at least one fencing agent. + // For Two Node OpenShift with Fencing, each node needs at least one healthy fencing agent to ensure + // that the cluster can recover from a node failure via STONITH (Shoot The Other Node In The Head). + // When True, at least one fencing agent is healthy with reason "FencingAvailable". + // When False, all fencing agents are unhealthy with reason "FencingUnavailable". This is a critical + // state that should degrade the operator. + NodeFencingAvailableConditionType = "FencingAvailable" + + // NodeFencingHealthyConditionType tracks whether all fencing agents for a node are healthy. + // This is an aggregate condition that reflects the health of all fencing agents targeting this node. + // When True, all fencing agents are healthy with reason "FencingHealthy". + // When False, one or more fencing agents are unhealthy with reason "FencingUnhealthy". Warning events + // should be emitted for failing agents, but the operator should not be degraded if FencingAvailable is True. + NodeFencingHealthyConditionType = "FencingHealthy" +) + +// NodeHealthy condition reasons +const ( + // NodeHealthyReasonHealthy means the node is healthy and operating normally. + NodeHealthyReasonHealthy = "NodeHealthy" + + // NodeHealthyReasonUnhealthy means the node has issues that need investigation. + NodeHealthyReasonUnhealthy = "NodeUnhealthy" +) + +// NodeOnline condition reasons +const ( + // NodeOnlineReasonOnline means the node is online. This is the normal operating state. + NodeOnlineReasonOnline = "Online" + + // NodeOnlineReasonOffline means the node is offline. + NodeOnlineReasonOffline = "Offline" +) + +// NodeInService condition reasons +const ( + // NodeInServiceReasonInService means the node is in service (not in maintenance mode). + // This is the normal operating state. + NodeInServiceReasonInService = "InService" + + // NodeInServiceReasonInMaintenance means the node is in maintenance mode. + // This is an unexpected state. + NodeInServiceReasonInMaintenance = "InMaintenance" +) + +// NodeActive condition reasons +const ( + // NodeActiveReasonActive means the node is active (not in standby mode). + // This is the normal operating state. + NodeActiveReasonActive = "Active" + + // NodeActiveReasonStandby means the node is in standby mode. + // This is an unexpected state. + NodeActiveReasonStandby = "Standby" +) + +// NodeReady condition reasons +const ( + // NodeReadyReasonReady means the node is ready (not in a pending state). + // This is the normal operating state. + NodeReadyReasonReady = "Ready" + + // NodeReadyReasonPending means the node is joining or leaving the cluster. + // This state is expected to be temporary. + NodeReadyReasonPending = "Pending" +) + +// NodeClean condition reasons +const ( + // NodeCleanReasonClean means the node is in a clean state. + // This is the normal operating state. + NodeCleanReasonClean = "Clean" + + // NodeCleanReasonUnclean means the node is in an unclean state. + // Pacemaker was unable to confirm the node's state, which signifies issues in fencing, communication, or configuration. + // This is an unexpected state. + NodeCleanReasonUnclean = "Unclean" +) + +// NodeMember condition reasons +const ( + // NodeMemberReasonMember means the node is a member of the cluster. + // For Two Node OpenShift with Fencing, we expect both nodes to be members. This is the normal operating state. + NodeMemberReasonMember = "Member" + + // NodeMemberReasonNotMember means the node is not a member of the cluster. + // This is an unexpected state. + NodeMemberReasonNotMember = "NotMember" +) + +// NodeFencingAvailable condition reasons +const ( + // NodeFencingAvailableReasonAvailable means at least one fencing agent for this node is healthy. + // The cluster can fence this node if needed. This is the normal operating state. + NodeFencingAvailableReasonAvailable = "FencingAvailable" + + // NodeFencingAvailableReasonUnavailable means all fencing agents for this node are unhealthy. + // The cluster cannot fence this node, which compromises high availability. + // This is a critical state that should degrade the operator. + NodeFencingAvailableReasonUnavailable = "FencingUnavailable" +) + +// NodeFencingHealthy condition reasons +const ( + // NodeFencingHealthyReasonHealthy means all fencing agents for this node are healthy. + // This is the ideal operating state with full redundancy. + NodeFencingHealthyReasonHealthy = "FencingHealthy" + + // NodeFencingHealthyReasonUnhealthy means one or more fencing agents for this node are unhealthy. + // Warning events should be emitted for failing agents, but the operator should not be degraded + // if FencingAvailable is still True. + NodeFencingHealthyReasonUnhealthy = "FencingUnhealthy" +) + +// Resource-level condition types for PacemakerCluster.status.nodes[].resources[].conditions +const ( + // ResourceHealthyConditionType tracks the overall health of a pacemaker resource. + // This is an aggregate condition that reflects the health of all resource-level conditions. + // Specifically, it aggregates the following conditions: + // - ResourceInServiceConditionType + // - ResourceManagedConditionType + // - ResourceEnabledConditionType + // - ResourceOperationalConditionType + // - ResourceActiveConditionType + // - ResourceStartedConditionType + // - ResourceSchedulableConditionType + // When True, the resource is healthy with reason "ResourceHealthy". + // When False, the resource is unhealthy with reason "ResourceUnhealthy". + ResourceHealthyConditionType = "Healthy" + + // ResourceInServiceConditionType tracks whether a resource is in service (not in maintenance mode). + // Resources in maintenance mode are not monitored or moved by pacemaker. + // In Two Node OpenShift with Fencing, we do not expect any resources to be in maintenance mode. + // When True, the resource is in service with reason "InService". This is the normal operating state. + // When False, the resource is in maintenance mode with reason "InMaintenance". This is an unexpected state. + ResourceInServiceConditionType = "InService" + + // ResourceManagedConditionType tracks whether a resource is managed by pacemaker. + // Resources that are not managed by pacemaker are effectively invisible to the pacemaker HA logic. + // For Two Node OpenShift with Fencing, all resources are expected to be managed. + // When True, the resource is managed with reason "Managed". This is the normal operating state. + // When False, the resource is not managed with reason "Unmanaged". This is an unexpected state. + ResourceManagedConditionType = "Managed" + + // ResourceEnabledConditionType tracks whether a resource is enabled. + // Resources that are disabled are stopped and not automatically managed or started by the cluster. + // In Two Node OpenShift with Fencing, we do not expect any resources to be disabled. + // When True, the resource is enabled with reason "Enabled". This is the normal operating state. + // When False, the resource is disabled with reason "Disabled". This is an unexpected state. + ResourceEnabledConditionType = "Enabled" + + // ResourceOperationalConditionType tracks whether a resource is operational (not failed). + // A failed resource is one that is not able to start or is in an error state. + // When True, the resource is operational with reason "Operational". This is the normal operating state. + // When False, the resource has failed with reason "Failed". This is an unexpected state. + ResourceOperationalConditionType = "Operational" + + // ResourceActiveConditionType tracks whether a resource is active. + // An active resource is running on a cluster node. + // In Two Node OpenShift with Fencing, all resources are expected to be active. + // When True, the resource is active with reason "Active". This is the normal operating state. + // When False, the resource is not active with reason "Inactive". This is an unexpected state. + ResourceActiveConditionType = "Active" + + // ResourceStartedConditionType tracks whether a resource is started. + // It's normal for a resource like etcd to become stopped in the event of a quorum loss event because + // the pacemaker recovery logic will fence a node and restore etcd quorum on the surviving node as a cluster-of-one. + // A resource that stays stopped for an extended period of time is an unexpected state and should be investigated. + // When True, the resource is started with reason "Started". This is the normal operating state. + // When False, the resource is not started with reason "Stopped". This is expected to be temporary. + ResourceStartedConditionType = "Started" + + // ResourceSchedulableConditionType tracks whether a resource is schedulable (not blocked). + // A resource that is not schedulable is unable to start or move to a different node. + // In Two Node OpenShift with Fencing, we do not expect any resources to be unschedulable. + // When True, the resource is schedulable with reason "Schedulable". This is the normal operating state. + // When False, the resource is not schedulable with reason "Unschedulable". This is an unexpected state. + ResourceSchedulableConditionType = "Schedulable" +) + +// ResourceHealthy condition reasons +const ( + // ResourceHealthyReasonHealthy means the resource is healthy and operating normally. + ResourceHealthyReasonHealthy = "ResourceHealthy" + + // ResourceHealthyReasonUnhealthy means the resource has issues that need investigation. + ResourceHealthyReasonUnhealthy = "ResourceUnhealthy" +) + +// ResourceInService condition reasons +const ( + // ResourceInServiceReasonInService means the resource is in service (not in maintenance mode). + // This is the normal operating state. + ResourceInServiceReasonInService = "InService" + + // ResourceInServiceReasonInMaintenance means the resource is in maintenance mode. + // Resources in maintenance mode are not monitored or moved by pacemaker. This is an unexpected state. + ResourceInServiceReasonInMaintenance = "InMaintenance" +) + +// ResourceManaged condition reasons +const ( + // ResourceManagedReasonManaged means the resource is managed by pacemaker. + // This is the normal operating state. + ResourceManagedReasonManaged = "Managed" + + // ResourceManagedReasonUnmanaged means the resource is not managed by pacemaker. + // Resources that are not managed by pacemaker are effectively invisible to the pacemaker HA logic. + // This is an unexpected state. + ResourceManagedReasonUnmanaged = "Unmanaged" +) + +// ResourceEnabled condition reasons +const ( + // ResourceEnabledReasonEnabled means the resource is enabled. + // This is the normal operating state. + ResourceEnabledReasonEnabled = "Enabled" + + // ResourceEnabledReasonDisabled means the resource is disabled. + // Resources that are disabled are stopped and not automatically managed or started by the cluster. + // This is an unexpected state. + ResourceEnabledReasonDisabled = "Disabled" +) + +// ResourceOperational condition reasons +const ( + // ResourceOperationalReasonOperational means the resource is operational (not failed). + // This is the normal operating state. + ResourceOperationalReasonOperational = "Operational" + + // ResourceOperationalReasonFailed means the resource has failed. + // A failed resource is one that is not able to start or is in an error state. This is an unexpected state. + ResourceOperationalReasonFailed = "Failed" +) + +// ResourceActive condition reasons +const ( + // ResourceActiveReasonActive means the resource is active. + // An active resource is running on a cluster node. This is the normal operating state. + ResourceActiveReasonActive = "Active" + + // ResourceActiveReasonInactive means the resource is not active. + // This is an unexpected state. + ResourceActiveReasonInactive = "Inactive" +) + +// ResourceStarted condition reasons +const ( + // ResourceStartedReasonStarted means the resource is started. + // This is the normal operating state. + ResourceStartedReasonStarted = "Started" + + // ResourceStartedReasonStopped means the resource is stopped. + // It's normal for a resource like etcd to become stopped in the event of a quorum loss event because + // the pacemaker recovery logic will fence a node and restore etcd quorum on the surviving node as a cluster-of-one. + // A resource that stays stopped for an extended period of time is an unexpected state and should be investigated. + ResourceStartedReasonStopped = "Stopped" +) + +// ResourceSchedulable condition reasons +const ( + // ResourceSchedulableReasonSchedulable means the resource is schedulable (not blocked). + // This is the normal operating state. + ResourceSchedulableReasonSchedulable = "Schedulable" + + // ResourceSchedulableReasonUnschedulable means the resource is not schedulable (blocked). + // A resource that is not schedulable is unable to start or move to a different node. This is an unexpected state. + ResourceSchedulableReasonUnschedulable = "Unschedulable" +) + +// PacemakerClusterResourceName represents the name of a pacemaker resource. +// Fencing agents are tracked separately in the fencingAgents field. +// +kubebuilder:validation:Enum=Kubelet;Etcd +type PacemakerClusterResourceName string + +// PacemakerClusterResourceName values +const ( + // PacemakerClusterResourceNameKubelet is the kubelet pacemaker resource. + // The kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + PacemakerClusterResourceNameKubelet PacemakerClusterResourceName = "Kubelet" + + // PacemakerClusterResourceNameEtcd is the etcd pacemaker resource. + // The etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + PacemakerClusterResourceNameEtcd PacemakerClusterResourceName = "Etcd" +) + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. +// PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is "cluster". This +// resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments. +// +// Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. +// +openshift:compatibility-gen:level=4 +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=pacemakerclusters,scope=Cluster,singular=pacemakercluster +// +kubebuilder:subresource:status +// +openshift:api-approved.openshift.io=https://github.com/openshift/api/pull/2544 +// +openshift:file-pattern=cvoRunLevel=0000_25,operatorName=etcd,operatorOrdering=01,operatorComponent=two-node-fencing +// +openshift:enable:FeatureGate=DualReplica +// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'cluster'",message="PacemakerCluster must be named 'cluster'" +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.status) || has(self.status)",message="status cannot be removed once set" +type PacemakerCluster struct { + metav1.TypeMeta `json:",inline"` + + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +required + metav1.ObjectMeta `json:"metadata,omitempty"` + + // status contains the actual pacemaker cluster status information collected from the cluster. + // The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. + // In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. + // This field is optional on creation - the status collector populates it immediately after creating + // the resource via the status subresource. + // +optional + Status *PacemakerClusterStatus `json:"status,omitempty"` +} + +// PacemakerClusterStatus contains the actual pacemaker cluster status information. As part of validating the status +// object, we need to ensure that the lastUpdated timestamp cannot be removed once set and cannot be set to an earlier +// timestamp than the current value. +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.lastUpdated) || has(self.lastUpdated)",message="lastUpdated cannot be removed once set" +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.lastUpdated) || !has(self.lastUpdated) || self.lastUpdated >= oldSelf.lastUpdated",message="lastUpdated cannot be set to an earlier timestamp" +type PacemakerClusterStatus struct { + // conditions represent the observations of the pacemaker cluster's current state. + // Known condition types are: "Healthy", "InService", "NodeCountAsExpected". + // The "Healthy" condition is an aggregate that tracks the overall health of the cluster. + // The "InService" condition tracks whether the cluster is in service (not in maintenance mode). + // The "NodeCountAsExpected" condition tracks whether the expected number of nodes are present. + // Each of these conditions is required, so the array must contain at least 3 items. + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MinItems=3 + // +kubebuilder:validation:MaxItems=8 + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Healthy')",message="conditions must contain a condition of type Healthy" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'InService')",message="conditions must contain a condition of type InService" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'NodeCountAsExpected')",message="conditions must contain a condition of type NodeCountAsExpected" + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // lastUpdated is the timestamp when this status was last updated. This is useful for identifying + // stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot + // be removed and cannot be set to an earlier timestamp than the current value. + // +kubebuilder:validation:Format=date-time + // +optional + LastUpdated metav1.Time `json:"lastUpdated,omitempty"` + + // nodes provides detailed information about each node in the cluster including per-node resource health. + // Each node entry includes the node's name, IP address, conditions, and resource status. + // The list can contain up to 32 nodes (the upper limit imposed by pacemaker). + // For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes + // are currently reporting status. + // +listType=map + // +listMapKey=name + // +kubebuilder:validation:MaxItems=32 + // +optional + Nodes []PacemakerClusterNodeStatus `json:"nodes,omitempty"` +} + +// PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including +// the node's conditions and the health of critical resources running on that node. +type PacemakerClusterNodeStatus struct { + // conditions represent the observations of the node's current state. + // Known condition types are: "Healthy", "Online", "InService", "Active", "Ready", "Clean", "Member", + // "FencingAvailable", "FencingHealthy". + // The "Healthy" condition is an aggregate that tracks the overall health of the node. + // The "Online" condition tracks whether the node is online. + // The "InService" condition tracks whether the node is in service (not in maintenance mode). + // The "Active" condition tracks whether the node is active (not in standby mode). + // The "Ready" condition tracks whether the node is ready (not in a pending state). + // The "Clean" condition tracks whether the node is in a clean (status known) state. + // The "Member" condition tracks whether the node is a member of the cluster. + // The "FencingAvailable" condition tracks whether this node can be fenced by at least one healthy agent. + // The "FencingHealthy" condition tracks whether all fencing agents for this node are healthy. + // Each of these conditions is required, so the array must contain at least 9 items. + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MinItems=9 + // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Healthy')",message="conditions must contain a condition of type Healthy" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Online')",message="conditions must contain a condition of type Online" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'InService')",message="conditions must contain a condition of type InService" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Active')",message="conditions must contain a condition of type Active" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Ready')",message="conditions must contain a condition of type Ready" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Clean')",message="conditions must contain a condition of type Clean" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Member')",message="conditions must contain a condition of type Member" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'FencingAvailable')",message="conditions must contain a condition of type FencingAvailable" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'FencingHealthy')",message="conditions must contain a condition of type FencingHealthy" + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase + // RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with + // an alphanumeric character, and be at most 253 characters in length. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:XValidation:rule="!format.dns1123Subdomain().validate(self).hasValue()",message="name must be a lowercase RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character" + // +required + Name string `json:"name,omitempty"` + + // addresses is a list of addresses reachable to the node. + // This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. + // Pacemaker allows multiple IP addresses for Corosync communication between nodes. + // The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. + // Each address must be a valid global unicast IPv4 or IPv6 address in canonical form + // (e.g., "192.168.1.1" not "192.168.001.001", or "2001:db8::1" not "2001:0db8::1"). + // This excludes special addresses like unspecified, loopback, link-local, and multicast. + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=8 + // +required + Addresses []corev1.NodeAddress `json:"addresses,omitempty"` + + // resources contains the status of pacemaker resources scheduled on this node. + // Each resource entry includes the resource name and its health conditions. + // For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. + // Both resources are required to be present, so the array must contain at least 2 items. + // Valid resource names are "Kubelet" and "Etcd". + // Fencing agents are tracked separately in the fencingAgents field. + // +listType=map + // +listMapKey=name + // +kubebuilder:validation:MinItems=2 + // +kubebuilder:validation:MaxItems=8 + // +kubebuilder:validation:XValidation:rule="self.exists(r, r.name == 'Kubelet')",message="resources must contain a resource named Kubelet" + // +kubebuilder:validation:XValidation:rule="self.exists(r, r.name == 'Etcd')",message="resources must contain a resource named Etcd" + // +optional + Resources []PacemakerClusterResourceStatus `json:"resources,omitempty"` + + // fencingAgents contains the status of fencing agents that can fence this node. + // Unlike resources (which are scheduled to run on this node), fencing agents are mapped + // to the node they can fence (their target), not the node where monitoring operations run. + // Each fencing agent entry includes the agent name, fencing method, and health conditions. + // A node is considered fence-capable if at least one fencing agent is healthy. + // Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy. + // +listType=map + // +listMapKey=name + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=8 + // +optional + FencingAgents []PacemakerClusterFencingAgentStatus `json:"fencingAgents,omitempty"` +} + +// PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. +// Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. +// Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they +// can fence), not the node where their monitoring operations are scheduled. +type PacemakerClusterFencingAgentStatus struct { + // conditions represent the observations of the fencing agent's current state. + // Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + // "Active", "Started", "Schedulable". + // The "Healthy" condition is an aggregate that tracks the overall health of the fencing agent. + // The "InService" condition tracks whether the fencing agent is in service (not in maintenance mode). + // The "Managed" condition tracks whether the fencing agent is managed by pacemaker. + // The "Enabled" condition tracks whether the fencing agent is enabled. + // The "Operational" condition tracks whether the fencing agent is operational (not failed). + // The "Active" condition tracks whether the fencing agent is active (available to be used). + // The "Started" condition tracks whether the fencing agent is started. + // The "Schedulable" condition tracks whether the fencing agent is schedulable (not blocked). + // Each of these conditions is required, so the array must contain at least 8 items. + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MinItems=8 + // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Healthy')",message="conditions must contain a condition of type Healthy" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'InService')",message="conditions must contain a condition of type InService" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Managed')",message="conditions must contain a condition of type Managed" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Enabled')",message="conditions must contain a condition of type Enabled" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Operational')",message="conditions must contain a condition of type Operational" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Active')",message="conditions must contain a condition of type Active" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Started')",message="conditions must contain a condition of type Started" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Schedulable')",message="conditions must contain a condition of type Schedulable" + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // name is the pacemaker resource name of the fencing agent (e.g., "master-0_redfish", "master-1_ipmi"). + // The name typically includes the target node name and fencing method. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +required + Name string `json:"name,omitempty"` + + // method is the fencing method used by this agent (e.g., "redfish", "ipmi", "fence_aws"). + // This is extracted from the pacemaker resource agent type. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +required + Method string `json:"method,omitempty"` +} + +// PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. +// A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or +// applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. +// For Two Node OpenShift with Fencing, we track two resources per node: +// - Kubelet (the Kubernetes node agent and a prerequisite for etcd) +// - Etcd (the distributed key-value store) +// +// Fencing agents are tracked separately in the fencingAgents field because they are mapped to +// their target node (the node they can fence), not the node where monitoring operations are scheduled. +type PacemakerClusterResourceStatus struct { + // conditions represent the observations of the resource's current state. + // Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + // "Active", "Started", "Schedulable". + // The "Healthy" condition is an aggregate that tracks the overall health of the resource. + // The "InService" condition tracks whether the resource is in service (not in maintenance mode). + // The "Managed" condition tracks whether the resource is managed by pacemaker. + // The "Enabled" condition tracks whether the resource is enabled. + // The "Operational" condition tracks whether the resource is operational (not failed). + // The "Active" condition tracks whether the resource is active (available to be used). + // The "Started" condition tracks whether the resource is started. + // The "Schedulable" condition tracks whether the resource is schedulable (not blocked). + // Each of these conditions is required, so the array must contain at least 8 items. + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MinItems=8 + // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Healthy')",message="conditions must contain a condition of type Healthy" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'InService')",message="conditions must contain a condition of type InService" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Managed')",message="conditions must contain a condition of type Managed" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Enabled')",message="conditions must contain a condition of type Enabled" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Operational')",message="conditions must contain a condition of type Operational" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Active')",message="conditions must contain a condition of type Active" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Started')",message="conditions must contain a condition of type Started" + // +kubebuilder:validation:XValidation:rule="self.exists(c, c.type == 'Schedulable')",message="conditions must contain a condition of type Schedulable" + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // name is the name of the pacemaker resource. + // Valid values are "Kubelet" and "Etcd". + // The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + // The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + // Fencing agents are tracked separately in the node's fencingAgents field. + // +required + Name PacemakerClusterResourceName `json:"name,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// PacemakerClusterList contains a list of PacemakerCluster objects. PacemakerCluster is a cluster-scoped singleton +// resource; only one instance named "cluster" may exist. This list type exists only to satisfy Kubernetes API +// conventions. +// +// Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. +// +openshift:compatibility-gen:level=4 +type PacemakerClusterList struct { + metav1.TypeMeta `json:",inline"` + + // metadata is the standard list's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + metav1.ListMeta `json:"metadata,omitempty"` + + // items is a list of PacemakerCluster objects. + Items []PacemakerCluster `json:"items"` +} diff --git a/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-CustomNoUpgrade.crd.yaml b/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-CustomNoUpgrade.crd.yaml new file mode 100644 index 00000000000..4e401fca37a --- /dev/null +++ b/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-CustomNoUpgrade.crd.yaml @@ -0,0 +1,598 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/2544 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: CustomNoUpgrade + name: pacemakerclusters.etcd.openshift.io +spec: + group: etcd.openshift.io + names: + kind: PacemakerCluster + listKind: PacemakerClusterList + plural: pacemakerclusters + singular: pacemakercluster + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. + PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is "cluster". This + resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments. + + Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + status: + description: |- + status contains the actual pacemaker cluster status information collected from the cluster. + The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. + In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. + This field is optional on creation - the status collector populates it immediately after creating + the resource via the status subresource. + properties: + conditions: + description: |- + conditions represent the observations of the pacemaker cluster's current state. + Known condition types are: "Healthy", "InService", "NodeCountAsExpected". + The "Healthy" condition is an aggregate that tracks the overall health of the cluster. + The "InService" condition tracks whether the cluster is in service (not in maintenance mode). + The "NodeCountAsExpected" condition tracks whether the expected number of nodes are present. + Each of these conditions is required, so the array must contain at least 3 items. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 3 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type NodeCountAsExpected + rule: self.exists(c, c.type == 'NodeCountAsExpected') + lastUpdated: + description: |- + lastUpdated is the timestamp when this status was last updated. This is useful for identifying + stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot + be removed and cannot be set to an earlier timestamp than the current value. + format: date-time + type: string + nodes: + description: |- + nodes provides detailed information about each node in the cluster including per-node resource health. + Each node entry includes the node's name, IP address, conditions, and resource status. + The list can contain up to 32 nodes (the upper limit imposed by pacemaker). + For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes + are currently reporting status. + items: + description: |- + PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including + the node's conditions and the health of critical resources running on that node. + properties: + addresses: + description: |- + addresses is a list of addresses reachable to the node. + This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. + Pacemaker allows multiple IP addresses for Corosync communication between nodes. + The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. + Each address must be a valid global unicast IPv4 or IPv6 address in canonical form + (e.g., "192.168.1.1" not "192.168.001.001", or "2001:db8::1" not "2001:0db8::1"). + This excludes special addresses like unspecified, loopback, link-local, and multicast. + items: + description: NodeAddress contains information for the node's + address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + conditions: + description: |- + conditions represent the observations of the node's current state. + Known condition types are: "Healthy", "Online", "InService", "Active", "Ready", "Clean", "Member", + "FencingAvailable", "FencingHealthy". + The "Healthy" condition is an aggregate that tracks the overall health of the node. + The "Online" condition tracks whether the node is online. + The "InService" condition tracks whether the node is in service (not in maintenance mode). + The "Active" condition tracks whether the node is active (not in standby mode). + The "Ready" condition tracks whether the node is ready (not in a pending state). + The "Clean" condition tracks whether the node is in a clean (status known) state. + The "Member" condition tracks whether the node is a member of the cluster. + The "FencingAvailable" condition tracks whether this node can be fenced by at least one healthy agent. + The "FencingHealthy" condition tracks whether all fencing agents for this node are healthy. + Each of these conditions is required, so the array must contain at least 9 items. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 9 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Ready + rule: self.exists(c, c.type == 'Ready') + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type Online + rule: self.exists(c, c.type == 'Online') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type Clean + rule: self.exists(c, c.type == 'Clean') + - message: conditions must contain a condition of type Member + rule: self.exists(c, c.type == 'Member') + - message: conditions must contain a condition of type FencingAvailable + rule: self.exists(c, c.type == 'FencingAvailable') + - message: conditions must contain a condition of type FencingHealthy + rule: self.exists(c, c.type == 'FencingHealthy') + fencingAgents: + description: |- + fencingAgents contains the status of fencing agents that can fence this node. + Unlike resources (which are scheduled to run on this node), fencing agents are mapped + to the node they can fence (their target), not the node where monitoring operations run. + Each fencing agent entry includes the agent name, fencing method, and health conditions. + A node is considered fence-capable if at least one fencing agent is healthy. + Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy. + items: + description: |- + PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. + Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. + Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they + can fence), not the node where their monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the fencing agent's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the fencing agent. + The "InService" condition tracks whether the fencing agent is in service (not in maintenance mode). + The "Managed" condition tracks whether the fencing agent is managed by pacemaker. + The "Enabled" condition tracks whether the fencing agent is enabled. + The "Operational" condition tracks whether the fencing agent is operational (not failed). + The "Active" condition tracks whether the fencing agent is active (available to be used). + The "Started" condition tracks whether the fencing agent is started. + The "Schedulable" condition tracks whether the fencing agent is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + method: + description: |- + method is the fencing method used by this agent (e.g., "redfish", "ipmi", "fence_aws"). + This is extracted from the pacemaker resource agent type. + maxLength: 63 + minLength: 1 + type: string + name: + description: |- + name is the pacemaker resource name of the fencing agent (e.g., "master-0_redfish", "master-1_ipmi"). + The name typically includes the target node name and fencing method. + maxLength: 253 + minLength: 1 + type: string + required: + - method + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + name: + description: |- + name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase + RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with + an alphanumeric character, and be at most 253 characters in length. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must be a lowercase RFC 1123 subdomain consisting + of lowercase alphanumeric characters, '-' or '.', and must + start and end with an alphanumeric character + rule: '!format.dns1123Subdomain().validate(self).hasValue()' + resources: + description: |- + resources contains the status of pacemaker resources scheduled on this node. + Each resource entry includes the resource name and its health conditions. + For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. + Both resources are required to be present, so the array must contain at least 2 items. + Valid resource names are "Kubelet" and "Etcd". + Fencing agents are tracked separately in the fencingAgents field. + items: + description: |- + PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. + A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or + applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. + For Two Node OpenShift with Fencing, we track two resources per node: + - Kubelet (the Kubernetes node agent and a prerequisite for etcd) + - Etcd (the distributed key-value store) + + Fencing agents are tracked separately in the fencingAgents field because they are mapped to + their target node (the node they can fence), not the node where monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the resource's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the resource. + The "InService" condition tracks whether the resource is in service (not in maintenance mode). + The "Managed" condition tracks whether the resource is managed by pacemaker. + The "Enabled" condition tracks whether the resource is enabled. + The "Operational" condition tracks whether the resource is operational (not failed). + The "Active" condition tracks whether the resource is active (available to be used). + The "Started" condition tracks whether the resource is started. + The "Schedulable" condition tracks whether the resource is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + name: + description: |- + name is the name of the pacemaker resource. + Valid values are "Kubelet" and "Etcd". + The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + Fencing agents are tracked separately in the node's fencingAgents field. + enum: + - Kubelet + - Etcd + type: string + required: + - name + type: object + maxItems: 8 + minItems: 2 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resources must contain a resource named Kubelet + rule: self.exists(r, r.name == 'Kubelet') + - message: resources must contain a resource named Etcd + rule: self.exists(r, r.name == 'Etcd') + required: + - addresses + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + x-kubernetes-validations: + - message: lastUpdated cannot be removed once set + rule: '!has(oldSelf.lastUpdated) || has(self.lastUpdated)' + - message: lastUpdated cannot be set to an earlier timestamp + rule: '!has(oldSelf.lastUpdated) || !has(self.lastUpdated) || self.lastUpdated + >= oldSelf.lastUpdated' + required: + - metadata + type: object + x-kubernetes-validations: + - message: PacemakerCluster must be named 'cluster' + rule: self.metadata.name == 'cluster' + - message: status cannot be removed once set + rule: '!has(oldSelf.status) || has(self.status)' + served: true + storage: true + subresources: + status: {} diff --git a/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-DevPreviewNoUpgrade.crd.yaml b/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-DevPreviewNoUpgrade.crd.yaml new file mode 100644 index 00000000000..85a8bd1f37f --- /dev/null +++ b/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-DevPreviewNoUpgrade.crd.yaml @@ -0,0 +1,598 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/2544 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: DevPreviewNoUpgrade + name: pacemakerclusters.etcd.openshift.io +spec: + group: etcd.openshift.io + names: + kind: PacemakerCluster + listKind: PacemakerClusterList + plural: pacemakerclusters + singular: pacemakercluster + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. + PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is "cluster". This + resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments. + + Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + status: + description: |- + status contains the actual pacemaker cluster status information collected from the cluster. + The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. + In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. + This field is optional on creation - the status collector populates it immediately after creating + the resource via the status subresource. + properties: + conditions: + description: |- + conditions represent the observations of the pacemaker cluster's current state. + Known condition types are: "Healthy", "InService", "NodeCountAsExpected". + The "Healthy" condition is an aggregate that tracks the overall health of the cluster. + The "InService" condition tracks whether the cluster is in service (not in maintenance mode). + The "NodeCountAsExpected" condition tracks whether the expected number of nodes are present. + Each of these conditions is required, so the array must contain at least 3 items. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 3 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type NodeCountAsExpected + rule: self.exists(c, c.type == 'NodeCountAsExpected') + lastUpdated: + description: |- + lastUpdated is the timestamp when this status was last updated. This is useful for identifying + stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot + be removed and cannot be set to an earlier timestamp than the current value. + format: date-time + type: string + nodes: + description: |- + nodes provides detailed information about each node in the cluster including per-node resource health. + Each node entry includes the node's name, IP address, conditions, and resource status. + The list can contain up to 32 nodes (the upper limit imposed by pacemaker). + For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes + are currently reporting status. + items: + description: |- + PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including + the node's conditions and the health of critical resources running on that node. + properties: + addresses: + description: |- + addresses is a list of addresses reachable to the node. + This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. + Pacemaker allows multiple IP addresses for Corosync communication between nodes. + The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. + Each address must be a valid global unicast IPv4 or IPv6 address in canonical form + (e.g., "192.168.1.1" not "192.168.001.001", or "2001:db8::1" not "2001:0db8::1"). + This excludes special addresses like unspecified, loopback, link-local, and multicast. + items: + description: NodeAddress contains information for the node's + address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + conditions: + description: |- + conditions represent the observations of the node's current state. + Known condition types are: "Healthy", "Online", "InService", "Active", "Ready", "Clean", "Member", + "FencingAvailable", "FencingHealthy". + The "Healthy" condition is an aggregate that tracks the overall health of the node. + The "Online" condition tracks whether the node is online. + The "InService" condition tracks whether the node is in service (not in maintenance mode). + The "Active" condition tracks whether the node is active (not in standby mode). + The "Ready" condition tracks whether the node is ready (not in a pending state). + The "Clean" condition tracks whether the node is in a clean (status known) state. + The "Member" condition tracks whether the node is a member of the cluster. + The "FencingAvailable" condition tracks whether this node can be fenced by at least one healthy agent. + The "FencingHealthy" condition tracks whether all fencing agents for this node are healthy. + Each of these conditions is required, so the array must contain at least 9 items. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 9 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Ready + rule: self.exists(c, c.type == 'Ready') + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type Online + rule: self.exists(c, c.type == 'Online') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type Clean + rule: self.exists(c, c.type == 'Clean') + - message: conditions must contain a condition of type Member + rule: self.exists(c, c.type == 'Member') + - message: conditions must contain a condition of type FencingAvailable + rule: self.exists(c, c.type == 'FencingAvailable') + - message: conditions must contain a condition of type FencingHealthy + rule: self.exists(c, c.type == 'FencingHealthy') + fencingAgents: + description: |- + fencingAgents contains the status of fencing agents that can fence this node. + Unlike resources (which are scheduled to run on this node), fencing agents are mapped + to the node they can fence (their target), not the node where monitoring operations run. + Each fencing agent entry includes the agent name, fencing method, and health conditions. + A node is considered fence-capable if at least one fencing agent is healthy. + Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy. + items: + description: |- + PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. + Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. + Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they + can fence), not the node where their monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the fencing agent's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the fencing agent. + The "InService" condition tracks whether the fencing agent is in service (not in maintenance mode). + The "Managed" condition tracks whether the fencing agent is managed by pacemaker. + The "Enabled" condition tracks whether the fencing agent is enabled. + The "Operational" condition tracks whether the fencing agent is operational (not failed). + The "Active" condition tracks whether the fencing agent is active (available to be used). + The "Started" condition tracks whether the fencing agent is started. + The "Schedulable" condition tracks whether the fencing agent is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + method: + description: |- + method is the fencing method used by this agent (e.g., "redfish", "ipmi", "fence_aws"). + This is extracted from the pacemaker resource agent type. + maxLength: 63 + minLength: 1 + type: string + name: + description: |- + name is the pacemaker resource name of the fencing agent (e.g., "master-0_redfish", "master-1_ipmi"). + The name typically includes the target node name and fencing method. + maxLength: 253 + minLength: 1 + type: string + required: + - method + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + name: + description: |- + name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase + RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with + an alphanumeric character, and be at most 253 characters in length. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must be a lowercase RFC 1123 subdomain consisting + of lowercase alphanumeric characters, '-' or '.', and must + start and end with an alphanumeric character + rule: '!format.dns1123Subdomain().validate(self).hasValue()' + resources: + description: |- + resources contains the status of pacemaker resources scheduled on this node. + Each resource entry includes the resource name and its health conditions. + For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. + Both resources are required to be present, so the array must contain at least 2 items. + Valid resource names are "Kubelet" and "Etcd". + Fencing agents are tracked separately in the fencingAgents field. + items: + description: |- + PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. + A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or + applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. + For Two Node OpenShift with Fencing, we track two resources per node: + - Kubelet (the Kubernetes node agent and a prerequisite for etcd) + - Etcd (the distributed key-value store) + + Fencing agents are tracked separately in the fencingAgents field because they are mapped to + their target node (the node they can fence), not the node where monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the resource's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the resource. + The "InService" condition tracks whether the resource is in service (not in maintenance mode). + The "Managed" condition tracks whether the resource is managed by pacemaker. + The "Enabled" condition tracks whether the resource is enabled. + The "Operational" condition tracks whether the resource is operational (not failed). + The "Active" condition tracks whether the resource is active (available to be used). + The "Started" condition tracks whether the resource is started. + The "Schedulable" condition tracks whether the resource is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + name: + description: |- + name is the name of the pacemaker resource. + Valid values are "Kubelet" and "Etcd". + The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + Fencing agents are tracked separately in the node's fencingAgents field. + enum: + - Kubelet + - Etcd + type: string + required: + - name + type: object + maxItems: 8 + minItems: 2 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resources must contain a resource named Kubelet + rule: self.exists(r, r.name == 'Kubelet') + - message: resources must contain a resource named Etcd + rule: self.exists(r, r.name == 'Etcd') + required: + - addresses + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + x-kubernetes-validations: + - message: lastUpdated cannot be removed once set + rule: '!has(oldSelf.lastUpdated) || has(self.lastUpdated)' + - message: lastUpdated cannot be set to an earlier timestamp + rule: '!has(oldSelf.lastUpdated) || !has(self.lastUpdated) || self.lastUpdated + >= oldSelf.lastUpdated' + required: + - metadata + type: object + x-kubernetes-validations: + - message: PacemakerCluster must be named 'cluster' + rule: self.metadata.name == 'cluster' + - message: status cannot be removed once set + rule: '!has(oldSelf.status) || has(self.status)' + served: true + storage: true + subresources: + status: {} diff --git a/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-TechPreviewNoUpgrade.crd.yaml b/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-TechPreviewNoUpgrade.crd.yaml new file mode 100644 index 00000000000..5f68b1aec56 --- /dev/null +++ b/etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters-TechPreviewNoUpgrade.crd.yaml @@ -0,0 +1,598 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/2544 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: TechPreviewNoUpgrade + name: pacemakerclusters.etcd.openshift.io +spec: + group: etcd.openshift.io + names: + kind: PacemakerCluster + listKind: PacemakerClusterList + plural: pacemakerclusters + singular: pacemakercluster + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. + PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is "cluster". This + resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments. + + Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + status: + description: |- + status contains the actual pacemaker cluster status information collected from the cluster. + The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. + In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. + This field is optional on creation - the status collector populates it immediately after creating + the resource via the status subresource. + properties: + conditions: + description: |- + conditions represent the observations of the pacemaker cluster's current state. + Known condition types are: "Healthy", "InService", "NodeCountAsExpected". + The "Healthy" condition is an aggregate that tracks the overall health of the cluster. + The "InService" condition tracks whether the cluster is in service (not in maintenance mode). + The "NodeCountAsExpected" condition tracks whether the expected number of nodes are present. + Each of these conditions is required, so the array must contain at least 3 items. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 3 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type NodeCountAsExpected + rule: self.exists(c, c.type == 'NodeCountAsExpected') + lastUpdated: + description: |- + lastUpdated is the timestamp when this status was last updated. This is useful for identifying + stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot + be removed and cannot be set to an earlier timestamp than the current value. + format: date-time + type: string + nodes: + description: |- + nodes provides detailed information about each node in the cluster including per-node resource health. + Each node entry includes the node's name, IP address, conditions, and resource status. + The list can contain up to 32 nodes (the upper limit imposed by pacemaker). + For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes + are currently reporting status. + items: + description: |- + PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including + the node's conditions and the health of critical resources running on that node. + properties: + addresses: + description: |- + addresses is a list of addresses reachable to the node. + This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. + Pacemaker allows multiple IP addresses for Corosync communication between nodes. + The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. + Each address must be a valid global unicast IPv4 or IPv6 address in canonical form + (e.g., "192.168.1.1" not "192.168.001.001", or "2001:db8::1" not "2001:0db8::1"). + This excludes special addresses like unspecified, loopback, link-local, and multicast. + items: + description: NodeAddress contains information for the node's + address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + conditions: + description: |- + conditions represent the observations of the node's current state. + Known condition types are: "Healthy", "Online", "InService", "Active", "Ready", "Clean", "Member", + "FencingAvailable", "FencingHealthy". + The "Healthy" condition is an aggregate that tracks the overall health of the node. + The "Online" condition tracks whether the node is online. + The "InService" condition tracks whether the node is in service (not in maintenance mode). + The "Active" condition tracks whether the node is active (not in standby mode). + The "Ready" condition tracks whether the node is ready (not in a pending state). + The "Clean" condition tracks whether the node is in a clean (status known) state. + The "Member" condition tracks whether the node is a member of the cluster. + The "FencingAvailable" condition tracks whether this node can be fenced by at least one healthy agent. + The "FencingHealthy" condition tracks whether all fencing agents for this node are healthy. + Each of these conditions is required, so the array must contain at least 9 items. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 9 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Ready + rule: self.exists(c, c.type == 'Ready') + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type Online + rule: self.exists(c, c.type == 'Online') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type Clean + rule: self.exists(c, c.type == 'Clean') + - message: conditions must contain a condition of type Member + rule: self.exists(c, c.type == 'Member') + - message: conditions must contain a condition of type FencingAvailable + rule: self.exists(c, c.type == 'FencingAvailable') + - message: conditions must contain a condition of type FencingHealthy + rule: self.exists(c, c.type == 'FencingHealthy') + fencingAgents: + description: |- + fencingAgents contains the status of fencing agents that can fence this node. + Unlike resources (which are scheduled to run on this node), fencing agents are mapped + to the node they can fence (their target), not the node where monitoring operations run. + Each fencing agent entry includes the agent name, fencing method, and health conditions. + A node is considered fence-capable if at least one fencing agent is healthy. + Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy. + items: + description: |- + PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. + Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. + Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they + can fence), not the node where their monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the fencing agent's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the fencing agent. + The "InService" condition tracks whether the fencing agent is in service (not in maintenance mode). + The "Managed" condition tracks whether the fencing agent is managed by pacemaker. + The "Enabled" condition tracks whether the fencing agent is enabled. + The "Operational" condition tracks whether the fencing agent is operational (not failed). + The "Active" condition tracks whether the fencing agent is active (available to be used). + The "Started" condition tracks whether the fencing agent is started. + The "Schedulable" condition tracks whether the fencing agent is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + method: + description: |- + method is the fencing method used by this agent (e.g., "redfish", "ipmi", "fence_aws"). + This is extracted from the pacemaker resource agent type. + maxLength: 63 + minLength: 1 + type: string + name: + description: |- + name is the pacemaker resource name of the fencing agent (e.g., "master-0_redfish", "master-1_ipmi"). + The name typically includes the target node name and fencing method. + maxLength: 253 + minLength: 1 + type: string + required: + - method + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + name: + description: |- + name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase + RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with + an alphanumeric character, and be at most 253 characters in length. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must be a lowercase RFC 1123 subdomain consisting + of lowercase alphanumeric characters, '-' or '.', and must + start and end with an alphanumeric character + rule: '!format.dns1123Subdomain().validate(self).hasValue()' + resources: + description: |- + resources contains the status of pacemaker resources scheduled on this node. + Each resource entry includes the resource name and its health conditions. + For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. + Both resources are required to be present, so the array must contain at least 2 items. + Valid resource names are "Kubelet" and "Etcd". + Fencing agents are tracked separately in the fencingAgents field. + items: + description: |- + PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. + A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or + applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. + For Two Node OpenShift with Fencing, we track two resources per node: + - Kubelet (the Kubernetes node agent and a prerequisite for etcd) + - Etcd (the distributed key-value store) + + Fencing agents are tracked separately in the fencingAgents field because they are mapped to + their target node (the node they can fence), not the node where monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the resource's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the resource. + The "InService" condition tracks whether the resource is in service (not in maintenance mode). + The "Managed" condition tracks whether the resource is managed by pacemaker. + The "Enabled" condition tracks whether the resource is enabled. + The "Operational" condition tracks whether the resource is operational (not failed). + The "Active" condition tracks whether the resource is active (available to be used). + The "Started" condition tracks whether the resource is started. + The "Schedulable" condition tracks whether the resource is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + name: + description: |- + name is the name of the pacemaker resource. + Valid values are "Kubelet" and "Etcd". + The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + Fencing agents are tracked separately in the node's fencingAgents field. + enum: + - Kubelet + - Etcd + type: string + required: + - name + type: object + maxItems: 8 + minItems: 2 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resources must contain a resource named Kubelet + rule: self.exists(r, r.name == 'Kubelet') + - message: resources must contain a resource named Etcd + rule: self.exists(r, r.name == 'Etcd') + required: + - addresses + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + x-kubernetes-validations: + - message: lastUpdated cannot be removed once set + rule: '!has(oldSelf.lastUpdated) || has(self.lastUpdated)' + - message: lastUpdated cannot be set to an earlier timestamp + rule: '!has(oldSelf.lastUpdated) || !has(self.lastUpdated) || self.lastUpdated + >= oldSelf.lastUpdated' + required: + - metadata + type: object + x-kubernetes-validations: + - message: PacemakerCluster must be named 'cluster' + rule: self.metadata.name == 'cluster' + - message: status cannot be removed once set + rule: '!has(oldSelf.status) || has(self.status)' + served: true + storage: true + subresources: + status: {} diff --git a/etcd/v1alpha1/zz_generated.crd-manifests/doc.go b/etcd/v1alpha1/zz_generated.crd-manifests/doc.go new file mode 100644 index 00000000000..fbefa7bcc2c --- /dev/null +++ b/etcd/v1alpha1/zz_generated.crd-manifests/doc.go @@ -0,0 +1 @@ +package etcd_v1alpha1_crdmanifests diff --git a/etcd/v1alpha1/zz_generated.deepcopy.go b/etcd/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..2820b2c4160 --- /dev/null +++ b/etcd/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,195 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by codegen. DO NOT EDIT. + +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PacemakerCluster) DeepCopyInto(out *PacemakerCluster) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(PacemakerClusterStatus) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PacemakerCluster. +func (in *PacemakerCluster) DeepCopy() *PacemakerCluster { + if in == nil { + return nil + } + out := new(PacemakerCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PacemakerCluster) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PacemakerClusterFencingAgentStatus) DeepCopyInto(out *PacemakerClusterFencingAgentStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PacemakerClusterFencingAgentStatus. +func (in *PacemakerClusterFencingAgentStatus) DeepCopy() *PacemakerClusterFencingAgentStatus { + if in == nil { + return nil + } + out := new(PacemakerClusterFencingAgentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PacemakerClusterList) DeepCopyInto(out *PacemakerClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PacemakerCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PacemakerClusterList. +func (in *PacemakerClusterList) DeepCopy() *PacemakerClusterList { + if in == nil { + return nil + } + out := new(PacemakerClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PacemakerClusterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PacemakerClusterNodeStatus) DeepCopyInto(out *PacemakerClusterNodeStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]corev1.NodeAddress, len(*in)) + copy(*out, *in) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]PacemakerClusterResourceStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FencingAgents != nil { + in, out := &in.FencingAgents, &out.FencingAgents + *out = make([]PacemakerClusterFencingAgentStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PacemakerClusterNodeStatus. +func (in *PacemakerClusterNodeStatus) DeepCopy() *PacemakerClusterNodeStatus { + if in == nil { + return nil + } + out := new(PacemakerClusterNodeStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PacemakerClusterResourceStatus) DeepCopyInto(out *PacemakerClusterResourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PacemakerClusterResourceStatus. +func (in *PacemakerClusterResourceStatus) DeepCopy() *PacemakerClusterResourceStatus { + if in == nil { + return nil + } + out := new(PacemakerClusterResourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PacemakerClusterStatus) DeepCopyInto(out *PacemakerClusterStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.LastUpdated.DeepCopyInto(&out.LastUpdated) + if in.Nodes != nil { + in, out := &in.Nodes, &out.Nodes + *out = make([]PacemakerClusterNodeStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PacemakerClusterStatus. +func (in *PacemakerClusterStatus) DeepCopy() *PacemakerClusterStatus { + if in == nil { + return nil + } + out := new(PacemakerClusterStatus) + in.DeepCopyInto(out) + return out +} diff --git a/etcd/v1alpha1/zz_generated.featuregated-crd-manifests.yaml b/etcd/v1alpha1/zz_generated.featuregated-crd-manifests.yaml new file mode 100644 index 00000000000..f5a64682ab2 --- /dev/null +++ b/etcd/v1alpha1/zz_generated.featuregated-crd-manifests.yaml @@ -0,0 +1,23 @@ +pacemakerclusters.etcd.openshift.io: + Annotations: {} + ApprovedPRNumber: https://github.com/openshift/api/pull/2544 + CRDName: pacemakerclusters.etcd.openshift.io + Capability: "" + Category: "" + FeatureGates: + - DualReplica + FilenameOperatorName: etcd + FilenameOperatorOrdering: "01" + FilenameRunLevel: "0000_25" + GroupName: etcd.openshift.io + HasStatus: true + KindName: PacemakerCluster + Labels: {} + PluralName: pacemakerclusters + PrinterColumns: [] + Scope: Cluster + ShortNames: null + TopLevelFeatureGates: + - DualReplica + Version: v1alpha1 + diff --git a/etcd/v1alpha1/zz_generated.featuregated-crd-manifests/pacemakerclusters.etcd.openshift.io/DualReplica.yaml b/etcd/v1alpha1/zz_generated.featuregated-crd-manifests/pacemakerclusters.etcd.openshift.io/DualReplica.yaml new file mode 100644 index 00000000000..d50ee8c991b --- /dev/null +++ b/etcd/v1alpha1/zz_generated.featuregated-crd-manifests/pacemakerclusters.etcd.openshift.io/DualReplica.yaml @@ -0,0 +1,598 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/2544 + api.openshift.io/filename-cvo-runlevel: "0000_25" + api.openshift.io/filename-operator: etcd + api.openshift.io/filename-ordering: "01" + feature-gate.release.openshift.io/DualReplica: "true" + name: pacemakerclusters.etcd.openshift.io +spec: + group: etcd.openshift.io + names: + kind: PacemakerCluster + listKind: PacemakerClusterList + plural: pacemakerclusters + singular: pacemakercluster + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. + PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is "cluster". This + resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments. + + Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + status: + description: |- + status contains the actual pacemaker cluster status information collected from the cluster. + The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. + In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. + This field is optional on creation - the status collector populates it immediately after creating + the resource via the status subresource. + properties: + conditions: + description: |- + conditions represent the observations of the pacemaker cluster's current state. + Known condition types are: "Healthy", "InService", "NodeCountAsExpected". + The "Healthy" condition is an aggregate that tracks the overall health of the cluster. + The "InService" condition tracks whether the cluster is in service (not in maintenance mode). + The "NodeCountAsExpected" condition tracks whether the expected number of nodes are present. + Each of these conditions is required, so the array must contain at least 3 items. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 3 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type NodeCountAsExpected + rule: self.exists(c, c.type == 'NodeCountAsExpected') + lastUpdated: + description: |- + lastUpdated is the timestamp when this status was last updated. This is useful for identifying + stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot + be removed and cannot be set to an earlier timestamp than the current value. + format: date-time + type: string + nodes: + description: |- + nodes provides detailed information about each node in the cluster including per-node resource health. + Each node entry includes the node's name, IP address, conditions, and resource status. + The list can contain up to 32 nodes (the upper limit imposed by pacemaker). + For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes + are currently reporting status. + items: + description: |- + PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including + the node's conditions and the health of critical resources running on that node. + properties: + addresses: + description: |- + addresses is a list of addresses reachable to the node. + This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. + Pacemaker allows multiple IP addresses for Corosync communication between nodes. + The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. + Each address must be a valid global unicast IPv4 or IPv6 address in canonical form + (e.g., "192.168.1.1" not "192.168.001.001", or "2001:db8::1" not "2001:0db8::1"). + This excludes special addresses like unspecified, loopback, link-local, and multicast. + items: + description: NodeAddress contains information for the node's + address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + conditions: + description: |- + conditions represent the observations of the node's current state. + Known condition types are: "Healthy", "Online", "InService", "Active", "Ready", "Clean", "Member", + "FencingAvailable", "FencingHealthy". + The "Healthy" condition is an aggregate that tracks the overall health of the node. + The "Online" condition tracks whether the node is online. + The "InService" condition tracks whether the node is in service (not in maintenance mode). + The "Active" condition tracks whether the node is active (not in standby mode). + The "Ready" condition tracks whether the node is ready (not in a pending state). + The "Clean" condition tracks whether the node is in a clean (status known) state. + The "Member" condition tracks whether the node is a member of the cluster. + The "FencingAvailable" condition tracks whether this node can be fenced by at least one healthy agent. + The "FencingHealthy" condition tracks whether all fencing agents for this node are healthy. + Each of these conditions is required, so the array must contain at least 9 items. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 9 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Ready + rule: self.exists(c, c.type == 'Ready') + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type Online + rule: self.exists(c, c.type == 'Online') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type Clean + rule: self.exists(c, c.type == 'Clean') + - message: conditions must contain a condition of type Member + rule: self.exists(c, c.type == 'Member') + - message: conditions must contain a condition of type FencingAvailable + rule: self.exists(c, c.type == 'FencingAvailable') + - message: conditions must contain a condition of type FencingHealthy + rule: self.exists(c, c.type == 'FencingHealthy') + fencingAgents: + description: |- + fencingAgents contains the status of fencing agents that can fence this node. + Unlike resources (which are scheduled to run on this node), fencing agents are mapped + to the node they can fence (their target), not the node where monitoring operations run. + Each fencing agent entry includes the agent name, fencing method, and health conditions. + A node is considered fence-capable if at least one fencing agent is healthy. + Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy. + items: + description: |- + PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. + Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. + Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they + can fence), not the node where their monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the fencing agent's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the fencing agent. + The "InService" condition tracks whether the fencing agent is in service (not in maintenance mode). + The "Managed" condition tracks whether the fencing agent is managed by pacemaker. + The "Enabled" condition tracks whether the fencing agent is enabled. + The "Operational" condition tracks whether the fencing agent is operational (not failed). + The "Active" condition tracks whether the fencing agent is active (available to be used). + The "Started" condition tracks whether the fencing agent is started. + The "Schedulable" condition tracks whether the fencing agent is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + method: + description: |- + method is the fencing method used by this agent (e.g., "redfish", "ipmi", "fence_aws"). + This is extracted from the pacemaker resource agent type. + maxLength: 63 + minLength: 1 + type: string + name: + description: |- + name is the pacemaker resource name of the fencing agent (e.g., "master-0_redfish", "master-1_ipmi"). + The name typically includes the target node name and fencing method. + maxLength: 253 + minLength: 1 + type: string + required: + - method + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + name: + description: |- + name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase + RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with + an alphanumeric character, and be at most 253 characters in length. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must be a lowercase RFC 1123 subdomain consisting + of lowercase alphanumeric characters, '-' or '.', and must + start and end with an alphanumeric character + rule: '!format.dns1123Subdomain().validate(self).hasValue()' + resources: + description: |- + resources contains the status of pacemaker resources scheduled on this node. + Each resource entry includes the resource name and its health conditions. + For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. + Both resources are required to be present, so the array must contain at least 2 items. + Valid resource names are "Kubelet" and "Etcd". + Fencing agents are tracked separately in the fencingAgents field. + items: + description: |- + PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. + A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or + applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. + For Two Node OpenShift with Fencing, we track two resources per node: + - Kubelet (the Kubernetes node agent and a prerequisite for etcd) + - Etcd (the distributed key-value store) + + Fencing agents are tracked separately in the fencingAgents field because they are mapped to + their target node (the node they can fence), not the node where monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the resource's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the resource. + The "InService" condition tracks whether the resource is in service (not in maintenance mode). + The "Managed" condition tracks whether the resource is managed by pacemaker. + The "Enabled" condition tracks whether the resource is enabled. + The "Operational" condition tracks whether the resource is operational (not failed). + The "Active" condition tracks whether the resource is active (available to be used). + The "Started" condition tracks whether the resource is started. + The "Schedulable" condition tracks whether the resource is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + name: + description: |- + name is the name of the pacemaker resource. + Valid values are "Kubelet" and "Etcd". + The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + Fencing agents are tracked separately in the node's fencingAgents field. + enum: + - Kubelet + - Etcd + type: string + required: + - name + type: object + maxItems: 8 + minItems: 2 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resources must contain a resource named Kubelet + rule: self.exists(r, r.name == 'Kubelet') + - message: resources must contain a resource named Etcd + rule: self.exists(r, r.name == 'Etcd') + required: + - addresses + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + x-kubernetes-validations: + - message: lastUpdated cannot be removed once set + rule: '!has(oldSelf.lastUpdated) || has(self.lastUpdated)' + - message: lastUpdated cannot be set to an earlier timestamp + rule: '!has(oldSelf.lastUpdated) || !has(self.lastUpdated) || self.lastUpdated + >= oldSelf.lastUpdated' + required: + - metadata + type: object + x-kubernetes-validations: + - message: PacemakerCluster must be named 'cluster' + rule: self.metadata.name == 'cluster' + - message: status cannot be removed once set + rule: '!has(oldSelf.status) || has(self.status)' + served: true + storage: true + subresources: + status: {} diff --git a/etcd/v1alpha1/zz_generated.swagger_doc_generated.go b/etcd/v1alpha1/zz_generated.swagger_doc_generated.go new file mode 100644 index 00000000000..00b30f5751c --- /dev/null +++ b/etcd/v1alpha1/zz_generated.swagger_doc_generated.go @@ -0,0 +1,79 @@ +package v1alpha1 + +// This file contains a collection of methods that can be used from go-restful to +// generate Swagger API documentation for its models. Please read this PR for more +// information on the implementation: https://github.com/emicklei/go-restful/pull/215 +// +// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if +// they are on one line! For multiple line or blocks that you want to ignore use ---. +// Any context after a --- is ignored. +// +// Those methods can be generated by using hack/update-swagger-docs.sh + +// AUTO-GENERATED FUNCTIONS START HERE +var map_PacemakerCluster = map[string]string{ + "": "PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is \"cluster\". This resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments.\n\nCompatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.", + "metadata": "metadata is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "status": "status contains the actual pacemaker cluster status information collected from the cluster. The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. This field is optional on creation - the status collector populates it immediately after creating the resource via the status subresource.", +} + +func (PacemakerCluster) SwaggerDoc() map[string]string { + return map_PacemakerCluster +} + +var map_PacemakerClusterFencingAgentStatus = map[string]string{ + "": "PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they can fence), not the node where their monitoring operations are scheduled.", + "conditions": "conditions represent the observations of the fencing agent's current state. Known condition types are: \"Healthy\", \"InService\", \"Managed\", \"Enabled\", \"Operational\", \"Active\", \"Started\", \"Schedulable\". The \"Healthy\" condition is an aggregate that tracks the overall health of the fencing agent. The \"InService\" condition tracks whether the fencing agent is in service (not in maintenance mode). The \"Managed\" condition tracks whether the fencing agent is managed by pacemaker. The \"Enabled\" condition tracks whether the fencing agent is enabled. The \"Operational\" condition tracks whether the fencing agent is operational (not failed). The \"Active\" condition tracks whether the fencing agent is active (available to be used). The \"Started\" condition tracks whether the fencing agent is started. The \"Schedulable\" condition tracks whether the fencing agent is schedulable (not blocked). Each of these conditions is required, so the array must contain at least 8 items.", + "name": "name is the pacemaker resource name of the fencing agent (e.g., \"master-0_redfish\", \"master-1_ipmi\"). The name typically includes the target node name and fencing method.", + "method": "method is the fencing method used by this agent (e.g., \"redfish\", \"ipmi\", \"fence_aws\"). This is extracted from the pacemaker resource agent type.", +} + +func (PacemakerClusterFencingAgentStatus) SwaggerDoc() map[string]string { + return map_PacemakerClusterFencingAgentStatus +} + +var map_PacemakerClusterList = map[string]string{ + "": "PacemakerClusterList contains a list of PacemakerCluster objects. PacemakerCluster is a cluster-scoped singleton resource; only one instance named \"cluster\" may exist. This list type exists only to satisfy Kubernetes API conventions.\n\nCompatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.", + "metadata": "metadata is the standard list's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "items": "items is a list of PacemakerCluster objects.", +} + +func (PacemakerClusterList) SwaggerDoc() map[string]string { + return map_PacemakerClusterList +} + +var map_PacemakerClusterNodeStatus = map[string]string{ + "": "PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including the node's conditions and the health of critical resources running on that node.", + "conditions": "conditions represent the observations of the node's current state. Known condition types are: \"Healthy\", \"Online\", \"InService\", \"Active\", \"Ready\", \"Clean\", \"Member\", \"FencingAvailable\", \"FencingHealthy\". The \"Healthy\" condition is an aggregate that tracks the overall health of the node. The \"Online\" condition tracks whether the node is online. The \"InService\" condition tracks whether the node is in service (not in maintenance mode). The \"Active\" condition tracks whether the node is active (not in standby mode). The \"Ready\" condition tracks whether the node is ready (not in a pending state). The \"Clean\" condition tracks whether the node is in a clean (status known) state. The \"Member\" condition tracks whether the node is a member of the cluster. The \"FencingAvailable\" condition tracks whether this node can be fenced by at least one healthy agent. The \"FencingHealthy\" condition tracks whether all fencing agents for this node are healthy. Each of these conditions is required, so the array must contain at least 9 items.", + "name": "name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with an alphanumeric character, and be at most 253 characters in length.", + "addresses": "addresses is a list of addresses reachable to the node. This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. Pacemaker allows multiple IP addresses for Corosync communication between nodes. The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. Each address must be a valid global unicast IPv4 or IPv6 address in canonical form (e.g., \"192.168.1.1\" not \"192.168.001.001\", or \"2001:db8::1\" not \"2001:0db8::1\"). This excludes special addresses like unspecified, loopback, link-local, and multicast.", + "resources": "resources contains the status of pacemaker resources scheduled on this node. Each resource entry includes the resource name and its health conditions. For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. Both resources are required to be present, so the array must contain at least 2 items. Valid resource names are \"Kubelet\" and \"Etcd\". Fencing agents are tracked separately in the fencingAgents field.", + "fencingAgents": "fencingAgents contains the status of fencing agents that can fence this node. Unlike resources (which are scheduled to run on this node), fencing agents are mapped to the node they can fence (their target), not the node where monitoring operations run. Each fencing agent entry includes the agent name, fencing method, and health conditions. A node is considered fence-capable if at least one fencing agent is healthy. Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy.", +} + +func (PacemakerClusterNodeStatus) SwaggerDoc() map[string]string { + return map_PacemakerClusterNodeStatus +} + +var map_PacemakerClusterResourceStatus = map[string]string{ + "": "PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. For Two Node OpenShift with Fencing, we track two resources per node:\n - Kubelet (the Kubernetes node agent and a prerequisite for etcd)\n - Etcd (the distributed key-value store)\n\nFencing agents are tracked separately in the fencingAgents field because they are mapped to their target node (the node they can fence), not the node where monitoring operations are scheduled.", + "conditions": "conditions represent the observations of the resource's current state. Known condition types are: \"Healthy\", \"InService\", \"Managed\", \"Enabled\", \"Operational\", \"Active\", \"Started\", \"Schedulable\". The \"Healthy\" condition is an aggregate that tracks the overall health of the resource. The \"InService\" condition tracks whether the resource is in service (not in maintenance mode). The \"Managed\" condition tracks whether the resource is managed by pacemaker. The \"Enabled\" condition tracks whether the resource is enabled. The \"Operational\" condition tracks whether the resource is operational (not failed). The \"Active\" condition tracks whether the resource is active (available to be used). The \"Started\" condition tracks whether the resource is started. The \"Schedulable\" condition tracks whether the resource is schedulable (not blocked). Each of these conditions is required, so the array must contain at least 8 items.", + "name": "name is the name of the pacemaker resource. Valid values are \"Kubelet\" and \"Etcd\". The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. Fencing agents are tracked separately in the node's fencingAgents field.", +} + +func (PacemakerClusterResourceStatus) SwaggerDoc() map[string]string { + return map_PacemakerClusterResourceStatus +} + +var map_PacemakerClusterStatus = map[string]string{ + "": "PacemakerClusterStatus contains the actual pacemaker cluster status information. As part of validating the status object, we need to ensure that the lastUpdated timestamp cannot be removed once set and cannot be set to an earlier timestamp than the current value.", + "conditions": "conditions represent the observations of the pacemaker cluster's current state. Known condition types are: \"Healthy\", \"InService\", \"NodeCountAsExpected\". The \"Healthy\" condition is an aggregate that tracks the overall health of the cluster. The \"InService\" condition tracks whether the cluster is in service (not in maintenance mode). The \"NodeCountAsExpected\" condition tracks whether the expected number of nodes are present. Each of these conditions is required, so the array must contain at least 3 items.", + "lastUpdated": "lastUpdated is the timestamp when this status was last updated. This is useful for identifying stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot be removed and cannot be set to an earlier timestamp than the current value.", + "nodes": "nodes provides detailed information about each node in the cluster including per-node resource health. Each node entry includes the node's name, IP address, conditions, and resource status. The list can contain up to 32 nodes (the upper limit imposed by pacemaker). For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes are currently reporting status.", +} + +func (PacemakerClusterStatus) SwaggerDoc() map[string]string { + return map_PacemakerClusterStatus +} + +// AUTO-GENERATED FUNCTIONS END HERE diff --git a/hack/update-payload-crds.sh b/hack/update-payload-crds.sh index 6edfd0496d7..072366143a8 100755 --- a/hack/update-payload-crds.sh +++ b/hack/update-payload-crds.sh @@ -29,6 +29,7 @@ crd_globs="\ operator/v1/zz_generated.crd-manifests/*_storage_01_storages*.crd.yaml operator/v1/zz_generated.crd-manifests/*_csi-driver_01_clustercsidrivers*.crd.yaml insights/v1/zz_generated.crd-manifests/0000_10_insights_01_datagathers*.crd.yaml + etcd/v1alpha1/zz_generated.crd-manifests/0000_25_etcd_01_pacemakerclusters*.crd.yaml " # To allow the crd_globs to be sourced in the verify script, diff --git a/install.go b/install.go index ea5f349708c..e4574e7c4f5 100644 --- a/install.go +++ b/install.go @@ -55,6 +55,7 @@ import ( "github.com/openshift/api/cloudnetwork" "github.com/openshift/api/config" "github.com/openshift/api/console" + "github.com/openshift/api/etcd" "github.com/openshift/api/helm" "github.com/openshift/api/image" "github.com/openshift/api/imageregistry" @@ -91,6 +92,7 @@ var ( build.Install, config.Install, console.Install, + etcd.Install, helm.Install, image.Install, imageregistry.Install, diff --git a/openapi/generated_openapi/zz_generated.openapi.go b/openapi/generated_openapi/zz_generated.openapi.go index 657afb352d4..8073e5ee3c2 100644 --- a/openapi/generated_openapi/zz_generated.openapi.go +++ b/openapi/generated_openapi/zz_generated.openapi.go @@ -544,6 +544,12 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/openshift/api/console/v1.ConsoleYAMLSampleSpec": schema_openshift_api_console_v1_ConsoleYAMLSampleSpec(ref), "github.com/openshift/api/console/v1.Link": schema_openshift_api_console_v1_Link(ref), "github.com/openshift/api/console/v1.NamespaceDashboardSpec": schema_openshift_api_console_v1_NamespaceDashboardSpec(ref), + "github.com/openshift/api/etcd/v1alpha1.PacemakerCluster": schema_openshift_api_etcd_v1alpha1_PacemakerCluster(ref), + "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterFencingAgentStatus": schema_openshift_api_etcd_v1alpha1_PacemakerClusterFencingAgentStatus(ref), + "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterList": schema_openshift_api_etcd_v1alpha1_PacemakerClusterList(ref), + "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterNodeStatus": schema_openshift_api_etcd_v1alpha1_PacemakerClusterNodeStatus(ref), + "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterResourceStatus": schema_openshift_api_etcd_v1alpha1_PacemakerClusterResourceStatus(ref), + "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterStatus": schema_openshift_api_etcd_v1alpha1_PacemakerClusterStatus(ref), "github.com/openshift/api/example/v1.CELUnion": schema_openshift_api_example_v1_CELUnion(ref), "github.com/openshift/api/example/v1.EvolvingUnion": schema_openshift_api_example_v1_EvolvingUnion(ref), "github.com/openshift/api/example/v1.FormatMarkerExamples": schema_openshift_api_example_v1_FormatMarkerExamples(ref), @@ -26383,6 +26389,370 @@ func schema_openshift_api_console_v1_NamespaceDashboardSpec(ref common.Reference } } +func schema_openshift_api_etcd_v1alpha1_PacemakerCluster(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is \"cluster\". This resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments.\n\nCompatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status contains the actual pacemaker cluster status information collected from the cluster. The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. This field is optional on creation - the status collector populates it immediately after creating the resource via the status subresource.", + Ref: ref("github.com/openshift/api/etcd/v1alpha1.PacemakerClusterStatus"), + }, + }, + }, + Required: []string{"metadata"}, + }, + }, + Dependencies: []string{ + "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_openshift_api_etcd_v1alpha1_PacemakerClusterFencingAgentStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they can fence), not the node where their monitoring operations are scheduled.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "type", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "conditions represent the observations of the fencing agent's current state. Known condition types are: \"Healthy\", \"InService\", \"Managed\", \"Enabled\", \"Operational\", \"Active\", \"Started\", \"Schedulable\". The \"Healthy\" condition is an aggregate that tracks the overall health of the fencing agent. The \"InService\" condition tracks whether the fencing agent is in service (not in maintenance mode). The \"Managed\" condition tracks whether the fencing agent is managed by pacemaker. The \"Enabled\" condition tracks whether the fencing agent is enabled. The \"Operational\" condition tracks whether the fencing agent is operational (not failed). The \"Active\" condition tracks whether the fencing agent is active (available to be used). The \"Started\" condition tracks whether the fencing agent is started. The \"Schedulable\" condition tracks whether the fencing agent is schedulable (not blocked). Each of these conditions is required, so the array must contain at least 8 items.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + }, + }, + }, + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the pacemaker resource name of the fencing agent (e.g., \"master-0_redfish\", \"master-1_ipmi\"). The name typically includes the target node name and fencing method.", + Type: []string{"string"}, + Format: "", + }, + }, + "method": { + SchemaProps: spec.SchemaProps{ + Description: "method is the fencing method used by this agent (e.g., \"redfish\", \"ipmi\", \"fence_aws\"). This is extracted from the pacemaker resource agent type.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "method"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, + } +} + +func schema_openshift_api_etcd_v1alpha1_PacemakerClusterList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PacemakerClusterList contains a list of PacemakerCluster objects. PacemakerCluster is a cluster-scoped singleton resource; only one instance named \"cluster\" may exist. This list type exists only to satisfy Kubernetes API conventions.\n\nCompatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata is the standard list's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "items is a list of PacemakerCluster objects.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/openshift/api/etcd/v1alpha1.PacemakerCluster"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/openshift/api/etcd/v1alpha1.PacemakerCluster", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_openshift_api_etcd_v1alpha1_PacemakerClusterNodeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including the node's conditions and the health of critical resources running on that node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "type", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "conditions represent the observations of the node's current state. Known condition types are: \"Healthy\", \"Online\", \"InService\", \"Active\", \"Ready\", \"Clean\", \"Member\", \"FencingAvailable\", \"FencingHealthy\". The \"Healthy\" condition is an aggregate that tracks the overall health of the node. The \"Online\" condition tracks whether the node is online. The \"InService\" condition tracks whether the node is in service (not in maintenance mode). The \"Active\" condition tracks whether the node is active (not in standby mode). The \"Ready\" condition tracks whether the node is ready (not in a pending state). The \"Clean\" condition tracks whether the node is in a clean (status known) state. The \"Member\" condition tracks whether the node is a member of the cluster. The \"FencingAvailable\" condition tracks whether this node can be fenced by at least one healthy agent. The \"FencingHealthy\" condition tracks whether all fencing agents for this node are healthy. Each of these conditions is required, so the array must contain at least 9 items.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + }, + }, + }, + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with an alphanumeric character, and be at most 253 characters in length.", + Type: []string{"string"}, + Format: "", + }, + }, + "addresses": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "addresses is a list of addresses reachable to the node. This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. Pacemaker allows multiple IP addresses for Corosync communication between nodes. The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. Each address must be a valid global unicast IPv4 or IPv6 address in canonical form (e.g., \"192.168.1.1\" not \"192.168.001.001\", or \"2001:db8::1\" not \"2001:0db8::1\"). This excludes special addresses like unspecified, loopback, link-local, and multicast.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.NodeAddress"), + }, + }, + }, + }, + }, + "resources": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "resources contains the status of pacemaker resources scheduled on this node. Each resource entry includes the resource name and its health conditions. For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. Both resources are required to be present, so the array must contain at least 2 items. Valid resource names are \"Kubelet\" and \"Etcd\". Fencing agents are tracked separately in the fencingAgents field.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/openshift/api/etcd/v1alpha1.PacemakerClusterResourceStatus"), + }, + }, + }, + }, + }, + "fencingAgents": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "fencingAgents contains the status of fencing agents that can fence this node. Unlike resources (which are scheduled to run on this node), fencing agents are mapped to the node they can fence (their target), not the node where monitoring operations run. Each fencing agent entry includes the agent name, fencing method, and health conditions. A node is considered fence-capable if at least one fencing agent is healthy. Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/openshift/api/etcd/v1alpha1.PacemakerClusterFencingAgentStatus"), + }, + }, + }, + }, + }, + }, + Required: []string{"name", "addresses"}, + }, + }, + Dependencies: []string{ + "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterFencingAgentStatus", "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterResourceStatus", "k8s.io/api/core/v1.NodeAddress", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, + } +} + +func schema_openshift_api_etcd_v1alpha1_PacemakerClusterResourceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. For Two Node OpenShift with Fencing, we track two resources per node:\n - Kubelet (the Kubernetes node agent and a prerequisite for etcd)\n - Etcd (the distributed key-value store)\n\nFencing agents are tracked separately in the fencingAgents field because they are mapped to their target node (the node they can fence), not the node where monitoring operations are scheduled.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "type", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "conditions represent the observations of the resource's current state. Known condition types are: \"Healthy\", \"InService\", \"Managed\", \"Enabled\", \"Operational\", \"Active\", \"Started\", \"Schedulable\". The \"Healthy\" condition is an aggregate that tracks the overall health of the resource. The \"InService\" condition tracks whether the resource is in service (not in maintenance mode). The \"Managed\" condition tracks whether the resource is managed by pacemaker. The \"Enabled\" condition tracks whether the resource is enabled. The \"Operational\" condition tracks whether the resource is operational (not failed). The \"Active\" condition tracks whether the resource is active (available to be used). The \"Started\" condition tracks whether the resource is started. The \"Schedulable\" condition tracks whether the resource is schedulable (not blocked). Each of these conditions is required, so the array must contain at least 8 items.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + }, + }, + }, + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the name of the pacemaker resource. Valid values are \"Kubelet\" and \"Etcd\". The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. Fencing agents are tracked separately in the node's fencingAgents field.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, + } +} + +func schema_openshift_api_etcd_v1alpha1_PacemakerClusterStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PacemakerClusterStatus contains the actual pacemaker cluster status information. As part of validating the status object, we need to ensure that the lastUpdated timestamp cannot be removed once set and cannot be set to an earlier timestamp than the current value.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "type", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "conditions represent the observations of the pacemaker cluster's current state. Known condition types are: \"Healthy\", \"InService\", \"NodeCountAsExpected\". The \"Healthy\" condition is an aggregate that tracks the overall health of the cluster. The \"InService\" condition tracks whether the cluster is in service (not in maintenance mode). The \"NodeCountAsExpected\" condition tracks whether the expected number of nodes are present. Each of these conditions is required, so the array must contain at least 3 items.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + }, + }, + }, + }, + }, + "lastUpdated": { + SchemaProps: spec.SchemaProps{ + Description: "lastUpdated is the timestamp when this status was last updated. This is useful for identifying stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot be removed and cannot be set to an earlier timestamp than the current value.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "nodes": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "nodes provides detailed information about each node in the cluster including per-node resource health. Each node entry includes the node's name, IP address, conditions, and resource status. The list can contain up to 32 nodes (the upper limit imposed by pacemaker). For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes are currently reporting status.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/openshift/api/etcd/v1alpha1.PacemakerClusterNodeStatus"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/openshift/api/etcd/v1alpha1.PacemakerClusterNodeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + func schema_openshift_api_example_v1_CELUnion(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/openapi/openapi.json b/openapi/openapi.json index 97afd57f431..d07a6eb46fa 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -14566,6 +14566,210 @@ } } }, + "com.github.openshift.api.etcd.v1alpha1.PacemakerCluster": { + "description": "PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is \"cluster\". This resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments.\n\nCompatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.", + "type": "object", + "required": [ + "metadata" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "description": "metadata is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "default": {}, + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "status": { + "description": "status contains the actual pacemaker cluster status information collected from the cluster. The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. This field is optional on creation - the status collector populates it immediately after creating the resource via the status subresource.", + "$ref": "#/definitions/com.github.openshift.api.etcd.v1alpha1.PacemakerClusterStatus" + } + } + }, + "com.github.openshift.api.etcd.v1alpha1.PacemakerClusterFencingAgentStatus": { + "description": "PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they can fence), not the node where their monitoring operations are scheduled.", + "type": "object", + "required": [ + "name", + "method" + ], + "properties": { + "conditions": { + "description": "conditions represent the observations of the fencing agent's current state. Known condition types are: \"Healthy\", \"InService\", \"Managed\", \"Enabled\", \"Operational\", \"Active\", \"Started\", \"Schedulable\". The \"Healthy\" condition is an aggregate that tracks the overall health of the fencing agent. The \"InService\" condition tracks whether the fencing agent is in service (not in maintenance mode). The \"Managed\" condition tracks whether the fencing agent is managed by pacemaker. The \"Enabled\" condition tracks whether the fencing agent is enabled. The \"Operational\" condition tracks whether the fencing agent is operational (not failed). The \"Active\" condition tracks whether the fencing agent is active (available to be used). The \"Started\" condition tracks whether the fencing agent is started. The \"Schedulable\" condition tracks whether the fencing agent is schedulable (not blocked). Each of these conditions is required, so the array must contain at least 8 items.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "method": { + "description": "method is the fencing method used by this agent (e.g., \"redfish\", \"ipmi\", \"fence_aws\"). This is extracted from the pacemaker resource agent type.", + "type": "string" + }, + "name": { + "description": "name is the pacemaker resource name of the fencing agent (e.g., \"master-0_redfish\", \"master-1_ipmi\"). The name typically includes the target node name and fencing method.", + "type": "string" + } + } + }, + "com.github.openshift.api.etcd.v1alpha1.PacemakerClusterList": { + "description": "PacemakerClusterList contains a list of PacemakerCluster objects. PacemakerCluster is a cluster-scoped singleton resource; only one instance named \"cluster\" may exist. This list type exists only to satisfy Kubernetes API conventions.\n\nCompatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of PacemakerCluster objects.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/com.github.openshift.api.etcd.v1alpha1.PacemakerCluster" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "description": "metadata is the standard list's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "default": {}, + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + } + }, + "com.github.openshift.api.etcd.v1alpha1.PacemakerClusterNodeStatus": { + "description": "PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including the node's conditions and the health of critical resources running on that node.", + "type": "object", + "required": [ + "name", + "addresses" + ], + "properties": { + "addresses": { + "description": "addresses is a list of addresses reachable to the node. This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. Pacemaker allows multiple IP addresses for Corosync communication between nodes. The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. Each address must be a valid global unicast IPv4 or IPv6 address in canonical form (e.g., \"192.168.1.1\" not \"192.168.001.001\", or \"2001:db8::1\" not \"2001:0db8::1\"). This excludes special addresses like unspecified, loopback, link-local, and multicast.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/io.k8s.api.core.v1.NodeAddress" + }, + "x-kubernetes-list-type": "atomic" + }, + "conditions": { + "description": "conditions represent the observations of the node's current state. Known condition types are: \"Healthy\", \"Online\", \"InService\", \"Active\", \"Ready\", \"Clean\", \"Member\". The \"Healthy\" condition is an aggregate that tracks the overall health of the node. The \"Online\" condition tracks whether the node is online. The \"InService\" condition tracks whether the node is in service (not in maintenance mode). The \"Active\" condition tracks whether the node is active (not in standby mode). The \"Ready\" condition tracks whether the node is ready (not in a pending state). The \"Clean\" condition tracks whether the node is in a clean (status known) state. The \"Member\" condition tracks whether the node is a member of the cluster. Each of these conditions is required, so the array must contain at least 7 items.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "fencingAgents": { + "description": "fencingAgents contains the status of fencing agents that can fence this node. Unlike resources (which are scheduled to run on this node), fencing agents are mapped to the node they can fence (their target), not the node where monitoring operations run. Each fencing agent entry includes the agent name, fencing method, and health conditions. A node is considered fence-capable if at least one fencing agent is healthy. Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/com.github.openshift.api.etcd.v1alpha1.PacemakerClusterFencingAgentStatus" + }, + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + }, + "name": { + "description": "name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with an alphanumeric character, and be at most 253 characters in length.", + "type": "string" + }, + "resources": { + "description": "resources contains the status of pacemaker resources scheduled on this node. Each resource entry includes the resource name and its health conditions. For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. Both resources are required to be present, so the array must contain at least 2 items. Valid resource names are \"Kubelet\" and \"Etcd\". Fencing agents are tracked separately in the fencingAgents field.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/com.github.openshift.api.etcd.v1alpha1.PacemakerClusterResourceStatus" + }, + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + } + } + }, + "com.github.openshift.api.etcd.v1alpha1.PacemakerClusterResourceStatus": { + "description": "PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. For Two Node OpenShift with Fencing, we track two resources per node:\n - Kubelet (the Kubernetes node agent and a prerequisite for etcd)\n - Etcd (the distributed key-value store)\n\nFencing agents are tracked separately in the fencingAgents field because they are mapped to their target node (the node they can fence), not the node where monitoring operations are scheduled.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "conditions": { + "description": "conditions represent the observations of the resource's current state. Known condition types are: \"Healthy\", \"InService\", \"Managed\", \"Enabled\", \"Operational\", \"Active\", \"Started\", \"Schedulable\". The \"Healthy\" condition is an aggregate that tracks the overall health of the resource. The \"InService\" condition tracks whether the resource is in service (not in maintenance mode). The \"Managed\" condition tracks whether the resource is managed by pacemaker. The \"Enabled\" condition tracks whether the resource is enabled. The \"Operational\" condition tracks whether the resource is operational (not failed). The \"Active\" condition tracks whether the resource is active (available to be used). The \"Started\" condition tracks whether the resource is started. The \"Schedulable\" condition tracks whether the resource is schedulable (not blocked). Each of these conditions is required, so the array must contain at least 8 items.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "name": { + "description": "name is the name of the pacemaker resource. Valid values are \"Kubelet\" and \"Etcd\". The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. Fencing agents are tracked separately in the node's fencingAgents field.", + "type": "string" + } + } + }, + "com.github.openshift.api.etcd.v1alpha1.PacemakerClusterStatus": { + "description": "PacemakerClusterStatus contains the actual pacemaker cluster status information. As part of validating the status object, we need to ensure that the lastUpdated timestamp cannot be removed once set and cannot be set to an earlier timestamp than the current value.", + "type": "object", + "properties": { + "conditions": { + "description": "conditions represent the observations of the pacemaker cluster's current state. Known condition types are: \"Healthy\", \"InService\", \"NodeCountAsExpected\". The \"Healthy\" condition is an aggregate that tracks the overall health of the cluster. The \"InService\" condition tracks whether the cluster is in service (not in maintenance mode). The \"NodeCountAsExpected\" condition tracks whether the expected number of nodes are present. Each of these conditions is required, so the array must contain at least 3 items.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "lastUpdated": { + "description": "lastUpdated is the timestamp when this status was last updated. This is useful for identifying stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot be removed and cannot be set to an earlier timestamp than the current value.", + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + }, + "nodes": { + "description": "nodes provides detailed information about each node in the cluster including per-node resource health. Each node entry includes the node's name, IP address, conditions, and resource status. The list can contain up to 32 nodes (the upper limit imposed by pacemaker). For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes are currently reporting status.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/com.github.openshift.api.etcd.v1alpha1.PacemakerClusterNodeStatus" + }, + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + } + } + }, "com.github.openshift.api.example.v1.CELUnion": { "description": "CELUnion demonstrates how to use a discriminated union and how to validate it using CEL.", "type": "object", diff --git a/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-CustomNoUpgrade.crd.yaml b/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-CustomNoUpgrade.crd.yaml new file mode 100644 index 00000000000..34782af1251 --- /dev/null +++ b/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-CustomNoUpgrade.crd.yaml @@ -0,0 +1,598 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/2544 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: CustomNoUpgrade + name: pacemakerclusters.etcd.openshift.io +spec: + group: etcd.openshift.io + names: + kind: PacemakerCluster + listKind: PacemakerClusterList + plural: pacemakerclusters + singular: pacemakercluster + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. + PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is "cluster". This + resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments. + + Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + status: + description: |- + status contains the actual pacemaker cluster status information collected from the cluster. + The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. + In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. + This field is optional on creation - the status collector populates it immediately after creating + the resource via the status subresource. + properties: + conditions: + description: |- + conditions represent the observations of the pacemaker cluster's current state. + Known condition types are: "Healthy", "InService", "NodeCountAsExpected". + The "Healthy" condition is an aggregate that tracks the overall health of the cluster. + The "InService" condition tracks whether the cluster is in service (not in maintenance mode). + The "NodeCountAsExpected" condition tracks whether the expected number of nodes are present. + Each of these conditions is required, so the array must contain at least 3 items. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 3 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type NodeCountAsExpected + rule: self.exists(c, c.type == 'NodeCountAsExpected') + lastUpdated: + description: |- + lastUpdated is the timestamp when this status was last updated. This is useful for identifying + stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot + be removed and cannot be set to an earlier timestamp than the current value. + format: date-time + type: string + nodes: + description: |- + nodes provides detailed information about each node in the cluster including per-node resource health. + Each node entry includes the node's name, IP address, conditions, and resource status. + The list can contain up to 32 nodes (the upper limit imposed by pacemaker). + For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes + are currently reporting status. + items: + description: |- + PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including + the node's conditions and the health of critical resources running on that node. + properties: + addresses: + description: |- + addresses is a list of addresses reachable to the node. + This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. + Pacemaker allows multiple IP addresses for Corosync communication between nodes. + The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. + Each address must be a valid global unicast IPv4 or IPv6 address in canonical form + (e.g., "192.168.1.1" not "192.168.001.001", or "2001:db8::1" not "2001:0db8::1"). + This excludes special addresses like unspecified, loopback, link-local, and multicast. + items: + description: NodeAddress contains information for the node's + address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + conditions: + description: |- + conditions represent the observations of the node's current state. + Known condition types are: "Healthy", "Online", "InService", "Active", "Ready", "Clean", "Member", + "FencingAvailable", "FencingHealthy". + The "Healthy" condition is an aggregate that tracks the overall health of the node. + The "Online" condition tracks whether the node is online. + The "InService" condition tracks whether the node is in service (not in maintenance mode). + The "Active" condition tracks whether the node is active (not in standby mode). + The "Ready" condition tracks whether the node is ready (not in a pending state). + The "Clean" condition tracks whether the node is in a clean (status known) state. + The "Member" condition tracks whether the node is a member of the cluster. + The "FencingAvailable" condition tracks whether this node can be fenced by at least one healthy agent. + The "FencingHealthy" condition tracks whether all fencing agents for this node are healthy. + Each of these conditions is required, so the array must contain at least 9 items. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 9 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type Online + rule: self.exists(c, c.type == 'Online') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type Ready + rule: self.exists(c, c.type == 'Ready') + - message: conditions must contain a condition of type Clean + rule: self.exists(c, c.type == 'Clean') + - message: conditions must contain a condition of type Member + rule: self.exists(c, c.type == 'Member') + - message: conditions must contain a condition of type FencingAvailable + rule: self.exists(c, c.type == 'FencingAvailable') + - message: conditions must contain a condition of type FencingHealthy + rule: self.exists(c, c.type == 'FencingHealthy') + fencingAgents: + description: |- + fencingAgents contains the status of fencing agents that can fence this node. + Unlike resources (which are scheduled to run on this node), fencing agents are mapped + to the node they can fence (their target), not the node where monitoring operations run. + Each fencing agent entry includes the agent name, fencing method, and health conditions. + A node is considered fence-capable if at least one fencing agent is healthy. + Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy. + items: + description: |- + PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. + Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. + Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they + can fence), not the node where their monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the fencing agent's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the fencing agent. + The "InService" condition tracks whether the fencing agent is in service (not in maintenance mode). + The "Managed" condition tracks whether the fencing agent is managed by pacemaker. + The "Enabled" condition tracks whether the fencing agent is enabled. + The "Operational" condition tracks whether the fencing agent is operational (not failed). + The "Active" condition tracks whether the fencing agent is active (available to be used). + The "Started" condition tracks whether the fencing agent is started. + The "Schedulable" condition tracks whether the fencing agent is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + method: + description: |- + method is the fencing method used by this agent (e.g., "redfish", "ipmi", "fence_aws"). + This is extracted from the pacemaker resource agent type. + maxLength: 63 + minLength: 1 + type: string + name: + description: |- + name is the pacemaker resource name of the fencing agent (e.g., "master-0_redfish", "master-1_ipmi"). + The name typically includes the target node name and fencing method. + maxLength: 253 + minLength: 1 + type: string + required: + - method + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + name: + description: |- + name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase + RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with + an alphanumeric character, and be at most 253 characters in length. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must be a lowercase RFC 1123 subdomain consisting + of lowercase alphanumeric characters, '-' or '.', and must + start and end with an alphanumeric character + rule: '!format.dns1123Subdomain().validate(self).hasValue()' + resources: + description: |- + resources contains the status of pacemaker resources scheduled on this node. + Each resource entry includes the resource name and its health conditions. + For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. + Both resources are required to be present, so the array must contain at least 2 items. + Valid resource names are "Kubelet" and "Etcd". + Fencing agents are tracked separately in the fencingAgents field. + items: + description: |- + PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. + A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or + applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. + For Two Node OpenShift with Fencing, we track two resources per node: + - Kubelet (the Kubernetes node agent and a prerequisite for etcd) + - Etcd (the distributed key-value store) + + Fencing agents are tracked separately in the fencingAgents field because they are mapped to + their target node (the node they can fence), not the node where monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the resource's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the resource. + The "InService" condition tracks whether the resource is in service (not in maintenance mode). + The "Managed" condition tracks whether the resource is managed by pacemaker. + The "Enabled" condition tracks whether the resource is enabled. + The "Operational" condition tracks whether the resource is operational (not failed). + The "Active" condition tracks whether the resource is active (available to be used). + The "Started" condition tracks whether the resource is started. + The "Schedulable" condition tracks whether the resource is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + name: + description: |- + name is the name of the pacemaker resource. + Valid values are "Kubelet" and "Etcd". + The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + Fencing agents are tracked separately in the node's fencingAgents field. + enum: + - Kubelet + - Etcd + type: string + required: + - name + type: object + maxItems: 8 + minItems: 2 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resources must contain a resource named Kubelet + rule: self.exists(r, r.name == 'Kubelet') + - message: resources must contain a resource named Etcd + rule: self.exists(r, r.name == 'Etcd') + required: + - addresses + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + x-kubernetes-validations: + - message: lastUpdated cannot be removed once set + rule: '!has(oldSelf.lastUpdated) || has(self.lastUpdated)' + - message: lastUpdated cannot be set to an earlier timestamp + rule: '!has(oldSelf.lastUpdated) || !has(self.lastUpdated) || self.lastUpdated + >= oldSelf.lastUpdated' + required: + - metadata + type: object + x-kubernetes-validations: + - message: PacemakerCluster must be named 'cluster' + rule: self.metadata.name == 'cluster' + - message: status cannot be removed once set + rule: '!has(oldSelf.status) || has(self.status)' + served: true + storage: true + subresources: + status: {} diff --git a/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-DevPreviewNoUpgrade.crd.yaml b/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-DevPreviewNoUpgrade.crd.yaml new file mode 100644 index 00000000000..dd2e125ed58 --- /dev/null +++ b/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-DevPreviewNoUpgrade.crd.yaml @@ -0,0 +1,598 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/2544 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: DevPreviewNoUpgrade + name: pacemakerclusters.etcd.openshift.io +spec: + group: etcd.openshift.io + names: + kind: PacemakerCluster + listKind: PacemakerClusterList + plural: pacemakerclusters + singular: pacemakercluster + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. + PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is "cluster". This + resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments. + + Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + status: + description: |- + status contains the actual pacemaker cluster status information collected from the cluster. + The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. + In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. + This field is optional on creation - the status collector populates it immediately after creating + the resource via the status subresource. + properties: + conditions: + description: |- + conditions represent the observations of the pacemaker cluster's current state. + Known condition types are: "Healthy", "InService", "NodeCountAsExpected". + The "Healthy" condition is an aggregate that tracks the overall health of the cluster. + The "InService" condition tracks whether the cluster is in service (not in maintenance mode). + The "NodeCountAsExpected" condition tracks whether the expected number of nodes are present. + Each of these conditions is required, so the array must contain at least 3 items. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 3 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type NodeCountAsExpected + rule: self.exists(c, c.type == 'NodeCountAsExpected') + lastUpdated: + description: |- + lastUpdated is the timestamp when this status was last updated. This is useful for identifying + stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot + be removed and cannot be set to an earlier timestamp than the current value. + format: date-time + type: string + nodes: + description: |- + nodes provides detailed information about each node in the cluster including per-node resource health. + Each node entry includes the node's name, IP address, conditions, and resource status. + The list can contain up to 32 nodes (the upper limit imposed by pacemaker). + For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes + are currently reporting status. + items: + description: |- + PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including + the node's conditions and the health of critical resources running on that node. + properties: + addresses: + description: |- + addresses is a list of addresses reachable to the node. + This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. + Pacemaker allows multiple IP addresses for Corosync communication between nodes. + The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. + Each address must be a valid global unicast IPv4 or IPv6 address in canonical form + (e.g., "192.168.1.1" not "192.168.001.001", or "2001:db8::1" not "2001:0db8::1"). + This excludes special addresses like unspecified, loopback, link-local, and multicast. + items: + description: NodeAddress contains information for the node's + address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + conditions: + description: |- + conditions represent the observations of the node's current state. + Known condition types are: "Healthy", "Online", "InService", "Active", "Ready", "Clean", "Member", + "FencingAvailable", "FencingHealthy". + The "Healthy" condition is an aggregate that tracks the overall health of the node. + The "Online" condition tracks whether the node is online. + The "InService" condition tracks whether the node is in service (not in maintenance mode). + The "Active" condition tracks whether the node is active (not in standby mode). + The "Ready" condition tracks whether the node is ready (not in a pending state). + The "Clean" condition tracks whether the node is in a clean (status known) state. + The "Member" condition tracks whether the node is a member of the cluster. + The "FencingAvailable" condition tracks whether this node can be fenced by at least one healthy agent. + The "FencingHealthy" condition tracks whether all fencing agents for this node are healthy. + Each of these conditions is required, so the array must contain at least 9 items. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 9 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type Online + rule: self.exists(c, c.type == 'Online') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type Ready + rule: self.exists(c, c.type == 'Ready') + - message: conditions must contain a condition of type Clean + rule: self.exists(c, c.type == 'Clean') + - message: conditions must contain a condition of type Member + rule: self.exists(c, c.type == 'Member') + - message: conditions must contain a condition of type FencingAvailable + rule: self.exists(c, c.type == 'FencingAvailable') + - message: conditions must contain a condition of type FencingHealthy + rule: self.exists(c, c.type == 'FencingHealthy') + fencingAgents: + description: |- + fencingAgents contains the status of fencing agents that can fence this node. + Unlike resources (which are scheduled to run on this node), fencing agents are mapped + to the node they can fence (their target), not the node where monitoring operations run. + Each fencing agent entry includes the agent name, fencing method, and health conditions. + A node is considered fence-capable if at least one fencing agent is healthy. + Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy. + items: + description: |- + PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. + Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. + Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they + can fence), not the node where their monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the fencing agent's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the fencing agent. + The "InService" condition tracks whether the fencing agent is in service (not in maintenance mode). + The "Managed" condition tracks whether the fencing agent is managed by pacemaker. + The "Enabled" condition tracks whether the fencing agent is enabled. + The "Operational" condition tracks whether the fencing agent is operational (not failed). + The "Active" condition tracks whether the fencing agent is active (available to be used). + The "Started" condition tracks whether the fencing agent is started. + The "Schedulable" condition tracks whether the fencing agent is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + method: + description: |- + method is the fencing method used by this agent (e.g., "redfish", "ipmi", "fence_aws"). + This is extracted from the pacemaker resource agent type. + maxLength: 63 + minLength: 1 + type: string + name: + description: |- + name is the pacemaker resource name of the fencing agent (e.g., "master-0_redfish", "master-1_ipmi"). + The name typically includes the target node name and fencing method. + maxLength: 253 + minLength: 1 + type: string + required: + - method + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + name: + description: |- + name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase + RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with + an alphanumeric character, and be at most 253 characters in length. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must be a lowercase RFC 1123 subdomain consisting + of lowercase alphanumeric characters, '-' or '.', and must + start and end with an alphanumeric character + rule: '!format.dns1123Subdomain().validate(self).hasValue()' + resources: + description: |- + resources contains the status of pacemaker resources scheduled on this node. + Each resource entry includes the resource name and its health conditions. + For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. + Both resources are required to be present, so the array must contain at least 2 items. + Valid resource names are "Kubelet" and "Etcd". + Fencing agents are tracked separately in the fencingAgents field. + items: + description: |- + PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. + A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or + applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. + For Two Node OpenShift with Fencing, we track two resources per node: + - Kubelet (the Kubernetes node agent and a prerequisite for etcd) + - Etcd (the distributed key-value store) + + Fencing agents are tracked separately in the fencingAgents field because they are mapped to + their target node (the node they can fence), not the node where monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the resource's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the resource. + The "InService" condition tracks whether the resource is in service (not in maintenance mode). + The "Managed" condition tracks whether the resource is managed by pacemaker. + The "Enabled" condition tracks whether the resource is enabled. + The "Operational" condition tracks whether the resource is operational (not failed). + The "Active" condition tracks whether the resource is active (available to be used). + The "Started" condition tracks whether the resource is started. + The "Schedulable" condition tracks whether the resource is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + name: + description: |- + name is the name of the pacemaker resource. + Valid values are "Kubelet" and "Etcd". + The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + Fencing agents are tracked separately in the node's fencingAgents field. + enum: + - Kubelet + - Etcd + type: string + required: + - name + type: object + maxItems: 8 + minItems: 2 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resources must contain a resource named Kubelet + rule: self.exists(r, r.name == 'Kubelet') + - message: resources must contain a resource named Etcd + rule: self.exists(r, r.name == 'Etcd') + required: + - addresses + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + x-kubernetes-validations: + - message: lastUpdated cannot be removed once set + rule: '!has(oldSelf.lastUpdated) || has(self.lastUpdated)' + - message: lastUpdated cannot be set to an earlier timestamp + rule: '!has(oldSelf.lastUpdated) || !has(self.lastUpdated) || self.lastUpdated + >= oldSelf.lastUpdated' + required: + - metadata + type: object + x-kubernetes-validations: + - message: PacemakerCluster must be named 'cluster' + rule: self.metadata.name == 'cluster' + - message: status cannot be removed once set + rule: '!has(oldSelf.status) || has(self.status)' + served: true + storage: true + subresources: + status: {} diff --git a/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-TechPreviewNoUpgrade.crd.yaml b/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-TechPreviewNoUpgrade.crd.yaml new file mode 100644 index 00000000000..ab14de7e226 --- /dev/null +++ b/payload-manifests/crds/0000_25_etcd_01_pacemakerclusters-TechPreviewNoUpgrade.crd.yaml @@ -0,0 +1,598 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/2544 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: TechPreviewNoUpgrade + name: pacemakerclusters.etcd.openshift.io +spec: + group: etcd.openshift.io + names: + kind: PacemakerCluster + listKind: PacemakerClusterList + plural: pacemakerclusters + singular: pacemakercluster + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + PacemakerCluster represents the current state of the pacemaker cluster as reported by the pcs status command. + PacemakerCluster is a cluster-scoped singleton resource. The name of this instance is "cluster". This + resource provides a view into the health and status of a pacemaker-managed cluster in Two Node OpenShift with Fencing deployments. + + Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + status: + description: |- + status contains the actual pacemaker cluster status information collected from the cluster. + The goal of this status is to be able to quickly identify if pacemaker is in a healthy state. + In Two Node OpenShift with Fencing, a healthy pacemaker cluster has 2 nodes, both of which have healthy kubelet, etcd, and fencing resources. + This field is optional on creation - the status collector populates it immediately after creating + the resource via the status subresource. + properties: + conditions: + description: |- + conditions represent the observations of the pacemaker cluster's current state. + Known condition types are: "Healthy", "InService", "NodeCountAsExpected". + The "Healthy" condition is an aggregate that tracks the overall health of the cluster. + The "InService" condition tracks whether the cluster is in service (not in maintenance mode). + The "NodeCountAsExpected" condition tracks whether the expected number of nodes are present. + Each of these conditions is required, so the array must contain at least 3 items. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 3 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type NodeCountAsExpected + rule: self.exists(c, c.type == 'NodeCountAsExpected') + lastUpdated: + description: |- + lastUpdated is the timestamp when this status was last updated. This is useful for identifying + stale status reports. It must be a valid timestamp in RFC3339 format. Once set, this field cannot + be removed and cannot be set to an earlier timestamp than the current value. + format: date-time + type: string + nodes: + description: |- + nodes provides detailed information about each node in the cluster including per-node resource health. + Each node entry includes the node's name, IP address, conditions, and resource status. + The list can contain up to 32 nodes (the upper limit imposed by pacemaker). + For Two Node OpenShift with Fencing, exactly 2 nodes are expected in a healthy cluster. An empty list indicates no nodes + are currently reporting status. + items: + description: |- + PacemakerClusterNodeStatus represents the status of a single node in the pacemaker cluster including + the node's conditions and the health of critical resources running on that node. + properties: + addresses: + description: |- + addresses is a list of addresses reachable to the node. + This mirrors the Kubernetes Node addresses field for consistency with the Kubernetes API. + Pacemaker allows multiple IP addresses for Corosync communication between nodes. + The first InternalIP address in this list is used for IP-based peer URLs for etcd membership. + Each address must be a valid global unicast IPv4 or IPv6 address in canonical form + (e.g., "192.168.1.1" not "192.168.001.001", or "2001:db8::1" not "2001:0db8::1"). + This excludes special addresses like unspecified, loopback, link-local, and multicast. + items: + description: NodeAddress contains information for the node's + address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + conditions: + description: |- + conditions represent the observations of the node's current state. + Known condition types are: "Healthy", "Online", "InService", "Active", "Ready", "Clean", "Member", + "FencingAvailable", "FencingHealthy". + The "Healthy" condition is an aggregate that tracks the overall health of the node. + The "Online" condition tracks whether the node is online. + The "InService" condition tracks whether the node is in service (not in maintenance mode). + The "Active" condition tracks whether the node is active (not in standby mode). + The "Ready" condition tracks whether the node is ready (not in a pending state). + The "Clean" condition tracks whether the node is in a clean (status known) state. + The "Member" condition tracks whether the node is a member of the cluster. + The "FencingAvailable" condition tracks whether this node can be fenced by at least one healthy agent. + The "FencingHealthy" condition tracks whether all fencing agents for this node are healthy. + Each of these conditions is required, so the array must contain at least 9 items. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 9 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type Online + rule: self.exists(c, c.type == 'Online') + - message: conditions must contain a condition of type InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type Ready + rule: self.exists(c, c.type == 'Ready') + - message: conditions must contain a condition of type Clean + rule: self.exists(c, c.type == 'Clean') + - message: conditions must contain a condition of type Member + rule: self.exists(c, c.type == 'Member') + - message: conditions must contain a condition of type FencingAvailable + rule: self.exists(c, c.type == 'FencingAvailable') + - message: conditions must contain a condition of type FencingHealthy + rule: self.exists(c, c.type == 'FencingHealthy') + fencingAgents: + description: |- + fencingAgents contains the status of fencing agents that can fence this node. + Unlike resources (which are scheduled to run on this node), fencing agents are mapped + to the node they can fence (their target), not the node where monitoring operations run. + Each fencing agent entry includes the agent name, fencing method, and health conditions. + A node is considered fence-capable if at least one fencing agent is healthy. + Expected to have 1 fencing agent per node, but up to 8 are supported for redundancy. + items: + description: |- + PacemakerClusterFencingAgentStatus represents the status of a fencing agent that can fence a node. + Fencing agents are STONITH (Shoot The Other Node In The Head) devices used to isolate failed nodes. + Unlike regular pacemaker resources, fencing agents are mapped to their target node (the node they + can fence), not the node where their monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the fencing agent's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the fencing agent. + The "InService" condition tracks whether the fencing agent is in service (not in maintenance mode). + The "Managed" condition tracks whether the fencing agent is managed by pacemaker. + The "Enabled" condition tracks whether the fencing agent is enabled. + The "Operational" condition tracks whether the fencing agent is operational (not failed). + The "Active" condition tracks whether the fencing agent is active (available to be used). + The "Started" condition tracks whether the fencing agent is started. + The "Schedulable" condition tracks whether the fencing agent is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + method: + description: |- + method is the fencing method used by this agent (e.g., "redfish", "ipmi", "fence_aws"). + This is extracted from the pacemaker resource agent type. + maxLength: 63 + minLength: 1 + type: string + name: + description: |- + name is the pacemaker resource name of the fencing agent (e.g., "master-0_redfish", "master-1_ipmi"). + The name typically includes the target node name and fencing method. + maxLength: 253 + minLength: 1 + type: string + required: + - method + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + name: + description: |- + name is the name of the node. This is expected to match the Kubernetes node's name, which must be a lowercase + RFC 1123 subdomain consisting of lowercase alphanumeric characters, '-' or '.', starting and ending with + an alphanumeric character, and be at most 253 characters in length. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must be a lowercase RFC 1123 subdomain consisting + of lowercase alphanumeric characters, '-' or '.', and must + start and end with an alphanumeric character + rule: '!format.dns1123Subdomain().validate(self).hasValue()' + resources: + description: |- + resources contains the status of pacemaker resources scheduled on this node. + Each resource entry includes the resource name and its health conditions. + For Two Node OpenShift with Fencing, we track Kubelet and Etcd resources per node. + Both resources are required to be present, so the array must contain at least 2 items. + Valid resource names are "Kubelet" and "Etcd". + Fencing agents are tracked separately in the fencingAgents field. + items: + description: |- + PacemakerClusterResourceStatus represents the status of a pacemaker resource scheduled on a node. + A pacemaker resource is a unit of work managed by pacemaker. In pacemaker terminology, resources are services or + applications that pacemaker monitors, starts, stops, and moves between nodes to maintain high availability. + For Two Node OpenShift with Fencing, we track two resources per node: + - Kubelet (the Kubernetes node agent and a prerequisite for etcd) + - Etcd (the distributed key-value store) + + Fencing agents are tracked separately in the fencingAgents field because they are mapped to + their target node (the node they can fence), not the node where monitoring operations are scheduled. + properties: + conditions: + description: |- + conditions represent the observations of the resource's current state. + Known condition types are: "Healthy", "InService", "Managed", "Enabled", "Operational", + "Active", "Started", "Schedulable". + The "Healthy" condition is an aggregate that tracks the overall health of the resource. + The "InService" condition tracks whether the resource is in service (not in maintenance mode). + The "Managed" condition tracks whether the resource is managed by pacemaker. + The "Enabled" condition tracks whether the resource is enabled. + The "Operational" condition tracks whether the resource is operational (not failed). + The "Active" condition tracks whether the resource is active (available to be used). + The "Started" condition tracks whether the resource is started. + The "Schedulable" condition tracks whether the resource is schedulable (not blocked). + Each of these conditions is required, so the array must contain at least 8 items. + items: + description: Condition contains details for one aspect + of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + 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. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + minItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: conditions must contain a condition of type + Healthy + rule: self.exists(c, c.type == 'Healthy') + - message: conditions must contain a condition of type + InService + rule: self.exists(c, c.type == 'InService') + - message: conditions must contain a condition of type + Managed + rule: self.exists(c, c.type == 'Managed') + - message: conditions must contain a condition of type + Enabled + rule: self.exists(c, c.type == 'Enabled') + - message: conditions must contain a condition of type + Operational + rule: self.exists(c, c.type == 'Operational') + - message: conditions must contain a condition of type + Active + rule: self.exists(c, c.type == 'Active') + - message: conditions must contain a condition of type + Started + rule: self.exists(c, c.type == 'Started') + - message: conditions must contain a condition of type + Schedulable + rule: self.exists(c, c.type == 'Schedulable') + name: + description: |- + name is the name of the pacemaker resource. + Valid values are "Kubelet" and "Etcd". + The Kubelet resource is a prerequisite for etcd in Two Node OpenShift with Fencing deployments. + The Etcd resource may temporarily transition to stopped during pacemaker quorum-recovery operations. + Fencing agents are tracked separately in the node's fencingAgents field. + enum: + - Kubelet + - Etcd + type: string + required: + - name + type: object + maxItems: 8 + minItems: 2 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resources must contain a resource named Kubelet + rule: self.exists(r, r.name == 'Kubelet') + - message: resources must contain a resource named Etcd + rule: self.exists(r, r.name == 'Etcd') + required: + - addresses + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + x-kubernetes-validations: + - message: lastUpdated cannot be removed once set + rule: '!has(oldSelf.lastUpdated) || has(self.lastUpdated)' + - message: lastUpdated cannot be set to an earlier timestamp + rule: '!has(oldSelf.lastUpdated) || !has(self.lastUpdated) || self.lastUpdated + >= oldSelf.lastUpdated' + required: + - metadata + type: object + x-kubernetes-validations: + - message: PacemakerCluster must be named 'cluster' + rule: self.metadata.name == 'cluster' + - message: status cannot be removed once set + rule: '!has(oldSelf.status) || has(self.status)' + served: true + storage: true + subresources: + status: {}