|
1 | 1 | package handler |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "fmt" |
4 | 5 | "io" |
5 | 6 | "reflect" |
6 | 7 | "slices" |
@@ -136,6 +137,11 @@ func (h *InventoryHandler) Patch(request *restful.Request, response *restful.Res |
136 | 137 | return |
137 | 138 | } |
138 | 139 |
|
| 140 | + if err := validateUniqueHostVariables(updatedInventory); err != nil { |
| 141 | + api.HandleBadRequest(response, request, errors.Wrapf(err, "unable to patch Inventory %s/%s in the cluster: %v", namespace, inventoryName, err)) |
| 142 | + return |
| 143 | + } |
| 144 | + |
139 | 145 | // completeInventory normalizes the inventory groups: |
140 | 146 | // - Synchronizes the "kube_control_plane" group to the "etcd" group. |
141 | 147 | // - Removes duplicate hosts and groups within each group. |
@@ -443,3 +449,47 @@ func (h *InventoryHandler) ListHosts(request *restful.Request, response *restful |
443 | 449 | results := query.DefaultList(hostTable, queryParam, less, filter) |
444 | 450 | _ = response.WriteEntity(results) |
445 | 451 | } |
| 452 | + |
| 453 | +// validateUniqueHostVariables ensures that certain host variables are unique across all hosts in the inventory. |
| 454 | +// Specifically, it checks that: |
| 455 | +// - Each internal IPv4 address (_const.VariableIPv4) is assigned to only one host. |
| 456 | +// - Each SSH connection (the combination of ssh_host and ssh_port under the "connector" variable) is unique to a single host. |
| 457 | +func validateUniqueHostVariables(inventory *kkcorev1.Inventory) error { |
| 458 | + // Maps to track uniqueness: internal IPv4 address -> hostname, and "ssh_host:ssh_port" -> hostname |
| 459 | + internalIPv4ToHostname := make(map[string]string) |
| 460 | + sshConnectionToHostname := make(map[string]string) |
| 461 | + |
| 462 | + for hostname, rawHostVars := range inventory.Spec.Hosts { |
| 463 | + hostVars := variable.Extension2Variables(rawHostVars) |
| 464 | + |
| 465 | + // 1. Ensure internal IPv4 address is unique across all hosts |
| 466 | + if internalIPv4, ok := hostVars[_const.VariableIPv4].(string); ok && internalIPv4 != "" { |
| 467 | + if existingHost, found := internalIPv4ToHostname[internalIPv4]; found && existingHost != hostname { |
| 468 | + return fmt.Errorf("duplicate internal_ipv4 detected: %s is assigned to both %s and %s", internalIPv4, existingHost, hostname) |
| 469 | + } |
| 470 | + internalIPv4ToHostname[internalIPv4] = hostname |
| 471 | + } |
| 472 | + |
| 473 | + // 2. Ensure SSH connection (ssh_host + ssh_port) is unique across all hosts |
| 474 | + var sshHost, sshPort string |
| 475 | + if connector, ok := hostVars[_const.VariableConnector].(map[string]any); ok { |
| 476 | + if v, ok := connector[_const.VariableConnectorHost].(string); ok { |
| 477 | + sshHost = v |
| 478 | + } |
| 479 | + switch v := connector[_const.VariableConnectorPort].(type) { |
| 480 | + case string: |
| 481 | + sshPort = v |
| 482 | + case float64: |
| 483 | + sshPort = fmt.Sprintf("%.0f", v) |
| 484 | + } |
| 485 | + } |
| 486 | + if sshHost != "" && sshPort != "" { |
| 487 | + sshKey := sshHost + ":" + sshPort |
| 488 | + if existingHost, found := sshConnectionToHostname[sshKey]; found && existingHost != hostname { |
| 489 | + return fmt.Errorf("duplicate SSH connection detected: %s is assigned to both %s and %s", sshKey, existingHost, hostname) |
| 490 | + } |
| 491 | + sshConnectionToHostname[sshKey] = hostname |
| 492 | + } |
| 493 | + } |
| 494 | + return nil |
| 495 | +} |
0 commit comments