...

Source file src/edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/nodefirewall/nodefirewall.go

Documentation: edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/nodefirewall

     1  package nodefirewall
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  
     9  	ctrl "sigs.k8s.io/controller-runtime"
    10  	"sigs.k8s.io/controller-runtime/pkg/client"
    11  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    12  
    13  	"edge-infra.dev/pkg/k8s/meta/status"
    14  	"edge-infra.dev/pkg/k8s/runtime/conditions"
    15  	"edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
    16  	"edge-infra.dev/pkg/k8s/runtime/patch"
    17  	v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
    18  	"edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
    19  )
    20  
    21  type Plugin struct {
    22  	config           config.Config
    23  	name             string
    24  	owner            string
    25  	conditions       reconcile.Conditions
    26  	defaultInterface string
    27  }
    28  
    29  var nodeFirewallConditions = reconcile.Conditions{
    30  	Target: status.ReadyCondition,
    31  	Owned: []string{
    32  		string(v1ien.NodeFirewallController),
    33  	},
    34  	Summarize: []string{
    35  		string(v1ien.NodeFirewallController),
    36  	},
    37  	NegativePolarity: []string{},
    38  }
    39  
    40  func (fw Plugin) Reconcile(ctx context.Context, object client.Object, cfg config.Config) (recErr error) {
    41  	log := ctrl.LoggerFrom(ctx)
    42  	ctx = ctrl.LoggerInto(ctx, log)
    43  
    44  	if reflect.DeepEqual(fw.conditions, reconcile.Conditions{}) {
    45  		fw.conditions = nodeFirewallConditions
    46  	}
    47  
    48  	nodefirewall, ok := object.(*v1ien.NodeFirewall)
    49  	if !ok {
    50  		return nil
    51  	}
    52  
    53  	fw.config = cfg
    54  	fw.name = nodefirewall.Name
    55  	if len(nodefirewall.OwnerReferences) != 1 {
    56  		return fmt.Errorf("invalid NodeFirewall - invalid OwnerReferences: %+v", nodefirewall.OwnerReferences)
    57  	}
    58  	fw.owner = nodefirewall.OwnerReferences[0].Name
    59  
    60  	result := reconcile.ResultEmpty
    61  
    62  	patcher := patch.NewSerialPatcher(nodefirewall, fw.config.GetClient())
    63  	defer func() {
    64  		recErr = fw.summarizer(ctx, patcher, nodefirewall, result, recErr)
    65  	}()
    66  
    67  	conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.NfwProgressing), "%s", v1ien.Reconciling.String())
    68  
    69  	// Add finalizer if it doesn't exist
    70  	if !controllerutil.ContainsFinalizer(nodefirewall, v1ien.NodeFirewallFinalizer) {
    71  		controllerutil.AddFinalizer(nodefirewall, v1ien.NodeFirewallFinalizer)
    72  	}
    73  
    74  	// perform update or remove logic based on whether the object was deleted
    75  	if !nodefirewall.ObjectMeta.DeletionTimestamp.IsZero() {
    76  		recErr = fw.removeFiles(ctx, nodefirewall)
    77  		return
    78  	}
    79  
    80  	if recErr = fw.updateFiles(ctx, nodefirewall); recErr != nil {
    81  		return
    82  	}
    83  
    84  	conditions.MarkTrue(nodefirewall, status.ReadyCondition, string(v1ien.NfwSuccessful), "%s", v1ien.Succeeded.String())
    85  	result = reconcile.ResultSuccess
    86  	return
    87  }
    88  
    89  func (fw Plugin) updateFiles(ctx context.Context, nodefirewall *v1ien.NodeFirewall) error {
    90  	log := ctrl.LoggerFrom(ctx)
    91  
    92  	// validate new NodeFirewall
    93  	if valid, reason := fw.validateNodeFirewall(nodefirewall); !valid {
    94  		err := errors.New("validation failed")
    95  		log.Error(err, fmt.Sprintf(v1ien.Invalid, reason))
    96  		conditions.MarkTrue(nodefirewall, status.ReadyCondition, string(v1ien.CfwFailed), v1ien.Invalid, reason)
    97  		return err
    98  	}
    99  
   100  	if err := fw.setDefaultInterface(ctx); err != nil {
   101  		log.Error(err, v1ien.InterfaceRequeueing)
   102  		conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.CfwFailed), "%s", v1ien.InterfaceRequeueing)
   103  		return err
   104  	}
   105  
   106  	// write new rule files
   107  	files, err := fw.writeFiles(nodefirewall.Spec.Rules)
   108  	if err != nil {
   109  		// attempt to remove anything written in case of failure
   110  		_ = fw.deleteFiles(files)
   111  		log.Error(err, v1ien.WriteFilesRequeueing)
   112  		conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.CfwFailed), "%s", v1ien.WriteFilesRequeueing)
   113  		return err
   114  	}
   115  
   116  	// remove stale files
   117  	if err := fw.deleteStaleFiles(fw.getFileInventory(nodefirewall), files); err != nil {
   118  		log.Error(err, v1ien.RemoveFilesRequeueing)
   119  		conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.CfwFailed), "%s", v1ien.RemoveFilesRequeueing)
   120  		return err
   121  	}
   122  
   123  	// udpate inventory
   124  	fw.setFileInventory(nodefirewall, files)
   125  
   126  	return nil
   127  }
   128  
   129  func (fw Plugin) validateNodeFirewall(nodefirewall *v1ien.NodeFirewall) (bool, string) {
   130  	if valid, reason := nodefirewall.ValidateRules(); !valid {
   131  		return valid, reason
   132  	}
   133  
   134  	for _, rule := range nodefirewall.Spec.Rules {
   135  		if rule.InterfaceMAC == "" {
   136  			continue
   137  		}
   138  		if _, err := fw.config.GetInterfaceFromHardwareAddress(rule.InterfaceMAC); err != nil {
   139  			return false, fmt.Sprintf("couldn't get interface from provided hardware address %s: %v", rule.InterfaceMAC, err)
   140  		}
   141  	}
   142  
   143  	return true, ""
   144  }
   145  
   146  func (fw Plugin) removeFiles(ctx context.Context, nodefirewall *v1ien.NodeFirewall) error {
   147  	// delete rules files
   148  	if err := fw.deleteFiles(fw.getFileInventory(nodefirewall)); err != nil {
   149  		ctrl.LoggerFrom(ctx).Error(err, v1ien.RemoveFilesRequeueing)
   150  		conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.NfwFailed), "%s", v1ien.RemoveFilesRequeueing)
   151  		return err
   152  	}
   153  
   154  	conditions.MarkTrue(nodefirewall, status.ReadyCondition, string(v1ien.NfwSuccessful), "%s", v1ien.Succeeded.String())
   155  	controllerutil.RemoveFinalizer(nodefirewall, v1ien.NodeFirewallFinalizer)
   156  	return nil
   157  }
   158  
   159  func (fw Plugin) getFileInventory(nodefirewall *v1ien.NodeFirewall) []string {
   160  	if nodefirewall.Status != nil && nodefirewall.Status.Inventory != nil {
   161  		return nodefirewall.Status.Inventory
   162  	}
   163  	return []string{}
   164  }
   165  
   166  func (fw Plugin) setFileInventory(nodefirewall *v1ien.NodeFirewall, files []string) {
   167  	if nodefirewall.Status == nil {
   168  		nodefirewall.Status = &v1ien.NodeFirewallStatus{}
   169  	}
   170  	nodefirewall.Status.Inventory = files
   171  }
   172  
   173  func (fw *Plugin) setDefaultInterface(ctx context.Context) error {
   174  	ienode, err := fw.config.GetHostIENode(ctx)
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	iface := ienode.Status.Network.DefaultInterfaceName
   180  	if iface == "" {
   181  		return errors.New("default interface not set")
   182  	}
   183  	fw.defaultInterface = iface
   184  	return nil
   185  }
   186  
   187  func (fw *Plugin) summarizer(ctx context.Context, patcher *patch.SerialPatcher, nodeFirewall *v1ien.NodeFirewall, result reconcile.Result, recErr error) error {
   188  	s := reconcile.NewSummarizer(patcher)
   189  	_, err := s.SummarizeAndPatch(ctx, nodeFirewall,
   190  		reconcile.WithConditions(fw.conditions),
   191  		reconcile.WithResult(result),
   192  		reconcile.WithError(recErr),
   193  		reconcile.WithIgnoreNotFound(),
   194  		reconcile.WithProcessors(
   195  			reconcile.RecordReconcileReq,
   196  			reconcile.RecordResult,
   197  		),
   198  		reconcile.WithFieldOwner(fw.name),
   199  	)
   200  	return err
   201  }
   202  

View as plain text