Skip to content

Commit 63d44d0

Browse files
committed
update PE to support a new ns scoped pods field and status conditions
1 parent 0499f4c commit 63d44d0

File tree

7 files changed

+227
-23
lines changed

7 files changed

+227
-23
lines changed

api/v1alpha1/policyendpoint_types.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,46 @@ type PolicyEndpointSpec struct {
9393

9494
// Egress is the list of egress rules containing resolved network addresses
9595
Egress []EndpointInfo `json:"egress,omitempty"`
96+
97+
// AllPodsInNameSpace is the boolean value indicating should all pods in the policy namespace be selected
98+
// +optional
99+
AllPodsInNamespace bool `json:"allPodsInNamespace,omitempty"`
96100
}
97101

98102
// PolicyEndpointStatus defines the observed state of PolicyEndpoint
99103
type PolicyEndpointStatus struct {
100104
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
101105
// Important: Run "make" to regenerate code after modifying this file
106+
107+
// +optional
108+
Conditions []PolicyEndpointCondition `json:"conditions,omitempty"`
109+
}
110+
111+
type PolicyEndpointConditionType string
112+
113+
const (
114+
Packed PolicyEndpointConditionType = "PackedPolicyEndpoint"
115+
Updated PolicyEndpointConditionType = "PatchedPolicyEndpoint"
116+
)
117+
118+
// PolicyEndpointCondition describes the state of a PolicyEndpoint at a certain point.
119+
// For example, binpacking PE slices should be updated as a condition change
120+
type PolicyEndpointCondition struct {
121+
// Type of PolicyEndpoint condition.
122+
// +optional
123+
Type PolicyEndpointConditionType `json:"type"`
124+
// Status of the condition, one of True, False, Unknown.
125+
// +optional
126+
Status corev1.ConditionStatus `json:"status"`
127+
// Last time the condition transitioned from one status to another.
128+
// +optional
129+
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
130+
// The reason for the condition's last transition.
131+
// +optional
132+
Reason string `json:"reason,omitempty"`
133+
// A human readable message indicating details about the transition.
134+
// +optional
135+
Message string `json:"message,omitempty"`
102136
}
103137

104138
//+kubebuilder:object:root=true

config/crd/bases/networking.k8s.aws_policyendpoints.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ spec:
3535
spec:
3636
description: PolicyEndpointSpec defines the desired state of PolicyEndpoint
3737
properties:
38+
allPodsInNamespace:
39+
description: AllPodsInNameSpace is the boolean value indicating should
40+
all pods in the policy namespace be selected
41+
type: boolean
3842
egress:
3943
description: Egress is the list of egress rules containing resolved
4044
network addresses
@@ -225,6 +229,33 @@ spec:
225229
type: object
226230
status:
227231
description: PolicyEndpointStatus defines the observed state of PolicyEndpoint
232+
properties:
233+
conditions:
234+
items:
235+
description: PolicyEndpointCondition describes the state of a PolicyEndpoint
236+
at a certain point. For example, binpacking PE slices should be
237+
updated as a condition change
238+
properties:
239+
lastTransitionTime:
240+
description: Last time the condition transitioned from one status
241+
to another.
242+
format: date-time
243+
type: string
244+
message:
245+
description: A human readable message indicating details about
246+
the transition.
247+
type: string
248+
reason:
249+
description: The reason for the condition's last transition.
250+
type: string
251+
status:
252+
description: Status of the condition, one of True, False, Unknown.
253+
type: string
254+
type:
255+
description: Type of PolicyEndpoint condition.
256+
type: string
257+
type: object
258+
type: array
228259
type: object
229260
type: object
230261
served: true

pkg/policyendpoints/manager.go

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"context"
55
"crypto/sha256"
66
"encoding/hex"
7+
"fmt"
78
"strconv"
89

910
"golang.org/x/exp/maps"
1011

1112
"github.com/go-logr/logr"
1213
"github.com/pkg/errors"
1314
"github.com/samber/lo"
15+
corev1 "k8s.io/api/core/v1"
1416
networking "k8s.io/api/networking/v1"
1517
"k8s.io/apimachinery/pkg/api/equality"
1618
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -21,6 +23,8 @@ import (
2123
policyinfo "github.com/aws/amazon-network-policy-controller-k8s/api/v1alpha1"
2224
"github.com/aws/amazon-network-policy-controller-k8s/pkg/k8s"
2325
"github.com/aws/amazon-network-policy-controller-k8s/pkg/resolvers"
26+
"github.com/aws/amazon-network-policy-controller-k8s/pkg/utils/conditions"
27+
"github.com/aws/amazon-network-policy-controller-k8s/pkg/utils/conversions"
2428
)
2529

2630
type PolicyEndpointsManager interface {
@@ -39,6 +43,19 @@ func NewPolicyEndpointsManager(k8sClient client.Client, endpointChunkSize int, l
3943
}
4044
}
4145

46+
const (
47+
ingressShift = 2
48+
egressShift = 1
49+
psShift = 0
50+
51+
ingBit = 4
52+
egBit = 2
53+
psBit = 1
54+
55+
reasonBinPacking = "PEBinPacked"
56+
reasonPatching = "PEPatched"
57+
)
58+
4259
var _ PolicyEndpointsManager = (*policyEndpointsManager)(nil)
4360

4461
type policyEndpointsManager struct {
@@ -60,12 +77,9 @@ func (m *policyEndpointsManager) Reconcile(ctx context.Context, policy *networki
6077
client.MatchingFields{IndexKeyPolicyReferenceName: policy.Name}); err != nil {
6178
return err
6279
}
63-
existingPolicyEndpoints := make([]policyinfo.PolicyEndpoint, 0, len(policyEndpointList.Items))
64-
for _, policyEndpoint := range policyEndpointList.Items {
65-
existingPolicyEndpoints = append(existingPolicyEndpoints, policyEndpoint)
66-
}
6780

68-
createList, updateList, deleteList, err := m.computePolicyEndpoints(policy, existingPolicyEndpoints, ingressRules, egressRules, podSelectorEndpoints)
81+
createList, updateList, deleteList, packed, err := m.computePolicyEndpoints(policy, policyEndpointList.Items, ingressRules, egressRules, podSelectorEndpoints)
82+
m.logger.Info("the controller is packing PE rules", "Packed", conversions.IntToBool(packed))
6983
if err != nil {
7084
return err
7185
}
@@ -79,17 +93,42 @@ func (m *policyEndpointsManager) Reconcile(ctx context.Context, policy *networki
7993

8094
for _, policyEndpoint := range updateList {
8195
oldRes := &policyinfo.PolicyEndpoint{}
82-
if err := m.k8sClient.Get(ctx, k8s.NamespacedName(&policyEndpoint), oldRes); err != nil {
96+
peId := k8s.NamespacedName(&policyEndpoint)
97+
if err := m.k8sClient.Get(ctx, peId, oldRes); err != nil {
8398
return err
8499
}
85100
if equality.Semantic.DeepEqual(oldRes.Spec, policyEndpoint.Spec) {
86-
m.logger.V(1).Info("Policy endpoint already up to date", "id", k8s.NamespacedName(&policyEndpoint))
101+
m.logger.V(1).Info("Policy endpoint already up to date", "id", peId)
87102
continue
88103
}
104+
89105
if err := m.k8sClient.Patch(ctx, &policyEndpoint, client.MergeFrom(oldRes)); err != nil {
106+
if cErr := conditions.UpdatePEConditions(ctx, m.k8sClient,
107+
peId,
108+
m.logger,
109+
policyinfo.Updated,
110+
corev1.ConditionFalse,
111+
reasonPatching,
112+
fmt.Sprintf("patching policy endpoint failed: %s", err.Error()),
113+
); cErr != nil {
114+
m.logger.Error(cErr, "Adding PE patch failure condition updates to PE failed", "PENamespacedName", peId)
115+
}
90116
return err
91117
}
92-
m.logger.Info("Updated policy endpoint", "id", k8s.NamespacedName(&policyEndpoint))
118+
m.logger.Info("Updated policy endpoint", "id", peId)
119+
120+
if packed > 0 {
121+
if err := conditions.UpdatePEConditions(ctx, m.k8sClient,
122+
peId,
123+
m.logger,
124+
policyinfo.Packed,
125+
corev1.ConditionTrue,
126+
reasonBinPacking,
127+
fmt.Sprintf("binpacked network policy endpoint slices on Ingress - %t, Egress - %t, PodSelector - %t", packed&ingBit>>ingressShift == 1, packed&egBit>>egressShift == 1, packed&psBit>>psShift == 1),
128+
); err != nil {
129+
m.logger.Error(err, "Adding bingpacking condition updates to PE failed", "PENamespacedName", peId)
130+
}
131+
}
93132
}
94133

95134
for _, policyEndpoint := range deleteList {
@@ -123,7 +162,7 @@ func (m *policyEndpointsManager) Cleanup(ctx context.Context, policy *networking
123162
func (m *policyEndpointsManager) computePolicyEndpoints(policy *networking.NetworkPolicy,
124163
existingPolicyEndpoints []policyinfo.PolicyEndpoint, ingressEndpoints []policyinfo.EndpointInfo,
125164
egressEndpoints []policyinfo.EndpointInfo, podSelectorEndpoints []policyinfo.PodEndpoint) ([]policyinfo.PolicyEndpoint,
126-
[]policyinfo.PolicyEndpoint, []policyinfo.PolicyEndpoint, error) {
165+
[]policyinfo.PolicyEndpoint, []policyinfo.PolicyEndpoint, int, error) {
127166

128167
// Loop through ingressEndpoints, egressEndpoints and podSelectorEndpoints and put in map
129168
// also populate them into policy endpoints
@@ -138,11 +177,11 @@ func (m *policyEndpointsManager) computePolicyEndpoints(policy *networking.Netwo
138177
var deletePolicyEndpoints []policyinfo.PolicyEndpoint
139178

140179
// packing new ingress rules
141-
createPolicyEndpoints, doNotDeleteIngress := m.packingIngressRules(policy, ingressEndpointsMap, createPolicyEndpoints, modifiedEndpoints, potentialDeletes)
180+
createPolicyEndpoints, doNotDeleteIngress, ingPacked := m.packingIngressRules(policy, ingressEndpointsMap, createPolicyEndpoints, modifiedEndpoints, potentialDeletes)
142181
// packing new egress rules
143-
createPolicyEndpoints, doNotDeleteEgress := m.packingEgressRules(policy, egressEndpointsMap, createPolicyEndpoints, modifiedEndpoints, potentialDeletes)
182+
createPolicyEndpoints, doNotDeleteEgress, egPacked := m.packingEgressRules(policy, egressEndpointsMap, createPolicyEndpoints, modifiedEndpoints, potentialDeletes)
144183
// packing new pod selector
145-
createPolicyEndpoints, doNotDeletePs := m.packingPodSelectorEndpoints(policy, podSelectorEndpointSet.UnsortedList(), createPolicyEndpoints, modifiedEndpoints, potentialDeletes)
184+
createPolicyEndpoints, doNotDeletePs, psPacked := m.packingPodSelectorEndpoints(policy, podSelectorEndpointSet.UnsortedList(), createPolicyEndpoints, modifiedEndpoints, potentialDeletes)
146185

147186
doNotDelete.Insert(doNotDeleteIngress.UnsortedList()...)
148187
doNotDelete.Insert(doNotDeleteEgress.UnsortedList()...)
@@ -167,7 +206,7 @@ func (m *policyEndpointsManager) computePolicyEndpoints(policy *networking.Netwo
167206
}
168207
}
169208

170-
return createPolicyEndpoints, updatePolicyEndpoints, deletePolicyEndpoints, nil
209+
return createPolicyEndpoints, updatePolicyEndpoints, deletePolicyEndpoints, (conversions.BoolToint(ingPacked) << ingressShift) | (conversions.BoolToint(egPacked) << egressShift) | (conversions.BoolToint(psPacked) << psShift), nil
171210
}
172211

173212
func (m *policyEndpointsManager) newPolicyEndpoint(policy *networking.NetworkPolicy,
@@ -202,6 +241,13 @@ func (m *policyEndpointsManager) newPolicyEndpoint(policy *networking.NetworkPol
202241
Egress: egressRules,
203242
},
204243
}
244+
245+
// if no pod selector is specified, the controller adds a boolean value true to AllPodsInNamespace
246+
if policy.Spec.PodSelector.Size() == 0 {
247+
m.logger.Info("Creating a new PE but requested NP doesn't have pod selector", "NPName", policy.Name, "NPNamespace", policy.Namespace)
248+
policyEndpoint.Spec.AllPodsInNamespace = true
249+
}
250+
205251
return policyEndpoint
206252
}
207253

@@ -319,24 +365,32 @@ func (m *policyEndpointsManager) processExistingPolicyEndpoints(
319365
// it returns the ingress rules packed in policy endpoints and a set of policy endpoints that need to be kept.
320366
func (m *policyEndpointsManager) packingIngressRules(policy *networking.NetworkPolicy,
321367
rulesMap map[string]policyinfo.EndpointInfo,
322-
createPolicyEndpoints, modifiedEndpoints, potentialDeletes []policyinfo.PolicyEndpoint) ([]policyinfo.PolicyEndpoint, sets.Set[types.NamespacedName]) {
368+
createPolicyEndpoints, modifiedEndpoints, potentialDeletes []policyinfo.PolicyEndpoint) ([]policyinfo.PolicyEndpoint, sets.Set[types.NamespacedName], bool) {
323369
doNotDelete := sets.Set[types.NamespacedName]{}
324370
chunkStartIdx := 0
325371
chunkEndIdx := 0
326372
ingressList := maps.Keys(rulesMap)
327373

374+
packed := false
375+
328376
// try to fill existing polciy endpoints first and then new ones if needed
329377
for _, sliceToCheck := range [][]policyinfo.PolicyEndpoint{modifiedEndpoints, potentialDeletes, createPolicyEndpoints} {
330378
for i := range sliceToCheck {
331379
// reset start pointer if end pointer is updated
332380
chunkStartIdx = chunkEndIdx
381+
333382
// Instead of adding the entire chunk we should try to add to full the slice
334-
if len(sliceToCheck[i].Spec.Ingress) < m.endpointChunkSize && chunkEndIdx < len(ingressList) {
383+
// when new ingress rule list is greater than available spots in current non-empty PE rule's list, we do binpacking
384+
spots := m.endpointChunkSize - len(sliceToCheck[i].Spec.Ingress)
385+
packed = spots > 0 && len(sliceToCheck[i].Spec.Ingress) > 0 && spots < len(ingressList)
386+
387+
if spots > 0 && chunkEndIdx < len(ingressList) {
335388
for len(sliceToCheck[i].Spec.Ingress)+(chunkEndIdx-chunkStartIdx+1) < m.endpointChunkSize && chunkEndIdx < len(ingressList)-1 {
336389
chunkEndIdx++
337390
}
338391

339392
sliceToCheck[i].Spec.Ingress = append(sliceToCheck[i].Spec.Ingress, m.getListOfEndpointInfoFromHash(lo.Slice(ingressList, chunkStartIdx, chunkEndIdx+1), rulesMap)...)
393+
340394
// move the end to next available index to prepare next appending
341395
chunkEndIdx++
342396
}
@@ -355,26 +409,33 @@ func (m *policyEndpointsManager) packingIngressRules(policy *networking.NetworkP
355409
createPolicyEndpoints = append(createPolicyEndpoints, newEP)
356410
}
357411
}
358-
return createPolicyEndpoints, doNotDelete
412+
return createPolicyEndpoints, doNotDelete, packed
359413
}
360414

361415
// packingEgressRules iterates over egress rules across available policy endpoints and required egress rule changes.
362416
// it returns the egress rules packed in policy endpoints and a set of policy endpoints that need to be kept.
363417
func (m *policyEndpointsManager) packingEgressRules(policy *networking.NetworkPolicy,
364418
rulesMap map[string]policyinfo.EndpointInfo,
365-
createPolicyEndpoints, modifiedEndpoints, potentialDeletes []policyinfo.PolicyEndpoint) ([]policyinfo.PolicyEndpoint, sets.Set[types.NamespacedName]) {
419+
createPolicyEndpoints, modifiedEndpoints, potentialDeletes []policyinfo.PolicyEndpoint) ([]policyinfo.PolicyEndpoint, sets.Set[types.NamespacedName], bool) {
366420
doNotDelete := sets.Set[types.NamespacedName]{}
367421
chunkStartIdx := 0
368422
chunkEndIdx := 0
369423
egressList := maps.Keys(rulesMap)
370424

425+
packed := false
426+
371427
// try to fill existing polciy endpoints first and then new ones if needed
372428
for _, sliceToCheck := range [][]policyinfo.PolicyEndpoint{modifiedEndpoints, potentialDeletes, createPolicyEndpoints} {
373429
for i := range sliceToCheck {
374430
// reset start pointer if end pointer is updated
375431
chunkStartIdx = chunkEndIdx
432+
376433
// Instead of adding the entire chunk we should try to add to full the slice
377-
if len(sliceToCheck[i].Spec.Egress) < m.endpointChunkSize && chunkEndIdx < len(egressList) {
434+
// when new egress rule list is greater than available spots in current non-empty PE rule's list, we do binpacking
435+
spots := m.endpointChunkSize - len(sliceToCheck[i].Spec.Egress)
436+
packed = spots > 0 && len(sliceToCheck[i].Spec.Egress) > 0 && spots < len(egressList)
437+
438+
if spots > 0 && chunkEndIdx < len(egressList) {
378439
for len(sliceToCheck[i].Spec.Egress)+(chunkEndIdx-chunkStartIdx+1) < m.endpointChunkSize && chunkEndIdx < len(egressList)-1 {
379440
chunkEndIdx++
380441
}
@@ -398,26 +459,32 @@ func (m *policyEndpointsManager) packingEgressRules(policy *networking.NetworkPo
398459
createPolicyEndpoints = append(createPolicyEndpoints, newEP)
399460
}
400461
}
401-
return createPolicyEndpoints, doNotDelete
462+
return createPolicyEndpoints, doNotDelete, packed
402463
}
403464

404465
// packingPodSelectorEndpoints iterates over pod selectors across available policy endpoints and required pod selector changes.
405466
// it returns the pod selectors packed in policy endpoints and a set of policy endpoints that need to be kept.
406467
func (m *policyEndpointsManager) packingPodSelectorEndpoints(policy *networking.NetworkPolicy,
407468
psList []policyinfo.PodEndpoint,
408-
createPolicyEndpoints, modifiedEndpoints, potentialDeletes []policyinfo.PolicyEndpoint) ([]policyinfo.PolicyEndpoint, sets.Set[types.NamespacedName]) {
469+
createPolicyEndpoints, modifiedEndpoints, potentialDeletes []policyinfo.PolicyEndpoint) ([]policyinfo.PolicyEndpoint, sets.Set[types.NamespacedName], bool) {
409470

410471
doNotDelete := sets.Set[types.NamespacedName]{}
411472
chunkStartIdx := 0
412473
chunkEndIdx := 0
474+
packed := false
413475

414476
// try to fill existing polciy endpoints first and then new ones if needed
415477
for _, sliceToCheck := range [][]policyinfo.PolicyEndpoint{modifiedEndpoints, potentialDeletes, createPolicyEndpoints} {
416478
for i := range sliceToCheck {
417479
// reset start pointer if end pointer is updated
418480
chunkStartIdx = chunkEndIdx
481+
419482
// Instead of adding the entire chunk we should try to add to full the slice
420-
if len(sliceToCheck[i].Spec.PodSelectorEndpoints) < m.endpointChunkSize && chunkEndIdx < len(psList) {
483+
// when new pods list is greater than available spots in current non-empty PS list, we do binpacking
484+
spots := m.endpointChunkSize - len(sliceToCheck[i].Spec.PodSelectorEndpoints)
485+
packed = spots > 0 && len(sliceToCheck[i].Spec.PodSelectorEndpoints) > 0 && spots < len(psList)
486+
487+
if spots > 0 && chunkEndIdx < len(psList) {
421488
for len(sliceToCheck[i].Spec.PodSelectorEndpoints)+(chunkEndIdx-chunkStartIdx+1) < m.endpointChunkSize && chunkEndIdx < len(psList)-1 {
422489
chunkEndIdx++
423490
}
@@ -441,5 +508,5 @@ func (m *policyEndpointsManager) packingPodSelectorEndpoints(policy *networking.
441508
createPolicyEndpoints = append(createPolicyEndpoints, newEP)
442509
}
443510
}
444-
return createPolicyEndpoints, doNotDelete
511+
return createPolicyEndpoints, doNotDelete, packed
445512
}

pkg/policyendpoints/manager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ func Test_policyEndpointsManager_computePolicyEndpoints(t *testing.T) {
448448
m := &policyEndpointsManager{
449449
endpointChunkSize: tt.fields.endpointChunkSize,
450450
}
451-
createList, updateList, deleteList, err := m.computePolicyEndpoints(tt.args.policy, tt.args.policyEndpoints,
451+
createList, updateList, deleteList, _, err := m.computePolicyEndpoints(tt.args.policy, tt.args.policyEndpoints,
452452
tt.args.ingressRules, tt.args.egressRules, tt.args.podselectorEndpoints)
453453

454454
if len(tt.wantErr) > 0 {

0 commit comments

Comments
 (0)