Skip to content

Commit 98a0609

Browse files
authored
Merge Fuse with Learn (#120)
* record numSamples * removed fuse * removed fuse * rm fuse in envelop * fix e2e test * revert e2e crd * better log for loadConfig * fixed Mutex issues * fixed Mutex issues
1 parent 5993e05 commit 98a0609

26 files changed

+97
-386
lines changed

cmd/guard-service/services.go

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ import (
2424
pi "knative.dev/security-guard/pkg/pluginterfaces"
2525
)
2626

27-
var maxPileCount = uint32(1000)
27+
const (
28+
pileMergeLimit = uint32(1000)
29+
numSamplesLimit = uint32(1000000)
30+
)
2831

2932
// A cached record kept by guard-service for each deployed service
3033
type serviceRecord struct {
@@ -72,7 +75,6 @@ func (s *services) tick() {
7275
// Tick should not include any asynchronous work
7376
// Move all asynchronous work (e.g. KubeApi work) to go routines
7477
s.mutex.Lock()
75-
defer s.mutex.Unlock()
7678

7779
if len(s.tickerKeys) == 0 {
7880
// Assign more work to be done now and in future ticks
@@ -90,18 +92,28 @@ func (s *services) tick() {
9092
maxIterations = 100
9193
}
9294

93-
// try to learn one record
94-
i := 0
95+
// find a record to learn
96+
i := 0 // i is the index of the record to learn
97+
var record *serviceRecord
9598
for ; i < maxIterations; i++ {
96-
if record, exists := s.cache[s.tickerKeys[i]]; exists {
97-
// we still have this record, lets learn it
98-
if s.learnPile(record) {
99-
// we learned one record
99+
r, exists := s.cache[s.tickerKeys[i]]
100+
if exists {
101+
if r.pile.Count != 0 {
102+
// we will learn this record!
103+
record = r
104+
// (during the next tick we should try the next one)
100105
i++
101106
break
102107
}
103108
}
104109
}
110+
s.mutex.Unlock()
111+
// Must unlock s.mutex before s.learnPile
112+
113+
if record != nil {
114+
// lets learn it
115+
s.learnPile(record)
116+
}
105117

106118
// remove the keys we processed from the key slice
107119
s.tickerKeys = s.tickerKeys[i:]
@@ -132,6 +144,7 @@ func (s *services) get(ns string, sid string, cmFlag bool) *serviceRecord {
132144
// try to get from cache
133145
record := s.cache[service]
134146
s.mutex.Unlock()
147+
// Must unlock s.mutex before s.kmgr.Watch, s.kmgr.GetGuardian, s.set
135148

136149
// watch any unknown namespace
137150
if !knownNamespace {
@@ -153,6 +166,7 @@ func (s *services) set(ns string, sid string, cmFlag bool, guardianSpec *spec.Gu
153166
service := serviceKey(ns, sid, cmFlag)
154167

155168
s.mutex.Lock()
169+
defer s.mutex.Unlock()
156170
record, exists := s.cache[service]
157171
if !exists {
158172
record = new(serviceRecord)
@@ -162,7 +176,6 @@ func (s *services) set(ns string, sid string, cmFlag bool, guardianSpec *spec.Gu
162176
record.cmFlag = cmFlag
163177
s.cache[service] = record
164178
}
165-
s.mutex.Unlock()
166179

167180
record.guardianSpec = guardianSpec
168181
pi.Log.Debugf("cache record for %s.%s", ns, sid)
@@ -184,37 +197,33 @@ func (s *services) merge(record *serviceRecord, pile *spec.SessionDataPile) {
184197
record.pileMutex.Lock()
185198
record.pile.Merge(pile)
186199
record.pileMutex.Unlock()
187-
if record.pile.Count > maxPileCount {
200+
// Must unlock pileMutex before s.learnPile
201+
202+
if record.pile.Count > pileMergeLimit {
188203
s.learnPile(record)
189204
}
190205
}
191206

192207
// update the record guardianSpec by learning a new config and fusing with the record existing config
193208
// update KubeAPI as well.
194209
// return true if we try to learn and access kubeApi, false if count is zero and we have nothing to do
195-
func (s *services) learnPile(record *serviceRecord) bool {
196-
if record.pile.Count == 0 {
197-
return false
210+
func (s *services) learnPile(record *serviceRecord) {
211+
if record.guardianSpec.Learned == nil {
212+
record.guardianSpec.Learned = new(spec.SessionDataConfig)
198213
}
199-
config := new(spec.SessionDataConfig)
200214

201215
record.pileMutex.Lock()
202-
config.Learn(&record.pile)
216+
record.guardianSpec.Learned.Learn(&record.pile)
217+
record.guardianSpec.NumSamples += record.pile.Count
218+
if record.guardianSpec.NumSamples > numSamplesLimit {
219+
record.guardianSpec.NumSamples = numSamplesLimit
220+
}
203221
record.pile.Clear()
204222
record.pileMutex.Unlock()
205-
206-
if record.guardianSpec.Learned != nil {
207-
config.Fuse(record.guardianSpec.Learned)
208-
}
209-
210-
// update the cached record
211-
record.guardianSpec.Learned = config
212-
record.guardianSpec.Learned.Active = true
223+
// Must unlock record.pileMutex before s.persist
213224

214225
// update the kubeApi record
215226
go s.persist(record)
216-
217-
return true
218227
}
219228

220229
func (s *services) persist(record *serviceRecord) {

pkg/apis/guard/v1alpha1/asciiFlags.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,16 +134,7 @@ func (config *AsciiFlagsConfig) learnI(valPile ValuePile) {
134134

135135
// pile is RO and unchanged - never uses pile internal objects
136136
func (config *AsciiFlagsConfig) Learn(pile AsciiFlagsPile) {
137-
*config = AsciiFlagsConfig(pile)
138-
}
139-
140-
func (config *AsciiFlagsConfig) fuseI(otherValConfig ValueConfig) {
141-
config.Fuse(*otherValConfig.(*AsciiFlagsConfig))
142-
}
143-
144-
// otherConfig is RO and unchanged - never uses otherConfig internal objects
145-
func (config *AsciiFlagsConfig) Fuse(otherConfig AsciiFlagsConfig) {
146-
*config |= otherConfig
137+
*config |= AsciiFlagsConfig(pile)
147138
}
148139

149140
func (config *AsciiFlagsConfig) Prepare() {

pkg/apis/guard/v1alpha1/body.go

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -150,26 +150,6 @@ func (config *BodyConfig) Learn(pile *BodyPile) {
150150
}
151151
}
152152

153-
func (config *BodyConfig) fuseI(otherValConfig ValueConfig) {
154-
config.Fuse(otherValConfig.(*BodyConfig))
155-
}
156-
157-
// otherConfig is RO and unchanged - never uses otherConfig internal objects
158-
func (config *BodyConfig) Fuse(otherConfig *BodyConfig) {
159-
if otherConfig.Structured != nil {
160-
if config.Structured == nil {
161-
config.Structured = new(StructuredConfig)
162-
}
163-
config.Structured.Fuse(otherConfig.Structured)
164-
}
165-
if otherConfig.Unstructured != nil {
166-
if config.Unstructured == nil {
167-
config.Unstructured = new(SimpleValConfig)
168-
}
169-
config.Unstructured.Fuse(otherConfig.Unstructured)
170-
}
171-
}
172-
173153
func (config *BodyConfig) Prepare() {
174154
if config.Structured != nil {
175155
config.Structured.Prepare()

pkg/apis/guard/v1alpha1/count.go

Lines changed: 15 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,6 @@ type countRange struct {
6262
Max uint8 `json:"max"`
6363
}
6464

65-
func (cRange *countRange) fuseTwoRanges(otherRange *countRange) bool {
66-
if cRange.Max < otherRange.Min || cRange.Min > otherRange.Max {
67-
// no overlap - nothing to do!
68-
return false
69-
}
70-
71-
// There is overlap of some sort
72-
if cRange.Min > otherRange.Min {
73-
cRange.Min = otherRange.Min
74-
}
75-
if cRange.Max < otherRange.Max {
76-
cRange.Max = otherRange.Max
77-
}
78-
return true
79-
}
80-
8165
// Exposes ValueConfig interface
8266
type CountConfig []countRange
8367

@@ -116,14 +100,14 @@ func (config *CountConfig) learnI(valPile ValuePile) {
116100

117101
// Learn now offers the simplest single rule support
118102
// pile is RO and unchanged - never uses pile internal objects
119-
// Future: Improve Learn
103+
// Future: Improve Learn - e.g. by supporting more then one range
120104
func (config *CountConfig) Learn(pile CountPile) {
121-
min := uint8(0)
122-
max := uint8(0)
123-
if len(pile) > 0 {
124-
min = pile[0]
125-
max = pile[0]
105+
if len(pile) == 0 {
106+
return
126107
}
108+
109+
min := pile[0]
110+
max := pile[0]
127111
for _, v := range pile {
128112
if min > v {
129113
min = v
@@ -132,33 +116,17 @@ func (config *CountConfig) Learn(pile CountPile) {
132116
max = v
133117
}
134118
}
135-
*config = append(*config, countRange{min, max})
136-
}
137119

138-
func (config *CountConfig) fuseI(otherValConfig ValueConfig) {
139-
config.Fuse(*otherValConfig.(*CountConfig))
140-
}
120+
if *config == nil {
121+
*config = append(*config, countRange{min, max})
122+
return
123+
}
141124

142-
// Fuse CountConfig in-place
143-
// otherValConfig is RO and unchanged - never uses otherValConfig internal objects
144-
// The implementation look to opportunistically merge new entries to existing ones
145-
// The implementation does now squash entries even if after the Fuse they may be squashed
146-
// This is done to achieve Fuse in-place
147-
// Future: Improve Fuse - e.g. by keeping extra entries in Range [0,0] and reusing them instead of adding new entries
148-
func (config *CountConfig) Fuse(otherConfig CountConfig) {
149-
var fused bool
150-
for _, other := range otherConfig {
151-
fused = false
152-
for idx, this := range *config {
153-
if fused = this.fuseTwoRanges(&other); fused {
154-
(*config)[idx] = this
155-
break
156-
}
157-
}
158-
if !fused {
159-
// Creating new countRange avoids both objects pointing to the same object
160-
*config = append(*config, countRange{other.Min, other.Max})
161-
}
125+
if min < (*config)[0].Min {
126+
(*config)[0].Min = min
127+
}
128+
if max > (*config)[0].Max {
129+
(*config)[0].Max = max
162130
}
163131
}
164132

pkg/apis/guard/v1alpha1/envelop.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,6 @@ func (config *EnvelopConfig) Learn(pile *EnvelopPile) {
102102
config.ResponseTime.Learn(pile.ResponseTime)
103103
}
104104

105-
func (config *EnvelopConfig) fuseI(otherValConfig ValueConfig) {
106-
config.Fuse(otherValConfig.(*EnvelopConfig))
107-
}
108-
109-
func (config *EnvelopConfig) Fuse(otherConfig *EnvelopConfig) {
110-
config.CompletionTime.Fuse(&otherConfig.CompletionTime)
111-
config.ResponseTime.Fuse(&otherConfig.ResponseTime)
112-
}
113-
114105
func (config *EnvelopConfig) Prepare() {
115106
config.CompletionTime.Prepare()
116107
config.ResponseTime.Prepare()

pkg/apis/guard/v1alpha1/flagSlice.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,7 @@ func (config *FlagSliceConfig) learnI(valPile ValuePile) {
101101

102102
// otherPile is RO and unchanged - never uses otherPile internal objects
103103
func (config *FlagSliceConfig) Learn(pile FlagSlicePile) {
104-
*config = make(FlagSliceConfig, len(pile))
105-
copy(*config, pile)
106-
}
107-
108-
func (config *FlagSliceConfig) fuseI(otherValConfig ValueConfig) {
109-
config.Fuse(*otherValConfig.(*FlagSliceConfig))
110-
}
111-
112-
// otherConfig is RO and unchanged - never uses otherConfig internal objects
113-
func (config *FlagSliceConfig) Fuse(otherConfig FlagSliceConfig) {
114-
*config = mergeFlagSlices(*config, otherConfig)
104+
*config = mergeFlagSlices(*config, pile)
115105
}
116106

117107
func (config *FlagSliceConfig) Prepare() {

pkg/apis/guard/v1alpha1/guardianSpec.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type Ctrl struct {
2727
type GuardianSpec struct {
2828
Configured *SessionDataConfig `json:"configured"` // configrued criteria
2929
Learned *SessionDataConfig `json:"learned,omitempty"` // Learned citeria
30+
NumSamples uint32 `json:"samples"` // Number of Samples Learned
3031
Control *Ctrl `json:"control"` // Control
3132
}
3233

pkg/apis/guard/v1alpha1/httpHeaders.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,6 @@ func (config *HeadersConfig) Learn(pile *HeadersPile) {
9393
config.Kv.Learn(pile.Kv)
9494
}
9595

96-
func (config *HeadersConfig) fuseI(otherValConfig ValueConfig) {
97-
config.Fuse(otherValConfig.(*HeadersConfig))
98-
}
99-
100-
func (config *HeadersConfig) Fuse(otherConfig *HeadersConfig) {
101-
config.Kv.Fuse(&otherConfig.Kv)
102-
}
103-
10496
func (config *HeadersConfig) Prepare() {
10597
config.Kv.Prepare()
10698
}

pkg/apis/guard/v1alpha1/httpMediaType.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,6 @@ func (config *MediaTypeConfig) Learn(pile *MediaTypePile) {
104104
config.Params.Learn(&pile.Params)
105105
}
106106

107-
func (config *MediaTypeConfig) fuseI(otherValConfig ValueConfig) {
108-
config.Fuse(otherValConfig.(*MediaTypeConfig))
109-
}
110-
111-
func (config *MediaTypeConfig) Fuse(otherConfig *MediaTypeConfig) {
112-
config.TypeTokens.Fuse(&otherConfig.TypeTokens)
113-
config.Params.Fuse(&otherConfig.Params)
114-
}
115-
116107
func (config *MediaTypeConfig) Prepare() {
117108
config.TypeTokens.Prepare()
118109
config.Params.Prepare()

pkg/apis/guard/v1alpha1/httpReq.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -180,22 +180,6 @@ func (config *ReqConfig) Learn(pile *ReqPile) {
180180
config.Url.Learn(&pile.Url)
181181
}
182182

183-
func (config *ReqConfig) fuseI(otherValConfig ValueConfig) {
184-
config.Fuse(otherValConfig.(*ReqConfig))
185-
}
186-
187-
func (config *ReqConfig) Fuse(otherConfig *ReqConfig) {
188-
config.ClientIp.Fuse(&otherConfig.ClientIp)
189-
config.HopIp.Fuse(&otherConfig.HopIp)
190-
config.Method.Fuse(&otherConfig.Method)
191-
config.Proto.Fuse(&otherConfig.Proto)
192-
config.MediaType.Fuse(&otherConfig.MediaType)
193-
config.ContentLength.Fuse(otherConfig.ContentLength)
194-
config.Headers.Fuse(&otherConfig.Headers)
195-
config.Qs.Fuse(&otherConfig.Qs)
196-
config.Url.Fuse(&otherConfig.Url)
197-
}
198-
199183
func (config *ReqConfig) Prepare() {
200184
config.ClientIp.Prepare()
201185
config.HopIp.Prepare()

0 commit comments

Comments
 (0)