Skip to content

Commit 43d4287

Browse files
Pramodh Pallapothueriknordmark
authored andcommitted
Cleanup Replica sets only in single node mode
This commit fixes a serious bug in cluster configuration. Basically whenever domainmgr restarts there was a legacy code which deletes all VMs running. That was implemented to handle a corner case where apps were deleted when device is powered off. We want to delete any app config in kubernetes as soon as device reboots. Assumption is device will resync config with the controller. That works fine for single node cases. In a cluster setup, apps would have moved to other nodes when this device went to reboot. Because of this bug as soon as this device reboots its deleting VMs running on other nodes, basically bouncing all VMs. Added a new API to check if this device is in cluster mode and ignore delete of VMs. Also, we no longer directly deal with VMs, its all VM replica sets now. So moved CleanupVMs to CleanupVMRs. Move the const to types/locationconstants.go Change the logging to Debugf Signed-off-by: Pramodh Pallapothu <pramodh@zededa.com>
1 parent 75a8739 commit 43d4287

File tree

4 files changed

+77
-14
lines changed

4 files changed

+77
-14
lines changed

pkg/pillar/cmd/domainmgr/domainmgr.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,20 @@ func Run(ps *pubsub.PubSub, loggerArg *logrus.Logger, logArg *base.LogObject, ar
645645
} else {
646646
// If device rebooted abruptly, kubernetes did not get time to stop the VMs.
647647
// They will be in failed state, so clean them up if they exists.
648-
count, err := kubeapi.CleanupStaleVMI()
649-
log.Noticef("domainmgr cleanup vmi count %d, %v", count, err)
648+
// We have to do this only on single node config. In the cluster setup the VMs
649+
// will be running on some other node after failover.
650+
// Even in single node one might wonder why we need to delete VMI when node is coming up
651+
// after reboot !! This is for very corner case, if the user deleted the app in the controller when
652+
// the device is powered off. Next config refresh will see app is gone and domainmgr will not do anything.
653+
// But kubernetes thinks app is still running and starts. So its safe to delete all replica sets at the start
654+
// on single node installs.
655+
clusterMode := kubeapi.IsClusterMode()
656+
657+
if !clusterMode {
658+
count, err := kubeapi.CleanupStaleVMIRs()
659+
log.Noticef("domainmgr cleanup vmirs count %d, %v", count, err)
660+
}
661+
650662
}
651663
}
652664

pkg/pillar/kubeapi/kubeapi.go

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
netclientset "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/clientset/versioned"
1717
"github.com/lf-edge/eve/pkg/pillar/base"
1818
"github.com/lf-edge/eve/pkg/pillar/pubsub"
19+
"github.com/lf-edge/eve/pkg/pillar/types"
20+
"github.com/sirupsen/logrus"
1921
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2022
"k8s.io/apimachinery/pkg/util/wait"
2123
"k8s.io/client-go/kubernetes"
@@ -364,8 +366,12 @@ func WaitForPVCReady(pvcName string, log *base.LogObject) error {
364366
return fmt.Errorf("WaitForPVCReady: time expired count %d, err %v", count, err2)
365367
}
366368

367-
// CleanupStaleVMI : delete all VMIs. Used by domainmgr on startup.
368-
func CleanupStaleVMI() (int, error) {
369+
// CleanupStaleVMIRs : delete all VMI replica sets on single node. Used by domainmgr on startup.
370+
// There are two replica set types.
371+
// 1) vmirs (VM replica sets)
372+
// 2) podrs (Pod replica sets, basically native containers)
373+
// Iterate through all replicasets and delete those.
374+
func CleanupStaleVMIRs() (int, error) {
369375
// Only wait for kubevirt if we are not in base-k3s mode.
370376
if err := registrationAppliedToCluster(); err == nil {
371377
// In base k3s mode, pillar not deploying kubevirt VM app instances
@@ -377,25 +383,63 @@ func CleanupStaleVMI() (int, error) {
377383
return 0, fmt.Errorf("couldn't get the Kube Config: %v", err)
378384
}
379385

380-
clientset, err := kubecli.GetKubevirtClientFromRESTConfig(kubeconfig)
386+
virtClient, err := kubecli.GetKubevirtClientFromRESTConfig(kubeconfig)
381387
if err != nil {
382388
return 0, fmt.Errorf("couldn't get the Kube client Config: %v", err)
383389
}
384390

385391
ctx := context.Background()
386392

387-
// get a list of our VMs
388-
vmiList, err := clientset.VirtualMachineInstance(EVEKubeNameSpace).List(ctx, &metav1.ListOptions{})
393+
// get a list of our VM replica sets
394+
vmrsList, err := virtClient.ReplicaSet(EVEKubeNameSpace).List(metav1.ListOptions{})
389395
if err != nil {
390-
return 0, fmt.Errorf("couldn't get the Kubevirt VMs: %v", err)
396+
return 0, fmt.Errorf("couldn't get the Kubevirt VM replcia sets: %v", err)
391397
}
392398

393399
var count int
394-
for _, vmi := range vmiList.Items {
395-
if err := clientset.VirtualMachineInstance(EVEKubeNameSpace).Delete(ctx, vmi.ObjectMeta.Name, &metav1.DeleteOptions{}); err != nil {
396-
return count, fmt.Errorf("delete vmi error: %v", err)
400+
for _, vmirs := range vmrsList.Items {
401+
402+
if err := virtClient.ReplicaSet(EVEKubeNameSpace).Delete(vmirs.ObjectMeta.Name, &metav1.DeleteOptions{}); err != nil {
403+
return count, fmt.Errorf("delete vmirs error: %v", err)
397404
}
398405
count++
399406
}
407+
408+
// Get list of native container pods replica sets
409+
podrsList, err := virtClient.AppsV1().ReplicaSets(EVEKubeNameSpace).List(ctx, metav1.ListOptions{})
410+
if err != nil {
411+
return count, fmt.Errorf("couldn't get the pod replica sets: %v", err)
412+
}
413+
414+
for _, podrs := range podrsList.Items {
415+
416+
err := virtClient.AppsV1().ReplicaSets(EVEKubeNameSpace).Delete(ctx, podrs.ObjectMeta.Name, metav1.DeleteOptions{})
417+
if err != nil {
418+
return count, fmt.Errorf("delete podrs error: %v", err)
419+
}
420+
count++
421+
}
422+
400423
return count, nil
401424
}
425+
426+
// IsClusterMode : Returns true if this node is part of a cluster by checking EdgeNodeClusterConfigFile
427+
// If EdgeNodeClusterConfigFile exists and is > 0 bytes then this node is part of a cluster.
428+
func IsClusterMode() bool {
429+
430+
fileInfo, err := os.Stat(types.EdgeNodeClusterConfigFile)
431+
if os.IsNotExist(err) {
432+
logrus.Debugf("This node is not in cluster mode")
433+
return false
434+
} else if err != nil {
435+
logrus.Errorf("Error checking file '%s': %v", types.EdgeNodeClusterConfigFile, err)
436+
return false
437+
}
438+
439+
if fileInfo.Size() > 0 {
440+
logrus.Debugf("This node is in cluster mode")
441+
return true
442+
}
443+
444+
return false
445+
}

pkg/pillar/kubeapi/nokube.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ func WaitForKubernetes(string, *pubsub.PubSub, *time.Ticker,
1919
panic("WaitForKubernetes is not built")
2020
}
2121

22-
// CleanupStaleVMI in this file is just stub for non-kubevirt hypervisors.
23-
func CleanupStaleVMI() (int, error) {
24-
panic("CleanupStaleVMI is not built")
22+
// CleanupStaleVMIRs in this file is just stub for non-kubevirt hypervisors.
23+
func CleanupStaleVMIRs() (int, error) {
24+
panic("CleanupStaleVMIRs is not built")
2525
}
2626

2727
// GetPVCList in this file is just stub for non-kubevirt hypervisors.
@@ -40,3 +40,8 @@ func GetNodeDrainStatus(pubsub.Subscription, *base.LogObject) *NodeDrainStatus {
4040
// No need to query for inprogress operations, just a noop
4141
return &NodeDrainStatus{Status: NOTSUPPORTED}
4242
}
43+
44+
// IsClusterMode is a stub for non-kubevirt builds
45+
func IsClusterMode() bool {
46+
return false
47+
}

pkg/pillar/types/locationconsts.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ const (
144144

145145
// LocalActiveAppConfigDir - directory to put JSON of the apps that are running.
146146
LocalActiveAppConfigDir = "/persist/vault/active-app-instance-config/"
147+
// EdgeNodeClusterConfigFile - the file which contains edgenodecluster config
148+
EdgeNodeClusterConfigFile = PersistStatusDir + "/zedagent/EdgeNodeClusterConfig/global.json"
147149
)
148150

149151
var (

0 commit comments

Comments
 (0)