Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: watch kubernetes events #296

Merged
merged 26 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0382676
feat: watch kubernetes events
adityathebe Sep 5, 2023
0c1e43b
chore: update getSourceFromEvent test
adityathebe Sep 5, 2023
7fd6d12
feat: impl consuming of the events
adityathebe Sep 5, 2023
f1f76f1
refactor: save changes immediately and then consume the collected in
adityathebe Sep 6, 2023
ddf06f3
wip: setup a way to scrape selected configs by ids
adityathebe Sep 8, 2023
c0be9d9
chore: bump sigs.k8s.io/controller-runtime
adityathebe Sep 11, 2023
99e0387
chore: save results from targetted scrape
adityathebe Sep 11, 2023
8322e53
chore: fix github workflows
adityathebe Sep 11, 2023
73ddb39
fix: Docker image
adityathebe Sep 11, 2023
02ae95d
chore: bump go version in .github/lint.yml
adityathebe Sep 11, 2023
dc11e30
chore: bump duty
adityathebe Sep 12, 2023
88ba276
chore: remove Involved object definition.
adityathebe Sep 12, 2023
91e4a5d
refactor: scrape uses config directly instead of a config index
adityathebe Sep 12, 2023
14eb68b
feat: new format for exclusions
adityathebe Sep 13, 2023
b762efa
chore: bump commons
adityathebe Sep 13, 2023
ee6a55e
chore: go mod tidy on hack
adityathebe Sep 13, 2023
3951c13
refactor: use mapstructure to decode events instead of json
adityathebe Sep 14, 2023
d181711
feat: directly use the kubernetes scraper and remove TargettedScraper
adityathebe Sep 14, 2023
2de4c0e
chore: create different method to generate event from map.
adityathebe Sep 14, 2023
1f5a7ca
refactor: move KubernetsEvent to v1 so Filter() can take event object as
adityathebe Sep 14, 2023
5deb67c
feat: enable retry when watching events
adityathebe Sep 14, 2023
5a50c1b
Merge branch 'main' into feat/watch-kubernetes-events
adityathebe Oct 5, 2023
c1ed6b0
chore: bump gomplate
adityathebe Oct 5, 2023
457a670
Merge branch 'main' into feat/watch-kubernetes-events
adityathebe Jan 23, 2024
86235d9
chore: cleanup
adityathebe Jan 23, 2024
68a00a6
Merge branch 'main' into feat/watch-kubernetes-events
adityathebe Jan 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Dockerfile
.bin/
.vscode/
.idea/
.env
.DS_Store
cover.out
test.test
build/
.config-db
.github
9 changes: 5 additions & 4 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
- uses: actions/checkout@v4
- name: Install Go
uses: actions/setup-go@bfdd3570ce990073878bf10f6b2d79082de49492 # v2.2.0
uses: actions/setup-go@v4
with:
go-version: 1.19.x
go-version: 1.20.x
- name: golangci-lint
uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 # v3.4.0
uses: golangci/golangci-lint-action@v3
with:
version: v1.54
args: --timeout 10m
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@bfdd3570ce990073878bf10f6b2d79082de49492 # v2.2.0
uses: actions/setup-go@v4
with:
go-version: 1.19.x
go-version: 1.20.x
- name: Checkout code
uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
uses: actions/checkout@v4
- uses: actions/cache@8492260343ad570701412c2f464a5877dc76bace # v2
with:
path: |
Expand Down
8 changes: 3 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
FROM golang:1.19@sha256:8cefba2710250b21a8b8e32281788c5b53dc561ba0c51ea7de92b9a350663b7d as builder
FROM golang:1.20@sha256:bc5f0b5e43282627279fe5262ae275fecb3d2eae3b33977a7fd200c7a760d6f1 as builder
WORKDIR /app
COPY ./ ./

ARG VERSION
COPY go.mod /app/go.mod
COPY go.sum /app/go.sum
RUN go mod download
WORKDIR /app
RUN go version
COPY ./ ./
RUN make build

FROM ubuntu:bionic@sha256:14f1045816502e16fcbfc0b2a76747e9f5e40bc3899f8cfe20745abaafeaeab3
FROM ubuntu:jammy@sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508
WORKDIR /app

# install CA certificates
Expand Down
7 changes: 7 additions & 0 deletions api/v1/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ type Scraper interface {
CanScrape(config ScraperSpec) bool
}

// TargettedScraper can additionally scrape a predefined set of configs.
// +kubebuilder:object:generate=false
type TargettedScraper interface {
Scraper
ScrapeSome(ctx *ScrapeContext, config any, ids []string) ScrapeResults
}

// Analyzer ...
// +kubebuilder:object:generate=false
type Analyzer func(configs []ScrapeResult) AnalysisResult
Expand Down
110 changes: 94 additions & 16 deletions api/v1/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package v1
import (
"strings"

"github.com/flanksource/commons/collections"
"github.com/flanksource/duty/types"
)

Expand All @@ -13,27 +14,104 @@ type SeverityKeywords struct {
Error []string `json:"error,omitempty"`
}

type KubernetesEventExclusions struct {
Names []string `json:"name" yaml:"name"`
Namespaces []string `json:"namespace" yaml:"namespace"`
Reasons []string `json:"reason" yaml:"reason"`
}

// Filter returns true if the given input matches any of the exclusions.
func (t *KubernetesEventExclusions) Filter(name, namespace, reason string) bool {
if name != "" && len(t.Names) != 0 {
if collections.MatchItems(name, t.Names...) {
return true
}
}

if namespace != "" && len(t.Namespaces) != 0 {
if collections.MatchItems(namespace, t.Namespaces...) {
return true
}
}

if reason != "" && len(t.Reasons) != 0 {
if collections.MatchItems(reason, t.Reasons...) {
return true
}
}

return false
}

type KubernetesEvent struct {
// Exclusions is a list of keywords that'll be used to exclude
// event objects based on the reason.
Exclusions []string `json:"exclusions,omitempty"`
SeverityKeywords SeverityKeywords `json:"severityKeywords,omitempty"`
Exclusions KubernetesEventExclusions `json:"exclusions,omitempty"`
SeverityKeywords SeverityKeywords `json:"severityKeywords,omitempty"`
}

type KubernetesConfigExclusions struct {
Names []string `json:"name" yaml:"name"`
Kinds []string `json:"kind" yaml:"kind"`
Namespaces []string `json:"namespace" yaml:"namespace"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}

// List returns the union of the exclusions.
func (t *KubernetesConfigExclusions) List() []string {
result := append(t.Names, t.Kinds...)
result = append(result, t.Namespaces...)
return result
}

// Filter returns true if the given input matches any of the exclusions.
func (t *KubernetesConfigExclusions) Filter(name, namespace, kind string, labels map[string]string) bool {
if name != "" && len(t.Names) != 0 {
if collections.MatchItems(name, t.Names...) {
return true
}
}

if namespace != "" && len(t.Namespaces) != 0 {
if collections.MatchItems(namespace, t.Namespaces...) {
return true
}
}

if kind != "" && len(t.Kinds) != 0 {
if collections.MatchItems(kind, t.Kinds...) {
return true
}
}

if len(labels) != 0 {
for k, v := range t.Labels {
qVal, ok := labels[k]
if !ok {
continue
}

if collections.MatchItems(qVal, v) {
return true
}
}
}

return false
}

type Kubernetes struct {
BaseScraper `json:",inline"`
ClusterName string `json:"clusterName,omitempty"`
Namespace string `json:"namespace,omitempty"`
UseCache bool `json:"useCache,omitempty"`
AllowIncomplete bool `json:"allowIncomplete,omitempty"`
Scope string `json:"scope,omitempty"`
Since string `json:"since,omitempty"`
Selector string `json:"selector,omitempty"`
FieldSelector string `json:"fieldSelector,omitempty"`
MaxInflight int64 `json:"maxInflight,omitempty"`
Exclusions []string `json:"exclusions,omitempty"`
Kubeconfig *types.EnvVar `json:"kubeconfig,omitempty"`
Event KubernetesEvent `json:"event,omitempty"`
ClusterName string `json:"clusterName,omitempty"`
Namespace string `json:"namespace,omitempty"`
UseCache bool `json:"useCache,omitempty"`
AllowIncomplete bool `json:"allowIncomplete,omitempty"`
Scope string `json:"scope,omitempty"`
Since string `json:"since,omitempty"`
Selector string `json:"selector,omitempty"`
FieldSelector string `json:"fieldSelector,omitempty"`
MaxInflight int64 `json:"maxInflight,omitempty"`
Kubeconfig *types.EnvVar `json:"kubeconfig,omitempty"`
Event KubernetesEvent `json:"event,omitempty"`
Exclusions KubernetesConfigExclusions `json:"exclusions,omitempty"`
}

type KubernetesFile struct {
Expand Down
98 changes: 98 additions & 0 deletions api/v1/kubernetes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package v1

import (
"testing"
)

func TestKubernetesConfigExclusions_Filter(t *testing.T) {
type args struct {
name string
namespace string
kind string
labels map[string]string
}
tests := []struct {
name string
config KubernetesConfigExclusions
args args
shouldExclude bool
}{
{
name: "exclusion by name",
config: KubernetesConfigExclusions{
Names: []string{"junit-*"},
},
args: args{
name: "junit-123",
},
shouldExclude: true,
},
{
name: "exclusion by namespace",
config: KubernetesConfigExclusions{
Namespaces: []string{"*-canaries"},
},
args: args{
namespace: "customer-canaries",
},
shouldExclude: true,
},
{
name: "exclusion by kind",
config: KubernetesConfigExclusions{
Kinds: []string{"*Chart"},
},
args: args{
kind: "HelmChart",
},
shouldExclude: true,
},
{
name: "exclusion by labels | exact match",
config: KubernetesConfigExclusions{
Labels: map[string]string{
"prod": "env",
},
},
args: args{
labels: map[string]string{
"prod": "env",
},
},
shouldExclude: true,
},
{
name: "exclusion by labels | one matches",
config: KubernetesConfigExclusions{
Labels: map[string]string{
"prod": "env",
"is-billed": "true",
"trace-enabled": "true",
},
},
args: args{
labels: map[string]string{
"prod": "env",
"trace-enabled": "false",
},
},
shouldExclude: true,
},
{
name: "no exclusions",
config: KubernetesConfigExclusions{},
args: args{
namespace: "default",
name: "test-foo",
},
shouldExclude: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.config.Filter(tt.args.name, tt.args.namespace, tt.args.kind, tt.args.labels); got != tt.shouldExclude {
t.Errorf("KubernetesConfigExclusions.Filter() = %v, want %v", got, tt.shouldExclude)
}
})
}
}
Loading