Skip to content

Commit 52d61cb

Browse files
squeedaanm
authored andcommitted
policy: gather policy calculation in the repository
This simplifies the policy calculation flow by centralizing calculations entirely within the policy package. Endpoints no longer need to lock the repository manually; policy calculation manages this itself. Some care is needed to preserve the logic to skip policy revisisions; endpoints are informed when they can skip policy calculations and thus must pass this back down to the repository when calculating policy. Signed-off-by: Casey Callendrello <[email protected]>
1 parent d34358f commit 52d61cb

File tree

4 files changed

+76
-47
lines changed

4 files changed

+76
-47
lines changed

pkg/endpoint/metrics.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ func (s *regenerationStatistics) GetMap() map[string]*spanstat.SpanStat {
9090
return result
9191
}
9292

93+
// used by PolicyRepository.GetSelectorPolicy
94+
func (s *regenerationStatistics) WaitingForPolicyRepository() *spanstat.SpanStat {
95+
return &s.waitingForPolicyRepository
96+
}
97+
98+
// used by PolicyRepository.GetSelectorPolicy
99+
func (s *regenerationStatistics) PolicyCalculation() *spanstat.SpanStat {
100+
return &s.policyCalculation
101+
}
102+
93103
// endpointPolicyStatusMap is a map to store the endpoint id and the policy
94104
// enforcement status. It is used only to send metrics to prometheus.
95105
type endpointPolicyStatusMap struct {

pkg/endpoint/policy.go

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -190,48 +190,28 @@ func (e *Endpoint) regeneratePolicy(stats *regenerationStatistics, datapathRegen
190190
e.unlock()
191191

192192
e.getLogger().Debug("Starting policy recalculation...")
193-
194-
stats.waitingForPolicyRepository.Start()
195-
repo := e.policyGetter.GetPolicyRepository()
196-
repo.RLock() // Be sure to release this lock!
197-
stats.waitingForPolicyRepository.End(true)
198-
199-
result.policyRevision = repo.GetRevision()
200-
201-
// Recompute policy for this endpoint only if not already done for this revision
202-
// and identity.
203-
if e.nextPolicyRevision >= result.policyRevision && e.desiredPolicy != nil {
204-
205-
if !forcePolicyCompute {
206-
if logger := e.getLogger(); logging.CanLogAt(logger.Logger, logrus.DebugLevel) {
207-
e.getLogger().WithFields(logrus.Fields{
208-
"policyRevision.next": e.nextPolicyRevision,
209-
"policyRevision.repo": result.policyRevision,
210-
"policyChanged": e.nextPolicyRevision > e.policyRevision,
211-
}).Debug("Skipping unnecessary endpoint policy recalculation")
212-
}
213-
repo.RUnlock()
214-
return result, nil
215-
} else {
216-
e.getLogger().Debug("Forced policy recalculation")
217-
}
193+
skipPolicyRevision := e.nextPolicyRevision
194+
if forcePolicyCompute || e.desiredPolicy == nil {
195+
e.getLogger().Debug("Forced policy recalculation")
196+
skipPolicyRevision = 0
218197
}
219198

220-
stats.policyCalculation.Start()
221-
defer func() { stats.policyCalculation.End(err == nil) }()
222-
223-
// UpdatePolicy ensures the SelectorPolicy is fully resolved.
224-
// Endpoint lock must not be held!
225-
// TODO: GH-7515: Consider ways to compute policy outside of the
226-
// endpoint regeneration process, ideally as part of the policy change
227-
// handler.
228-
selectorPolicy, err := repo.GetPolicyCache().UpdatePolicy(securityIdentity)
199+
var selectorPolicy policy.SelectorPolicy
200+
selectorPolicy, result.policyRevision, err = e.policyGetter.GetPolicyRepository().GetSelectorPolicy(securityIdentity, skipPolicyRevision, stats)
229201
if err != nil {
230-
e.getLogger().WithError(err).Warning("Failed to update policy")
231-
repo.RUnlock()
202+
e.getLogger().WithError(err).Warning("Failed to calculate SelectorPolicy")
232203
return nil, err
233204
}
234-
repo.RUnlock() // Done with policy repository; release this now as Consume() can be slow
205+
206+
// selectorPolicy is nil if skipRevision was matched.
207+
if selectorPolicy == nil {
208+
e.getLogger().WithFields(logrus.Fields{
209+
"policyRevision.next": e.nextPolicyRevision,
210+
"policyRevision.repo": result.policyRevision,
211+
"policyChanged": e.nextPolicyRevision > e.policyRevision,
212+
}).Debug("Skipping unnecessary endpoint policy recalculation")
213+
return result, err
214+
}
235215

236216
// Add new redirects before Consume() so that all required proxy ports are available for it.
237217
var desiredRedirects map[string]uint16

pkg/policy/distillery.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,6 @@ func (cache *PolicyCache) LocalEndpointIdentityRemoved(identity *identityPkg.Ide
112112
cache.delete(identity)
113113
}
114114

115-
// UpdatePolicy resolves the policy for the security identity of the specified
116-
// endpoint and caches it for future use.
117-
//
118-
// The caller must provide threadsafety for iteration over the policy
119-
// repository.
120-
func (cache *PolicyCache) UpdatePolicy(identity *identityPkg.Identity) (SelectorPolicy, error) {
121-
sp, _, err := cache.updateSelectorPolicy(identity)
122-
return sp, err
123-
}
124-
125115
// GetAuthTypes returns the AuthTypes required by the policy between the localID and remoteID, if
126116
// any, otherwise returns nil.
127117
func (cache *PolicyCache) GetAuthTypes(localID, remoteID identityPkg.NumericIdentity) AuthTypes {

pkg/policy/repository.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/cilium/cilium/pkg/metrics"
2929
"github.com/cilium/cilium/pkg/option"
3030
"github.com/cilium/cilium/pkg/policy/api"
31+
"github.com/cilium/cilium/pkg/spanstat"
3132
)
3233

3334
// PolicyContext is an interface policy resolution functions use to access the Repository.
@@ -125,6 +126,15 @@ type PolicyRepository interface {
125126
GetAuthTypes(localID identity.NumericIdentity, remoteID identity.NumericIdentity) AuthTypes
126127
GetEnvoyHTTPRules(l7Rules *api.L7Rules, ns string) (*cilium.HttpNetworkPolicyRules, bool)
127128
GetPolicyCache() *PolicyCache
129+
130+
// GetSelectorPolicy computes the SelectorPolicy for a given identity.
131+
//
132+
// It returns nil if skipRevision is >= than the already calculated version.
133+
// This is used to skip policy calculation when a certain revision delta is
134+
// known to not affect the given identity. Pass a skipRevision of 0 to force
135+
// calculation.
136+
GetSelectorPolicy(id *identity.Identity, skipRevision uint64, stats GetPolicyStatistics) (SelectorPolicy, uint64, error)
137+
128138
GetRevision() uint64
129139
GetRulesList() *models.Policy
130140
GetSelectorCache() *SelectorCache
@@ -138,6 +148,11 @@ type PolicyRepository interface {
138148
Start()
139149
}
140150

151+
type GetPolicyStatistics interface {
152+
WaitingForPolicyRepository() *spanstat.SpanStat
153+
PolicyCalculation() *spanstat.SpanStat
154+
}
155+
141156
// Repository is a list of policy rules which in combination form the security
142157
// policy. A policy repository can be
143158
type Repository struct {
@@ -930,3 +945,37 @@ func wildcardRule(lbls labels.LabelArray, ingress bool) *rule {
930945

931946
return r
932947
}
948+
949+
// GetSelectorPolicy computes the SelectorPolicy for a given identity.
950+
//
951+
// It returns nil if skipRevision is >= than the already calculated version.
952+
// This is used to skip policy calculation when a certain revision delta is
953+
// known to not affect the given identity. Pass a skipRevision of 0 to force
954+
// calculation.
955+
func (r *Repository) GetSelectorPolicy(id *identity.Identity, skipRevision uint64, stats GetPolicyStatistics) (SelectorPolicy, uint64, error) {
956+
stats.WaitingForPolicyRepository().Start()
957+
r.RLock()
958+
defer r.RUnlock()
959+
stats.WaitingForPolicyRepository().End(true)
960+
961+
rev := r.GetRevision()
962+
963+
// Do we already have a given revision?
964+
// If so, skip calculation.
965+
if skipRevision >= rev {
966+
return nil, rev, nil
967+
}
968+
969+
stats.PolicyCalculation().Start()
970+
// This may call back in to the (locked) repository to generate the
971+
// selector policy
972+
sp, updated, err := r.policyCache.updateSelectorPolicy(id)
973+
stats.PolicyCalculation().EndError(err)
974+
975+
// If we hit cache, reset the statistics.
976+
if !updated {
977+
stats.PolicyCalculation().Reset()
978+
}
979+
980+
return sp, rev, nil
981+
}

0 commit comments

Comments
 (0)