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 }