github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/workdiff.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package manager
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/cilium/cilium/pkg/bgpv1/manager/instance"
    10  	v2api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    11  	v2alpha1api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
    12  )
    13  
    14  // reconcileDiff is a helper structure which provides fields and a method set
    15  // for computing a diff of work to achieve a given
    16  // *v2alpha1api.CiliumBGPVirtualRouter configuration.
    17  type reconcileDiff struct {
    18  	// incoming CiliumBGPVirtualRouter configs mapped by their
    19  	// local ASN.
    20  	seen map[int64]*v2alpha1api.CiliumBGPVirtualRouter
    21  	// The local CiliumNode information at the time which reconciliation was triggered.
    22  	ciliumNode *v2api.CiliumNode
    23  	// Local ASNs which BgpServers must be instantiated, configured,
    24  	// and added to the manager. Intended key for `seen` map.
    25  	register []int64
    26  	// Local ASNs which BgpServers exist for but current policy has marked
    27  	// for removal. Intended key for Manager's LocalASNMap.
    28  	withdraw []int64
    29  	// Local ASNs which BgpServers exist for but policy associated with server
    30  	// may have been updated and needs further reconciliation.
    31  	// Intended key for 'seen' map.
    32  	reconcile []int64
    33  }
    34  
    35  // newReconcileDiff constructs a new *reconcileDiff with all internal instructures
    36  // initialized.
    37  func newReconcileDiff(ciliumNode *v2api.CiliumNode) *reconcileDiff {
    38  	return &reconcileDiff{
    39  		seen:       make(map[int64]*v2alpha1api.CiliumBGPVirtualRouter),
    40  		ciliumNode: ciliumNode,
    41  		register:   []int64{},
    42  		withdraw:   []int64{},
    43  		reconcile:  []int64{},
    44  	}
    45  }
    46  
    47  // diff computes the reconcileDiff for given an incoming
    48  // *v2alpha1api.CiliumBGPPeeringPolicy and the current LocalASNMap state.
    49  //
    50  // Once diff is invoked the appropriate field will contain BgpServers to register,
    51  // withdraw, or reconcile in the reconcileDiff's respective fields.
    52  func (wd *reconcileDiff) diff(m LocalASNMap, policy *v2alpha1api.CiliumBGPPeeringPolicy) error {
    53  	if err := wd.registerOrReconcileDiff(m, policy); err != nil {
    54  		return fmt.Errorf("encountered error creating register or reconcile diff: %w", err)
    55  	}
    56  	if err := wd.withdrawDiff(m); err != nil {
    57  		return fmt.Errorf("encountered error creating withdraw diff: %w", err)
    58  	}
    59  	return nil
    60  }
    61  
    62  // String provides a string representation of the reconcileDiff.
    63  func (wd *reconcileDiff) String() string {
    64  	return fmt.Sprintf("Registering: %v Withdrawing: %v Reconciling: %v",
    65  		wd.register,
    66  		wd.withdraw,
    67  		wd.reconcile,
    68  	)
    69  }
    70  
    71  // empty informs the caller whether the reconcileDiff contains any work to undertake.
    72  func (wd *reconcileDiff) empty() bool {
    73  	switch {
    74  	case len(wd.register) > 0:
    75  		fallthrough
    76  	case len(wd.withdraw) > 0:
    77  		fallthrough
    78  	case len(wd.reconcile) > 0:
    79  		return false
    80  	}
    81  	return true
    82  }
    83  
    84  // registerOrReconcileDiff will populate the `seen` field of the reconcileDiff with `policy`,
    85  // compute BgpServers which must be registered and mark existing BgpServers for
    86  // reconciliation of their configuration.
    87  //
    88  // since registerOrReconcileDiff populates the `seen` field of a diff, this method should always
    89  // be called first when computing a reconcileDiff.
    90  func (wd *reconcileDiff) registerOrReconcileDiff(m LocalASNMap, policy *v2alpha1api.CiliumBGPPeeringPolicy) error {
    91  	for i, config := range policy.Spec.VirtualRouters {
    92  		if _, ok := wd.seen[config.LocalASN]; !ok {
    93  			wd.seen[config.LocalASN] = &policy.Spec.VirtualRouters[i]
    94  		} else {
    95  			return fmt.Errorf("encountered duplicate local ASNs")
    96  		}
    97  		if _, ok := m[config.LocalASN]; !ok {
    98  			wd.register = append(wd.register, config.LocalASN)
    99  		} else {
   100  			wd.reconcile = append(wd.reconcile, config.LocalASN)
   101  		}
   102  	}
   103  	return nil
   104  }
   105  
   106  // withdrawDiff will populate the `withdraw` field of a reconcileDiff, indicating which
   107  // existing BgpServers must disconnected and removed from the Manager.
   108  func (wd *reconcileDiff) withdrawDiff(m LocalASNMap) error {
   109  	for k := range m {
   110  		if _, ok := wd.seen[k]; !ok {
   111  			wd.withdraw = append(wd.withdraw, k)
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  type reconcileDiffV2 struct {
   118  	seen map[string]*v2alpha1api.CiliumBGPNodeInstance
   119  
   120  	ciliumNode *v2api.CiliumNode
   121  
   122  	register  []string
   123  	withdraw  []string
   124  	reconcile []string
   125  }
   126  
   127  // newReconcileDiffV2 constructs a new *reconcileDiffV2 with all internal structures
   128  // initialized.
   129  func newReconcileDiffV2(ciliumNode *v2api.CiliumNode) *reconcileDiffV2 {
   130  	return &reconcileDiffV2{
   131  		seen:       make(map[string]*v2alpha1api.CiliumBGPNodeInstance),
   132  		ciliumNode: ciliumNode,
   133  		register:   []string{},
   134  		withdraw:   []string{},
   135  		reconcile:  []string{},
   136  	}
   137  }
   138  
   139  func (wd *reconcileDiffV2) diff(existingInstances map[string]*instance.BGPInstance, desiredConfig *v2alpha1api.CiliumBGPNodeConfig) error {
   140  	if err := wd.registerOrReconcileDiff(existingInstances, desiredConfig); err != nil {
   141  		return fmt.Errorf("encountered error creating register or reconcile diff: %w", err)
   142  	}
   143  	if err := wd.withdrawDiff(existingInstances); err != nil {
   144  		return fmt.Errorf("encountered error creating withdraw diff: %w", err)
   145  	}
   146  	return nil
   147  }
   148  
   149  // String provides a string representation of the reconcileDiff.
   150  func (wd *reconcileDiffV2) String() string {
   151  	return fmt.Sprintf("Registering: %v Withdrawing: %v Reconciling: %v",
   152  		wd.register,
   153  		wd.withdraw,
   154  		wd.reconcile,
   155  	)
   156  }
   157  
   158  // empty informs the caller whether the reconcileDiff contains any work to undertake.
   159  func (wd *reconcileDiffV2) empty() bool {
   160  	switch {
   161  	case len(wd.register) > 0:
   162  		fallthrough
   163  	case len(wd.withdraw) > 0:
   164  		fallthrough
   165  	case len(wd.reconcile) > 0:
   166  		return false
   167  	}
   168  	return true
   169  }
   170  
   171  // registerOrReconcileDiff will populate the `seen` field of the reconcileDiff with `policy`,
   172  // compute BgpServers which must be registered and mark existing BgpServers for
   173  // reconciliation of their configuration.
   174  //
   175  // since registerOrReconcileDiff populates the `seen` field of a diff, this method should always
   176  // be called first when computing a reconcileDiff.
   177  func (wd *reconcileDiffV2) registerOrReconcileDiff(existingInstances map[string]*instance.BGPInstance, desiredConfig *v2alpha1api.CiliumBGPNodeConfig) error {
   178  	for i, config := range desiredConfig.Spec.BGPInstances {
   179  		if _, ok := wd.seen[config.Name]; !ok {
   180  			wd.seen[config.Name] = &desiredConfig.Spec.BGPInstances[i]
   181  		} else {
   182  			return fmt.Errorf("encountered duplicate BGP instance with name %s", config.Name)
   183  		}
   184  		if _, ok := existingInstances[config.Name]; !ok {
   185  			wd.register = append(wd.register, config.Name)
   186  		} else {
   187  			wd.reconcile = append(wd.reconcile, config.Name)
   188  		}
   189  	}
   190  	return nil
   191  }
   192  
   193  // withdrawDiff will populate the `withdraw` field of a reconcileDiff, indicating which
   194  // existing BgpInstances must be disconnected and removed from the Manager.
   195  func (wd *reconcileDiffV2) withdrawDiff(existingInstances map[string]*instance.BGPInstance) error {
   196  	for k := range existingInstances {
   197  		if _, ok := wd.seen[k]; !ok {
   198  			wd.withdraw = append(wd.withdraw, k)
   199  		}
   200  	}
   201  	return nil
   202  }