You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 3c8049c
Browse filesBrowse the repository at this point in the historyBrowse files
Stop concurrent CKS operations clobbering each other, recover stuck states
A Kubernetes cluster's state was written by id alone, so two operations
started on the same cluster at once both got past their first
transition. Each then ran its steps against VMs the other was changing,
and whichever finished last decided the final state. The status scanner
could also overwrite the state of an operation that started after it
read the cluster.
The state is now compared and set in one update, as the VM and volume
states are. Each operation moves the cluster into its own state before
it changes anything, and fails without touching the cluster when another
operation got there first. From then on it only moves the cluster while
the cluster is in a state the operation moved it to. Starting a cluster
in Alert now moves it to Starting like any other start. A start from
Error, a stop from Created or Error, and a retried destroy still go on
without a state change, as they did before; the start and stop then
leave the state alone when they end. From any other state with no
transition for it, an operation is now refused instead of running
alongside the one that holds the cluster. The scanner only completes a
stop or a recovery that it started itself.
A management server that stopped during an operation left the cluster in
that operation's state. Nothing moved it on, and every later operation
on it was refused until the database was edited by hand. The scanner now
fails the operation of a cluster in an operation's state when no job is
in progress for it on any management server. The existing Alert
reconciliation then returns a healthy cluster to Running. To find those
jobs, the start, stop, scale and upgrade jobs now record their cluster,
as the create, delete and node jobs already did. A cluster named by a
job in progress on a management server of an older version, which does
not record it, is left alone. This replaces the scanner's first-run
handling of Starting clusters, which tried a transition that does not
exist.
No schema or API change.
Signed-off-by: Brad House <bhouse@nexthop.ai>
(cherry picked from commit fee9834)
Copy file name to clipboardExpand all lines: plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
for (KubernetesClusterkubernetesCluster : startingKubernetesClusters) {
2925
-
if ((newDate()).getTime() - kubernetesCluster.getCreated().getTime() < 10*60*1000) {
2926
-
continue;
2927
-
}
2928
-
if (logger.isInfoEnabled()) {
2929
-
logger.info("Running Kubernetes cluster state scanner on Kubernetes cluster: {} for state: {}", kubernetesCluster, KubernetesCluster.State.Starting.toString());
2930
-
}
2931
-
try {
2932
-
if (isClusterVMsInDesiredState(kubernetesCluster, VirtualMachine.State.Running)) {
if (untaggedJobsCmdInfo.stream().anyMatch(cmdInfo -> cmdInfo.isEmpty() || cmdInfo.contains(cluster.getUuid()))) {
2984
+
logger.debug("Not recovering Kubernetes cluster {} from state {} while a job of an older management server may be acting on it", cluster, cluster.getState());
2985
+
continue;
2986
+
}
2987
+
try {
2988
+
recoverOrphanedOperationState(cluster.getId());
2989
+
} catch (Exceptione) {
2990
+
logger.warn("Failed to recover Kubernetes cluster {} from state {}", cluster, cluster.getState(), e);
2991
+
}
2992
+
}
2993
+
}
2994
+
2995
+
/**
2996
+
* The cluster is read before its jobs. An operation that ends between the two reads moves the cluster to another
2997
+
* state, so the compare-and-set of the state as read misses. For it to hit a live operation instead, one operation
2998
+
* would have to end and another start and reach the same state between the two reads and the update right after.
if (_stateMachine.transitTo(cluster, KubernetesCluster.Event.OperationFailed, null, kubernetesClusterDao)) {
3010
+
logger.warn("Kubernetes cluster {} was in state {} with no operation in progress, as its operation ended without moving it on, for example because the management server running it stopped; marked the operation as failed", cluster, state);
3011
+
}
3012
+
}
3013
+
2960
3014
// checks if Kubernetes cluster is in desired state
Copy file name to clipboardExpand all lines: plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java
+84-1Lines changed: 84 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -164,6 +164,10 @@ public class KubernetesClusterActionWorker {
logger.warn("Unable to find Kubernetes cluster with ID {} to apply event {}", kubernetesClusterId, e);
786
+
returnfalse;
787
+
}
788
+
if (operationStarted) {
789
+
if (ownedState == null || kubernetesCluster.getState() != ownedState) {
790
+
logger.debug("Not applying event {} to Kubernetes cluster {} in state {}, which its operation does not hold", e, kubernetesCluster, kubernetesCluster.getState());
791
+
returnfalse;
792
+
}
793
+
}
775
794
try {
776
-
return_stateMachine.transitTo(kubernetesCluster, e, null, kubernetesClusterDao);
795
+
booleantransitioned = _stateMachine.transitTo(kubernetesCluster, e, null, kubernetesClusterDao);
796
+
if (operationStarted) {
797
+
// the operation holds the state it moved the cluster to, until another operation moves it on; on a
Stringmessage = String.format("Kubernetes cluster %s changed from state %s to %s before %s could start; another operation may be in progress, please retry once it has finished",
Copy file name to clipboardExpand all lines: plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterAddWorker.java
+10-2Lines changed: 10 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -86,8 +86,9 @@ public boolean addNodesToCluster(List<Long> nodeIds, boolean mountCksIsoOnVr, bo
86
86
} catch (ManagementServerExceptione) {
87
87
thrownewCloudRuntimeException(String.format("Failed to retrieve public IP for the network: %s ", network.getName()));
88
88
}
89
+
// take the cluster before attaching the ISO, so that an add which is refused leaves nothing behind
Copy file name to clipboardExpand all lines: plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java
+4-1Lines changed: 4 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -328,7 +328,10 @@ public boolean destroy() throws CloudRuntimeException {
Copy file name to clipboardExpand all lines: plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterRemoveWorker.java
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -70,7 +70,7 @@ public boolean removeNodesFromCluster(List<Long> nodeIds) {
70
70
} catch (ManagementServerExceptione) {
71
71
thrownewCloudRuntimeException(String.format("Failed to retrieve public IP for the network: %s ", network.getName()));
0 commit comments