Skip to content

Commit

Permalink
update events configured to watch specific fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Surbhidongaonkar committed Dec 24, 2019
1 parent e8e5db0 commit 8f43633
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 24 deletions.
20 changes: 20 additions & 0 deletions deploy-all-in-one.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ data:
- update
- delete
- error
updateSetting:
includeDiff: true
fields:
- spec
- status
- name: statefulset
namespaces:
include:
Expand All @@ -52,6 +57,11 @@ data:
- update
- delete
- error
updateSetting:
includeDiff: true
fields:
- spec
- status
- name: ingress
namespaces:
include:
Expand Down Expand Up @@ -123,6 +133,11 @@ data:
- update
- delete
- error
updateSetting:
includeDiff: true
fields:
- spec
- status
- name: job
namespaces:
include:
Expand All @@ -134,6 +149,11 @@ data:
- update
- delete
- error
updateSetting:
includeDiff: true
fields:
- spec
- status
- name: role
namespaces:
include:
Expand Down
15 changes: 15 additions & 0 deletions helm/botkube/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ config:
- update
- delete
- error
updateSetting:
includeDiff: true
fields:
- spec
- status
- name: statefulset
namespaces:
include:
Expand All @@ -72,6 +77,11 @@ config:
- update
- delete
- error
updateSetting:
includeDiff: true
fields:
- spec
- status
- name: ingress
namespaces:
include:
Expand Down Expand Up @@ -153,6 +163,11 @@ config:
- update
- delete
- error
updateSetting:
includeDiff: true
fields:
- spec
- status
- name: role
namespaces:
include:
Expand Down
16 changes: 13 additions & 3 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ var Notify = true
// NotifType to change notification type
type NotifType string

// FieldType to specify the resource fields for which to get notification
type FieldType string

// Config structure of configuration yaml file
type Config struct {
Resources []Resource
Expand All @@ -56,9 +59,16 @@ type Config struct {

// Resource contains resources to watch
type Resource struct {
Name string
Namespaces Namespaces
Events []EventType
Name string
Namespaces Namespaces
Events []EventType
UpdateSetting UpdateSetting `yaml:"updateSetting"`
}

//UpdateSetting struct defines updateEvent fields specification
type UpdateSetting struct {
Fields []FieldType
IncludeDiff bool `yaml:"includeDiff"`
}

// Namespaces contains namespaces to include and ignore
Expand Down
11 changes: 10 additions & 1 deletion pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,16 @@ func sendEvent(obj, oldObj interface{}, c *config.Config, notifiers []notify.Not

// check for siginificant Update Events in objects
if eventType == config.UpdateEvent {
updateMsg := utils.Diff(oldObj, obj)
var updateMsg string
// Check if all namespaces allowed
updateSetting, exist := utils.AllowedUpdateEventsMap[utils.KindNS{Resource: kind, Namespace: "all"}]
if !exist {
// Check if specified namespace is allowed
updateSetting, exist = utils.AllowedUpdateEventsMap[utils.KindNS{Resource: kind, Namespace: objectMeta.Namespace}]
}
if exist {
updateMsg = utils.Diff(oldObj, obj, updateSetting)
}
if len(updateMsg) > 0 {
event.Messages = append(event.Messages, updateMsg)
} else {
Expand Down
31 changes: 19 additions & 12 deletions pkg/utils/cmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,51 @@ import (
"strings"

"github.com/google/go-cmp/cmp"
"github.com/infracloudio/botkube/pkg/config"
)

// SpecDiffReporter is a simple custom reporter that only records differences
// detected in Object Spec during comparison.
type SpecDiffReporter struct {
// DiffReporter is a simple custom reporter that records differences
// detected in Object during comparison.
type DiffReporter struct {
field string
path cmp.Path
diffs []string
}

// PushStep custom implements Reporter interface
func (r *SpecDiffReporter) PushStep(ps cmp.PathStep) {
func (r *DiffReporter) PushStep(ps cmp.PathStep) {
r.path = append(r.path, ps)
}

// Report custom implements Reporter interface
func (r *SpecDiffReporter) Report(rs cmp.Result) {
func (r *DiffReporter) Report(rs cmp.Result) {
if !rs.Equal() {
vx, vy := r.path.Last().Values()
path := fmt.Sprintf("%#v", r.path)
if ok := strings.Contains(path, ".Spec."); ok {
if ok := strings.Contains(path, "."+strings.Title(r.field)); ok {
r.diffs = append(r.diffs, fmt.Sprintf("%#v:\n\t-: %+v\n\t+: %+v\n", r.path, vx, vy))
}
}
}

// PopStep custom implements Reporter interface
func (r *SpecDiffReporter) PopStep() {
func (r *DiffReporter) PopStep() {
r.path = r.path[:len(r.path)-1]
}

// String custom implements Reporter interface
func (r *SpecDiffReporter) String() string {
func (r *DiffReporter) String() string {
return strings.Join(r.diffs, "\n")
}

// Diff provides differences between two objects spec
func Diff(x, y interface{}) string {
var r SpecDiffReporter
cmp.Equal(x, y, cmp.Reporter(&r))
return r.String()
func Diff(x, y interface{}, updatesetting config.UpdateSetting) string {
msg := ""
for _, val := range updatesetting.Fields {
var r DiffReporter
r.field = string(val)
cmp.Equal(x, y, cmp.Reporter(&r))
msg = msg + r.String()
}
return msg
}
81 changes: 76 additions & 5 deletions pkg/utils/cmp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ package utils
import (
"fmt"
"testing"

"github.com/infracloudio/botkube/pkg/config"
)

// Object mocks kubernetes objects
type Object struct {
Spec Spec
Other Other
Spec Spec
Status Status
Data Data
Rules Rules
Other Other
}

// Other mocks fileds like MetaData, Status etc in kubernetes objects
Expand All @@ -21,6 +26,21 @@ type Spec struct {
Port int
}

// Status mocks ObjectStatus field in kubernetes object
type Status struct {
Replicas int
}

// Data mocks ObjectData field in kubernetes object like configmap
type Data struct {
Properties string
}

// Rules mocks ObjectRules field in kubernetes object
type Rules struct {
Verbs string
}

// ExpectedDiff struct to generate expected diff
type ExpectedDiff struct {
Path string
Expand All @@ -32,11 +52,13 @@ func TestDiff(t *testing.T) {
tests := map[string]struct {
old Object
new Object
update config.UpdateSetting
expected ExpectedDiff
}{
`Spec Diff`: {
old: Object{Spec: Spec{Port: 81}, Other: Other{Foo: "bar"}},
new: Object{Spec: Spec{Port: 83}, Other: Other{Foo: "bar"}},
old: Object{Spec: Spec{Port: 81}, Other: Other{Foo: "bar"}},
new: Object{Spec: Spec{Port: 83}, Other: Other{Foo: "bar"}},
update: config.UpdateSetting{Fields: []config.FieldType{"Spec"}, IncludeDiff: true},
expected: ExpectedDiff{
Path: "{utils.Object}.Spec.Port",
X: "81",
Expand All @@ -46,13 +68,62 @@ func TestDiff(t *testing.T) {
`Non Spec Diff`: {
old: Object{Spec: Spec{Port: 81}, Other: Other{Foo: "bar"}},
new: Object{Spec: Spec{Port: 81}, Other: Other{Foo: "boo"}},
update: config.UpdateSetting{Fields: []config.FieldType{"metadata"}, IncludeDiff: true},
expected: ExpectedDiff{},
},
`Status Diff`: {
old: Object{Status: Status{Replicas: 1}, Other: Other{Foo: "bar"}},
new: Object{Status: Status{Replicas: 2}, Other: Other{Foo: "bar"}},
update: config.UpdateSetting{Fields: []config.FieldType{"Status"}, IncludeDiff: true},
expected: ExpectedDiff{
Path: "{utils.Object}.Status.Replicas",
X: "1",
Y: "2",
},
},
`Non Status Diff`: {
old: Object{Status: Status{Replicas: 1}, Other: Other{Foo: "bar"}},
new: Object{Status: Status{Replicas: 1}, Other: Other{Foo: "boo"}},
update: config.UpdateSetting{Fields: []config.FieldType{"metadata"}, IncludeDiff: true},
expected: ExpectedDiff{},
},
`Data Diff`: {
old: Object{Data: Data{Properties: "Color: blue"}, Other: Other{Foo: "bar"}},
new: Object{Data: Data{Properties: "Color: red"}, Other: Other{Foo: "bar"}},
update: config.UpdateSetting{Fields: []config.FieldType{"Data"}, IncludeDiff: true},
expected: ExpectedDiff{
Path: "{utils.Object}.Data.Properties",
X: "Color: blue",
Y: "Color: red",
},
},
`Non Data Diff`: {
old: Object{Data: Data{Properties: "Color: blue"}, Other: Other{Foo: "bar"}},
new: Object{Data: Data{Properties: "Color: blue"}, Other: Other{Foo: "boo"}},
update: config.UpdateSetting{Fields: []config.FieldType{"metadata"}, IncludeDiff: true},
expected: ExpectedDiff{},
},
`Rules Diff`: {
old: Object{Rules: Rules{Verbs: "list"}, Other: Other{Foo: "bar"}},
new: Object{Rules: Rules{Verbs: "watch"}, Other: Other{Foo: "bar"}},
update: config.UpdateSetting{Fields: []config.FieldType{"Rules"}, IncludeDiff: true},
expected: ExpectedDiff{
Path: "{utils.Object}.Rules.Verbs",
X: "list",
Y: "watch",
},
},
`Non Rules Diff`: {
old: Object{Rules: Rules{Verbs: "list"}, Other: Other{Foo: "bar"}},
new: Object{Rules: Rules{Verbs: "list"}, Other: Other{Foo: "boo"}},
update: config.UpdateSetting{Fields: []config.FieldType{"metadata"}, IncludeDiff: true},
expected: ExpectedDiff{},
},
}
for name, test := range tests {
name, test := name, test
t.Run(name, func(t *testing.T) {
if actual := Diff(test.old, test.new); actual != test.expected.MockDiff() {
if actual := Diff(test.old, test.new, test.update); actual != test.expected.MockDiff() {
t.Errorf("expected: %+v != actual: %+v\n", test.expected.MockDiff(), actual)
}
})
Expand Down
20 changes: 18 additions & 2 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ var (
ResourceInformerMap map[string]cache.SharedIndexInformer
// AllowedEventKindsMap is a map to filter valid event kinds
AllowedEventKindsMap map[EventKind]bool
// AllowedUpdateEventsMap is a map of resourceand namespace to updateconfig
AllowedUpdateEventsMap map[KindNS]config.UpdateSetting
// KubeClient is a global kubernetes client to communicate to apiserver
KubeClient kubernetes.Interface
// KubeInformerFactory is a global SharedInformerFactory object to watch resources
Expand Down Expand Up @@ -63,6 +65,12 @@ type EventKind struct {
EventType config.EventType
}

// KindNS used in AllowedUpdateEventsMap
type KindNS struct {
Resource string
Namespace string
}

// InitInformerMap initializes helper maps to filter events
func InitInformerMap() {
botkubeConf, err := config.New()
Expand All @@ -86,6 +94,7 @@ func InitInformerMap() {
// Init maps
ResourceInformerMap = make(map[string]cache.SharedIndexInformer)
AllowedEventKindsMap = make(map[EventKind]bool)
AllowedUpdateEventsMap = make(map[KindNS]config.UpdateSetting)

// Informer map
ResourceInformerMap["pod"] = KubeInformerFactory.Core().V1().Pods().Informer()
Expand All @@ -112,7 +121,7 @@ func InitInformerMap() {
ResourceInformerMap["clusterrole"] = KubeInformerFactory.Rbac().V1().ClusterRoles().Informer()
ResourceInformerMap["clusterrolebinding"] = KubeInformerFactory.Rbac().V1().RoleBindings().Informer()

// Allowed event kinds map
// Allowed event kinds map and Allowed Update Events Map
for _, r := range botkubeConf.Resources {
allEvents := false
for _, e := range r.Events {
Expand All @@ -123,6 +132,12 @@ func InitInformerMap() {
for _, ns := range r.Namespaces.Include {
AllowedEventKindsMap[EventKind{Resource: r.Name, Namespace: ns, EventType: e}] = true
}
// AllowedUpdateEventsMap entry is created only for UpdateEvent
if e == config.UpdateEvent {
for _, ns := range r.Namespaces.Include {
AllowedUpdateEventsMap[KindNS{Resource: r.Name, Namespace: ns}] = r.UpdateSetting
}
}
}

// For AllEvent type, add all events to map
Expand All @@ -131,12 +146,13 @@ func InitInformerMap() {
for _, ev := range events {
for _, ns := range r.Namespaces.Include {
AllowedEventKindsMap[EventKind{Resource: r.Name, Namespace: ns, EventType: ev}] = true
AllowedUpdateEventsMap[KindNS{Resource: r.Name, Namespace: ns}] = r.UpdateSetting
}
}
}

}
log.Logger.Infof("Allowed Events - %+v", AllowedEventKindsMap)
log.Logger.Infof("Allowed UpdateEvents - %+v", AllowedUpdateEventsMap)
}

// GetObjectMetaData returns metadata of the given object
Expand Down
Loading

0 comments on commit 8f43633

Please sign in to comment.