package etcd import ( "context" "fmt" "strings" "github.com/rancher/k3s/pkg/version" controllerv1 "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" ) func RegisterMemberHandlers(ctx context.Context, etcd *ETCD, nodes controllerv1.NodeController) { e := &etcdMemberHandler{ etcd: etcd, nodeController: nodes, ctx: ctx, } nodes.OnChange(ctx, "managed-etcd-controller", e.sync) nodes.OnRemove(ctx, "managed-etcd-controller", e.onRemove) } var ( removalAnnotation = "etcd." + version.Program + ".cattle.io/remove" removedNodeNameAnnotation = "etcd." + version.Program + ".cattle.io/removed-node-name" ) type etcdMemberHandler struct { etcd *ETCD nodeController controllerv1.NodeController ctx context.Context } func (e *etcdMemberHandler) sync(key string, node *v1.Node) (*v1.Node, error) { if node == nil { return nil, nil } if _, ok := node.Labels[EtcdRoleLabel]; !ok { logrus.Debugf("Node %s was not labeled etcd node, skipping sync", key) return node, nil } node = node.DeepCopy() if removalRequested, ok := node.Annotations[removalAnnotation]; ok { if removed, ok := node.Annotations[removedNodeNameAnnotation]; ok { // check to see if removed is true. if it is, nothing to do. if currentNodeName, ok := node.Annotations[NodeNameAnnotation]; ok { if currentNodeName != removed { // If the current node name is not the same as the removed node name, reset the tainted annotation and removed node name logrus.Infof("Resetting removed node flag as removed node name ( did not match current node name") delete(node.Annotations, removedNodeNameAnnotation) node.Annotations[removalAnnotation] = "false" return e.nodeController.Update(node) } // this is the case where the current node name matches the removed node name. We have already removed the // node, so no need to perform any action. Fallthrough to the non-op below. } // This is the edge case where the removed annotation exists, but there is not a current node name annotation. // This should be a non-op, as we can't remove the node anyway. logrus.Debugf("etcd member %s was already marked via annotations as removed", key) return node, nil } if strings.ToLower(removalRequested) == "true" { // remove the member. name, ok := node.Annotations[NodeNameAnnotation] if !ok { return node, fmt.Errorf("node name annotation for node %s not found", key) } address, ok := node.Annotations[NodeAddressAnnotation] if !ok { return node, fmt.Errorf("node address annotation for node %s not found", key) } logrus.Debugf("removing etcd member from cluster name: %s address: %s", name, address) if err := e.etcd.RemovePeer(e.ctx, name, address, true); err != nil { return node, err } logrus.Debugf("etcd member removal successful for name: %s address: %s", name, address) // Set the removed node name annotation and clean up the other etcd node annotations. // These will be set if the tombstone file is then created and the etcd member is re-added, to their new // respective values. node.Annotations[removedNodeNameAnnotation] = name delete(node.Annotations, NodeNameAnnotation) delete(node.Annotations, NodeAddressAnnotation) return e.nodeController.Update(node) } // In the event that we had an unexpected removal value, simply return. // Fallthrough to the non-op below. } // This is a non-op, as we don't have a tainted annotation to worry about. return node, nil } func (e *etcdMemberHandler) onRemove(key string, node *v1.Node) (*v1.Node, error) { if _, ok := node.Labels[EtcdRoleLabel]; !ok { logrus.Debugf("Node %s was not labeled etcd node, skipping etcd member removal", key) return node, nil } logrus.Infof("Removing etcd member %s from cluster", key) if removalRequested, ok := node.Annotations[removalAnnotation]; ok { if strings.ToLower(removalRequested) == "true" { if removedNodeName, ok := node.Annotations[removedNodeNameAnnotation]; ok { if len(removedNodeName) > 0 { // If we received a node to delete that has already been removed via annotation, it will be missing // the corresponding node name and address annotations. logrus.Infof("etcd member %s was already removed as member name %s via annotation from the cluster", key, removedNodeName) return node, nil } } } } name, ok := node.Annotations[NodeNameAnnotation] if !ok { return node, fmt.Errorf("node name annotation for node %s not found", key) } address, ok := node.Annotations[NodeAddressAnnotation] if !ok { return node, fmt.Errorf("node address annotation for node %s not found", key) } return node, e.etcd.RemovePeer(e.ctx, name, address, true) }