...

Source file src/edge-infra.dev/pkg/sds/lanoutage/detector/leave.go

Documentation: edge-infra.dev/pkg/sds/lanoutage/detector

     1  package detector
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"edge-infra.dev/pkg/lib/fog"
     9  	v1etcd "edge-infra.dev/pkg/sds/etcd/operator/apis/etcdmember/v1"
    10  	"edge-infra.dev/pkg/sds/lanoutage/detector/internal/constants"
    11  	"edge-infra.dev/pkg/sds/lanoutage/detector/internal/firewall"
    12  	"edge-infra.dev/pkg/sds/lanoutage/detector/internal/leave"
    13  
    14  	"github.com/vishvananda/netlink"
    15  	"golang.org/x/sys/unix"
    16  	"k8s.io/apimachinery/pkg/runtime"
    17  	"k8s.io/apimachinery/pkg/types"
    18  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    19  	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
    20  	"k8s.io/client-go/tools/clientcmd"
    21  	"sigs.k8s.io/controller-runtime/pkg/client"
    22  )
    23  
    24  // main function for leaving LAN outage mode
    25  func (l *LOMReconciler) Leave(ctx context.Context) error {
    26  	log := fog.FromContext(ctx)
    27  	log.Info(constants.LeavingMessage)
    28  
    29  	log.Info("getting lo interface")
    30  	lo, err := netlink.LinkByName("lo")
    31  	if err != nil {
    32  		return fmt.Errorf("there was an error getting the loopback interface")
    33  	}
    34  
    35  	log.Info("getting vip address")
    36  	vip, err := getVipAddress(l.cfg.Kubeconfig)
    37  	if err != nil {
    38  		return fmt.Errorf("there was an error getting the VIP address")
    39  	}
    40  
    41  	leaveConf := leave.NewLeaveConf(l.cfg, vip, lo)
    42  
    43  	listOfSteps := []leave.Step{
    44  		RemoveVipAlias,
    45  		RemoveKubeManifests,
    46  		RemoveKubeConfig,
    47  		RemoveEtcdManifest,
    48  		ReverseIsolateNode,
    49  		RemoveLOMFlag,
    50  		DeleteMember,
    51  	}
    52  
    53  	for _, step := range listOfSteps {
    54  		err := step(ctx, *leaveConf)
    55  		if err != nil {
    56  			return fmt.Errorf("failed to leave LAN Outage Mode: %w", err)
    57  		}
    58  	}
    59  
    60  	// setting LOM state should be highly coupled with Leave method
    61  	l.isLOM = false
    62  
    63  	log.Info(constants.LeaveSuccessMessage)
    64  	return nil
    65  }
    66  
    67  // func removeVipAlias() removes the VIP alias from the lo interface, doesn't error if there isn't a VIP present
    68  func RemoveVipAlias(ctx context.Context, cfg leave.Conf) error {
    69  	log := fog.FromContext(ctx)
    70  	log.Info("removing vip alias")
    71  
    72  	addr, err := netlink.ParseAddr(cfg.Vip + "/32")
    73  	if err != nil {
    74  		return fmt.Errorf("there was an error parsing the VIP address: %w", err)
    75  	}
    76  
    77  	err = netlink.AddrDel(cfg.Lo, addr)
    78  
    79  	// error 0x63 means it was unable to remove the IP Address from the network interface, in which case we carry on
    80  	if err != nil && !errors.Is(err, unix.Errno(0x63)) {
    81  		return fmt.Errorf("there was an error deleting the VIP alias on lo: %w", err)
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  // removeKubeConfig removes the zylevel0.conf kubeconfig and keys from the node's filesystem
    88  func RemoveKubeConfig(ctx context.Context, cfg leave.Conf) error {
    89  	log := fog.FromContext(ctx)
    90  	log.Info("removing the kubeconfig")
    91  
    92  	err := cfg.ReconcilerConfig.Fs.RemoveAll(constants.Zylevel0ConfFilepath)
    93  	if err != nil {
    94  		return fmt.Errorf("there was an error removing the zylevel0 kube config: %w", err)
    95  	}
    96  	err = cfg.ReconcilerConfig.Fs.RemoveAll(constants.Zylevel0PEMFilepath)
    97  	if err != nil {
    98  		return fmt.Errorf("there was an error removing the zylevel0-auth cert file: %w", err)
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  func RemoveEtcdManifest(ctx context.Context, cfg leave.Conf) error {
   105  	log := fog.FromContext(ctx)
   106  	log.Info("deleting etcd manifest")
   107  
   108  	err := cfg.ReconcilerConfig.Fs.RemoveAll(constants.EtcdManifestFilepath)
   109  	if err != nil {
   110  		return fmt.Errorf("failed to remove the etcd yaml: %w", err)
   111  	}
   112  	return nil
   113  }
   114  
   115  func ReverseIsolateNode(ctx context.Context, cfg leave.Conf) error {
   116  	log := fog.FromContext(ctx)
   117  	log.Info("de-isolating the node")
   118  
   119  	lo, err := getLoopbackInterface()
   120  	if err != nil {
   121  		return fmt.Errorf("an error occurred fetching the loopback interface: %w", err)
   122  	}
   123  
   124  	err = firewall.RemoveIPFilterRules(cfg.ReconcilerConfig.Fs)
   125  	if err != nil {
   126  		return fmt.Errorf("there was an error removing the IP filter rules: %w", err)
   127  	}
   128  
   129  	err = firewall.SetARPFlags(cfg.ReconcilerConfig.Fs, "leave", cfg.ReconcilerConfig.MacAddress)
   130  	if err != nil {
   131  		return fmt.Errorf("an error occurred when removing ARP filter: %w", err)
   132  	}
   133  
   134  	err = netlink.LinkSetARPOn(lo)
   135  	if err != nil {
   136  		return fmt.Errorf("there was an error removing the ARP block: %w", err)
   137  	}
   138  	return nil
   139  }
   140  
   141  func RemoveLOMFlag(ctx context.Context, cfg leave.Conf) error {
   142  	log := fog.FromContext(ctx)
   143  	log.Info("removing the LAN Outage Mode flag file")
   144  
   145  	err := cfg.ReconcilerConfig.Fs.RemoveAll(constants.LOMFlagFilepath)
   146  	if err != nil {
   147  		return fmt.Errorf("an error occurred removing the LAN Outage Mode flag file: %w", err)
   148  	}
   149  	return nil
   150  }
   151  
   152  // deleteEtcdMember() deletes the EtcdMember resource which matches the node name
   153  // successful completion is not essential for the LAN outage Leave operation so
   154  // it returns nil in all cases
   155  func DeleteMember(ctx context.Context, cfg leave.Conf) error {
   156  	log := fog.FromContext(ctx)
   157  	log.Info("deleting the EtcdMember")
   158  	emName := types.NamespacedName{Namespace: "", Name: cfg.ReconcilerConfig.NodeName}
   159  	em := &v1etcd.EtcdMember{}
   160  
   161  	err := deleteObject(context.Background(), emName, em)
   162  	if err != nil {
   163  		log.Error(err, "failed to delete EtcdMembers")
   164  	}
   165  	return nil
   166  }
   167  
   168  func deleteObject(ctx context.Context, name types.NamespacedName, obj client.Object) error {
   169  	log := fog.FromContext(ctx)
   170  	log.V(1).Info("about to delete object", "Object", obj, "NamespacedName", name)
   171  	config, err := clientcmd.BuildConfigFromFlags("", constants.AdminConfFilepath)
   172  	if err != nil {
   173  		return fmt.Errorf("couldn't create kubeconfig: %w", err)
   174  	}
   175  
   176  	kclient, err := client.New(config, clientOptions())
   177  	if err != nil {
   178  		return fmt.Errorf("couldn't create k8s client: %w", err)
   179  	}
   180  	if err != client.IgnoreNotFound(kclient.Get(ctx, name, obj)) {
   181  		return fmt.Errorf("couldn't get EtcdMember: %w", err)
   182  	}
   183  	return client.IgnoreNotFound(kclient.Delete(ctx, obj))
   184  }
   185  
   186  // format the k8s client options
   187  func clientOptions() client.Options {
   188  	return client.Options{
   189  		Scheme: newScheme(),
   190  	}
   191  }
   192  
   193  func newScheme() *runtime.Scheme {
   194  	scheme := runtime.NewScheme()
   195  	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
   196  	utilruntime.Must(v1etcd.AddToScheme(scheme))
   197  	return scheme
   198  }
   199  

View as plain text