Skip to content

Commit 6ed4800

Browse files
authored
feat: scraper plugins (#1200)
* feat: ScrapePlugin CRD * feat: load plugins [skip ci] * feat: inject plugin to base scraper * feat: cache scrape plugins * chore: refactor applying of plugins
1 parent 5b04508 commit 6ed4800

19 files changed

+855
-283
lines changed

api/context.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,15 @@ func (ctx ScrapeContext) WithValue(key, val any) ScrapeContext {
8888

8989
}
9090

91-
func (ctx ScrapeContext) WithScrapeConfig(scraper *v1.ScrapeConfig) ScrapeContext {
92-
ctx.scrapeConfig = scraper
91+
func (ctx ScrapeContext) WithScrapeConfig(scraper *v1.ScrapeConfig, plugins ...v1.ScrapePluginSpec) ScrapeContext {
92+
sc := scraper.DeepCopy()
93+
sc.Spec = sc.Spec.ApplyPlugin(plugins)
9394

94-
ctx.Context = ctx.WithObject(scraper.ObjectMeta)
95+
ctx.scrapeConfig = sc
96+
ctx.Context = ctx.WithObject(sc.ObjectMeta)
9597

9698
// Try to use the temp cache if it exits
97-
if c, exists := scraperTempCache.Load(lo.FromPtr(scraper.GetPersistedID())); exists {
99+
if c, exists := scraperTempCache.Load(lo.FromPtr(sc.GetPersistedID())); exists {
98100
ctx.temp = c.(*TempCache)
99101
}
100102
return ctx

api/v1/common.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,18 @@ type BaseScraper struct {
241241
Properties []ConfigProperties `json:"properties,omitempty" template:"true"`
242242
}
243243

244+
func (base BaseScraper) ApplyPlugins(plugins ...ScrapePluginSpec) BaseScraper {
245+
for _, p := range plugins {
246+
base.Transform.Change.Exclude = append(base.Transform.Change.Exclude, p.Change.Exclude...)
247+
base.Transform.Change.Mapping = append(base.Transform.Change.Mapping, p.Change.Mapping...)
248+
249+
base.Transform.Relationship = append(base.Transform.Relationship, p.Relationship...)
250+
base.Properties = append(base.Properties, p.Properties...)
251+
}
252+
253+
return base
254+
}
255+
244256
func (base BaseScraper) WithoutTransform() BaseScraper {
245257
base.Transform = Transform{}
246258
return base

api/v1/scrapeplugin_types.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package v1
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/flanksource/duty/models"
8+
"github.com/google/uuid"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
)
11+
12+
// ScrapePluginStatus defines the observed state of Plugin
13+
type ScrapePluginStatus struct {
14+
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
15+
}
16+
17+
//+kubebuilder:object:root=true
18+
//+kubebuilder:subresource:status
19+
20+
// ScrapePlugin is the Schema for the scraper plugins
21+
type ScrapePlugin struct {
22+
metav1.TypeMeta `json:",inline"`
23+
metav1.ObjectMeta `json:"metadata,omitempty"`
24+
25+
Spec ScrapePluginSpec `json:"spec,omitempty"`
26+
Status ScrapePluginStatus `json:"status,omitempty"`
27+
}
28+
29+
type ScrapePluginSpec struct {
30+
Change TransformChange `json:"changes,omitempty"`
31+
32+
// Relationship allows you to form relationships between config items using selectors.
33+
Relationship []RelationshipConfig `json:"relationship,omitempty"`
34+
35+
// Properties are custom templatable properties for the scraped config items
36+
// grouped by the config type.
37+
Properties []ConfigProperties `json:"properties,omitempty" template:"true"`
38+
}
39+
40+
func (t ScrapePlugin) ToModel() (*models.ScrapePlugin, error) {
41+
var id uuid.UUID
42+
if v, err := uuid.Parse(string(t.GetUID())); err == nil {
43+
id = v
44+
}
45+
46+
specJSON, err := json.Marshal(t.Spec)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
return &models.ScrapePlugin{
52+
ID: id,
53+
Name: t.Name,
54+
Namespace: t.Namespace,
55+
Spec: specJSON,
56+
CreatedAt: t.CreationTimestamp.Time,
57+
}, nil
58+
}
59+
60+
func (t ScrapePlugin) LoggerName() string {
61+
return fmt.Sprintf("plugin.%s.%s", t.Namespace, t.Name)
62+
}
63+
64+
func (t ScrapePlugin) GetContext() map[string]any {
65+
return map[string]any{
66+
"namespace": t.Namespace,
67+
"name": t.Name,
68+
"scraper_id": t.GetPersistedID(),
69+
}
70+
}
71+
72+
func (t *ScrapePlugin) GetPersistedID() *uuid.UUID {
73+
if t.GetUID() == "" {
74+
return nil
75+
}
76+
77+
u, _ := uuid.Parse(string(t.GetUID()))
78+
return &u
79+
}
80+
81+
//+kubebuilder:object:root=true
82+
83+
// ScrapePluginList contains a list of Plugin
84+
type ScrapePluginList struct {
85+
metav1.TypeMeta `json:",inline"`
86+
metav1.ListMeta `json:"metadata,omitempty"`
87+
Items []ScrapePlugin `json:"items"`
88+
}
89+
90+
func init() {
91+
SchemeBuilder.Register(&ScrapePlugin{}, &ScrapePluginList{})
92+
}

api/v1/types.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,64 @@ type ScraperSpec struct {
7272
Full bool `json:"full,omitempty"`
7373
}
7474

75+
func (c ScraperSpec) ApplyPlugin(plugins []ScrapePluginSpec) ScraperSpec {
76+
spec := c.DeepCopy()
77+
78+
for i := range spec.GCP {
79+
spec.GCP[i].BaseScraper = spec.GCP[i].BaseScraper.ApplyPlugins(plugins...)
80+
}
81+
82+
for i := range spec.AWS {
83+
spec.AWS[i].BaseScraper = spec.AWS[i].BaseScraper.ApplyPlugins(plugins...)
84+
}
85+
86+
for i := range spec.File {
87+
spec.File[i].BaseScraper = spec.File[i].BaseScraper.ApplyPlugins(plugins...)
88+
}
89+
90+
for i := range spec.Kubernetes {
91+
spec.Kubernetes[i].BaseScraper = spec.Kubernetes[i].BaseScraper.ApplyPlugins(plugins...)
92+
}
93+
94+
for i := range spec.KubernetesFile {
95+
spec.KubernetesFile[i].BaseScraper = spec.KubernetesFile[i].BaseScraper.ApplyPlugins(plugins...)
96+
}
97+
98+
for i := range spec.AzureDevops {
99+
spec.AzureDevops[i].BaseScraper = spec.AzureDevops[i].BaseScraper.ApplyPlugins(plugins...)
100+
}
101+
102+
for i := range spec.GithubActions {
103+
spec.GithubActions[i].BaseScraper = spec.GithubActions[i].BaseScraper.ApplyPlugins(plugins...)
104+
}
105+
106+
for i := range spec.Azure {
107+
spec.Azure[i].BaseScraper = spec.Azure[i].BaseScraper.ApplyPlugins(plugins...)
108+
}
109+
110+
for i := range spec.SQL {
111+
spec.SQL[i].BaseScraper = spec.SQL[i].BaseScraper.ApplyPlugins(plugins...)
112+
}
113+
114+
for i := range spec.Slack {
115+
spec.Slack[i].BaseScraper = spec.Slack[i].BaseScraper.ApplyPlugins(plugins...)
116+
}
117+
118+
for i := range spec.Trivy {
119+
spec.Trivy[i].BaseScraper = spec.Trivy[i].BaseScraper.ApplyPlugins(plugins...)
120+
}
121+
122+
for i := range spec.Terraform {
123+
spec.Terraform[i].BaseScraper = spec.Terraform[i].BaseScraper.ApplyPlugins(plugins...)
124+
}
125+
126+
for i := range spec.HTTP {
127+
spec.HTTP[i].BaseScraper = spec.HTTP[i].BaseScraper.ApplyPlugins(plugins...)
128+
}
129+
130+
return *spec
131+
}
132+
75133
func (c ScraperSpec) GenerateName() (string, error) {
76134
return utils.Hash(c)
77135
}

api/v1/zz_generated.deepcopy.go

Lines changed: 104 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)