From 6ae8fa0e5f3c2aab975b188a3c67911ffc4f344d Mon Sep 17 00:00:00 2001 From: Alexander NeonXP Kiryukhin Date: Fri, 26 Jul 2024 11:33:38 +0300 Subject: [PATCH] initial wip --- cmd/kaboom/app/app.go | 38 ++++++++ cmd/kaboom/main.go | 30 +++++++ config/config.yaml | 31 +++++++ config/schema.yaml | 87 ++++++++++++++++++ go.mod | 34 +++++++ go.sum | 94 ++++++++++++++++++++ pkg/config/config.go | 52 +++++++++++ pkg/worker/rule.go | 127 +++++++++++++++++++++++++++ scheme.png | Bin 0 -> 85386 bytes testkafka/docker-compose.yml | 166 +++++++++++++++++++++++++++++++++++ 10 files changed, 659 insertions(+) create mode 100644 cmd/kaboom/app/app.go create mode 100644 cmd/kaboom/main.go create mode 100644 config/config.yaml create mode 100644 config/schema.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/config/config.go create mode 100644 pkg/worker/rule.go create mode 100644 scheme.png create mode 100644 testkafka/docker-compose.yml diff --git a/cmd/kaboom/app/app.go b/cmd/kaboom/app/app.go new file mode 100644 index 0000000..d1b963e --- /dev/null +++ b/cmd/kaboom/app/app.go @@ -0,0 +1,38 @@ +package app + +import ( + "context" + "log/slog" + + "github.com/IBM/sarama" + "gitrepo.ru/neonxp/kaboom/pkg/config" + "gitrepo.ru/neonxp/kaboom/pkg/worker" + "golang.org/x/sync/errgroup" +) + +func Run(ctx context.Context, config *config.Config) error { + clients := make(map[string]sarama.Client, len(config.Kafka)) + for name, params := range config.Kafka { + cfg := sarama.NewConfig() + cfg.ClientID = params.ClientID + + client, err := sarama.NewClient(params.Brokers, cfg) + if err != nil { + return err + } + + clients[name] = client + } + eg, egCtx := errgroup.WithContext(ctx) + for ruleName, rule := range config.Rules { + w, err := worker.New(rule, clients) + if err != nil { + return err + } + eg.Go(func() error { + slog.InfoContext(ctx, "run worker for rule", slog.String("rule", ruleName)) + return w.Run(egCtx) + }) + } + return nil +} diff --git a/cmd/kaboom/main.go b/cmd/kaboom/main.go new file mode 100644 index 0000000..72c5041 --- /dev/null +++ b/cmd/kaboom/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "context" + "flag" + "os" + "os/signal" + + "gitrepo.ru/neonxp/kaboom/cmd/kaboom/app" + "gitrepo.ru/neonxp/kaboom/pkg/config" +) + +var configFile string + +func main() { + flag.StringVar(&configFile, "config", "./config/config.yaml", "config file") + flag.Parse() + + cfg, err := config.New(configFile) + if err != nil { + panic(err) + } + + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill) + defer cancel() + + if err := app.Run(ctx, cfg); err != nil { + panic(err) + } +} diff --git a/config/config.yaml b/config/config.yaml new file mode 100644 index 0000000..82c431b --- /dev/null +++ b/config/config.yaml @@ -0,0 +1,31 @@ +$schema: ./schema.json + +kafka: + kafka1: + brokers: + - 127.0.0.1:9092 + client_id: "kaboom" + version: "0.10.2.0" + timeout: 10s + +rules: + test1: + in: + - kafka: kafka1 + topic: [ "topic1" ] + group_name: "kaboom" + transformation: | + { + "result": in.first + " " + in.second + } + - kafka: kafka1 + topic: [ "topic2" ] + group_name: "kaboom" + transformation: | + { + "result": in.a + " " + in.b + } + out: + - kafka: kafka1 + topic: ["result"] + timeout: 10s diff --git a/config/schema.yaml b/config/schema.yaml new file mode 100644 index 0000000..c1ec566 --- /dev/null +++ b/config/schema.yaml @@ -0,0 +1,87 @@ +$schema: http://json-schema.org/draft-06/schema + +type: object +additionalProperties: false +properties: + kafka: + $ref: '#/definitions/Kafka' + rules: + $ref: '#/definitions/Rules' +required: + - kafka + - rules + +definitions: + Kafka: + type: object + additionalProperties: + type: '#/definitions/KafkaConfig' + + KafkaConfig: + type: object + additionalProperties: false + properties: + brokers: + type: array + items: + type: string + client_id: + type: string + version: + type: string + timeout: + type: string + required: + - brokers + - client_id + - version + title: Kafka1Class + Rules: + type: object + additionalProperties: + type: '#/definitions/Rule' + Rule: + type: object + additionalProperties: false + properties: + in: + type: array + items: + $ref: '#/definitions/In' + out: + type: array + items: + $ref: '#/definitions/Out' + required: + - in + - out + In: + type: object + additionalProperties: false + properties: + kafka: + type: string + topic: + type: array + items: + type: string + group_name: + type: string + transformation: + type: string + required: + - kafka + - topic + Out: + type: object + additionalProperties: false + properties: + kafka: + type: string + topic: + type: array + items: + type: string + required: + - kafka + - topic diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..23e38e6 --- /dev/null +++ b/go.mod @@ -0,0 +1,34 @@ +module gitrepo.ru/neonxp/kaboom + +go 1.22.5 + +require gopkg.in/yaml.v3 v3.0.1 + +require ( + gopkg.in/yaml.v2 v2.2.7 // indirect + sigs.k8s.io/yaml v1.1.0 // indirect +) + +require ( + github.com/IBM/sarama v1.43.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/eapache/go-resiliency v1.6.0 // indirect + github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect + github.com/eapache/queue v1.1.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-jsonnet v0.20.0 + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/klauspost/compress v1.17.8 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sync v0.7.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0efd459 --- /dev/null +++ b/go.sum @@ -0,0 +1,94 @@ +github.com/IBM/sarama v1.43.2 h1:HABeEqRUh32z8yzY2hGB/j8mHSzC/HA9zlEjqFNCzSw= +github.com/IBM/sarama v1.43.2/go.mod h1:Kyo4WkF24Z+1nz7xeVUFWIuKVV8RS3wM8mkvPKMdXFQ= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30= +github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g= +github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..19c9242 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,52 @@ +package config + +import ( + "os" + + "gopkg.in/yaml.v3" +) + +type Config struct { + Workers int `yaml:"workers"` + Kafka Kafka `yaml:"kafka"` + Rules Rules `yaml:"rules"` +} + +type Kafka map[string]KafkaConfig + +type KafkaConfig struct { + Brokers []string `yaml:"brokers"` + ClientID string `yaml:"client_id"` + Version string `yaml:"version"` + Timeout string `yaml:"timeout"` +} + +type Rules map[string]Rule + +type Rule struct { + In []In `yaml:"in"` + Out []Out `yaml:"out"` +} + +type In struct { + Kafka string `yaml:"kafka"` + Topic []string `yaml:"topic"` + GroupName string `yaml:"group_name"` + Transformation string `yaml:"transformation"` +} + +type Out struct { + Kafka string `yaml:"kafka"` + Topic []string `yaml:"topic"` +} + +func New(filename string) (*Config, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + c := new(Config) + + return c, yaml.NewDecoder(f).Decode(c) +} diff --git a/pkg/worker/rule.go b/pkg/worker/rule.go new file mode 100644 index 0000000..f382e1f --- /dev/null +++ b/pkg/worker/rule.go @@ -0,0 +1,127 @@ +package worker + +import ( + "context" + "errors" + "fmt" + "log" + + "github.com/IBM/sarama" + "github.com/google/go-jsonnet" + "gitrepo.ru/neonxp/kaboom/pkg/config" + "golang.org/x/sync/errgroup" +) + +type Executor struct { + Rule config.Rule + Consumers []inRule + Producer []outRule + Transformer *jsonnet.VM +} + +func New(rule config.Rule, clients map[string]sarama.Client) (*Executor, error) { + consumers := make([]inRule, 0, len(rule.In)) + for _, in := range rule.In { + client, ok := clients[in.Kafka] + if !ok { + return nil, fmt.Errorf("server '%s' not found in servers config", in.Kafka) + } + consumer, err := sarama.NewConsumerGroupFromClient(in.GroupName, client) + if err != nil { + return nil, err + } + consumers = append(consumers, inRule{ + consumer: consumer, + topic: in.Topic, + transform: in.Transformation, + }) + } + + producers := make([]outRule, 0, len(rule.Out)) + for _, out := range rule.Out { + client, ok := clients[out.Kafka] + if !ok { + return nil, fmt.Errorf("server '%s' not found in servers config", out.Kafka) + } + p, err := sarama.NewAsyncProducerFromClient(client) + if err != nil { + return nil, err + } + producers = append(producers, outRule{ + producer: p, + topic: out.Topic, + }) + } + + return &Executor{ + Rule: rule, + Consumers: consumers, + Producer: producers, + Transformer: jsonnet.MakeVM(), + }, nil +} + +type inRule struct { + consumer sarama.ConsumerGroup + topic []string + transform string +} + +type outRule struct { + producer sarama.AsyncProducer + topic []string +} + +func (e *Executor) Run(ctx context.Context) error { + g, egctx := errgroup.WithContext(ctx) + for _, r := range e.Consumers { + g.Go(func() error { + for { + if err := e.run(egctx, r); err != nil { + if errors.Is(err, sarama.ErrClosedConsumerGroup) { + return nil + } + return err + } + // check if context was cancelled, signaling that the consumer should stop + if ctx.Err() != nil { + return nil + } + } + }) + } + return g.Wait() +} + +func (e *Executor) run(ctx context.Context, r inRule) error { + defer r.consumer.Close() + return r.consumer.Consume(ctx, r.topic, ConsumerFunc(func(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error { + for { + select { + case message, ok := <-claim.Messages(): + if !ok { + return nil + } + log.Printf("Message claimed: value = %s, timestamp = %v, topic = %s", string(message.Value), message.Timestamp, message.Topic) + session.MarkMessage(message, "") + + case <-session.Context().Done(): + return nil + } + } + })) +} + +type ConsumerFunc func(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error + +func (f ConsumerFunc) Setup(sarama.ConsumerGroupSession) error { + return nil +} + +func (f ConsumerFunc) Cleanup(sarama.ConsumerGroupSession) error { + return nil +} + +func (f ConsumerFunc) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error { + return f(session, claim) +} diff --git a/scheme.png b/scheme.png new file mode 100644 index 0000000000000000000000000000000000000000..7703182951825d411e3ec08eab9ca3d83856a8d6 GIT binary patch literal 85386 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 literal 0 HcmV?d00001 diff --git a/testkafka/docker-compose.yml b/testkafka/docker-compose.yml new file mode 100644 index 0000000..35dcc1b --- /dev/null +++ b/testkafka/docker-compose.yml @@ -0,0 +1,166 @@ +version: '3.6' + +volumes: + zookeeper-data: + driver: local + zookeeper-log: + driver: local + kafka-data: + driver: local + +services: + akhq: + # build: + # context: . + image: tchiotludo/akhq + restart: unless-stopped + environment: + AKHQ_CONFIGURATION: | + akhq: + connections: + docker-kafka-server: + properties: + bootstrap.servers: "kafka:9092" + schema-registry: + url: "http://schema-registry:8085" + connect: + - name: "connect" + url: "http://connect:8083" + + ports: + - 8080:8080 + links: + - kafka + - schema-registry + + zookeeper: + image: confluentinc/cp-zookeeper:${CONFLUENT_VERSION:-latest} + restart: unless-stopped + volumes: + - zookeeper-data:/var/lib/zookeeper/data:Z + - zookeeper-log:/var/lib/zookeeper/log:Z + environment: + ZOOKEEPER_CLIENT_PORT: '2181' + ZOOKEEPER_ADMIN_ENABLE_SERVER: 'false' + + kafka: + image: confluentinc/cp-kafka:${CONFLUENT_VERSION:-latest} + restart: unless-stopped + volumes: + - kafka-data:/var/lib/kafka/data:Z + environment: + KAFKA_BROKER_ID: '0' + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' + KAFKA_NUM_PARTITIONS: '12' + KAFKA_COMPRESSION_TYPE: 'gzip' + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: '1' + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: '1' + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: '1' + KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://kafka:9092' + KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: 'false' + KAFKA_JMX_PORT: '9091' + KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true' + KAFKA_AUTHORIZER_CLASS_NAME: 'kafka.security.authorizer.AclAuthorizer' + KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: 'true' + links: + - zookeeper + ports: + - 9092:9092 + + schema-registry: + image: confluentinc/cp-schema-registry:${CONFLUENT_VERSION:-latest} + restart: unless-stopped + depends_on: + - kafka + environment: + SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: 'PLAINTEXT://kafka:9092' + SCHEMA_REGISTRY_HOST_NAME: 'schema-registry' + SCHEMA_REGISTRY_LISTENERS: 'http://0.0.0.0:8085' + SCHEMA_REGISTRY_LOG4J_ROOT_LOGLEVEL: 'INFO' + + connect: + image: confluentinc/cp-kafka-connect:${CONFLUENT_VERSION:-latest} + restart: unless-stopped + depends_on: + - kafka + - schema-registry + environment: + CONNECT_BOOTSTRAP_SERVERS: 'kafka:9092' + CONNECT_REST_PORT: '8083' + CONNECT_REST_LISTENERS: 'http://0.0.0.0:8083' + CONNECT_REST_ADVERTISED_HOST_NAME: 'connect' + CONNECT_CONFIG_STORAGE_TOPIC: '__connect-config' + CONNECT_OFFSET_STORAGE_TOPIC: '__connect-offsets' + CONNECT_STATUS_STORAGE_TOPIC: '__connect-status' + CONNECT_GROUP_ID: 'kafka-connect' + CONNECT_KEY_CONVERTER_SCHEMAS_ENABLE: 'true' + CONNECT_KEY_CONVERTER: 'io.confluent.connect.avro.AvroConverter' + CONNECT_KEY_CONVERTER_SCHEMA_REGISTRY_URL: 'http://schema-registry:8085' + CONNECT_VALUE_CONVERTER_SCHEMAS_ENABLE: 'true' + CONNECT_VALUE_CONVERTER: 'io.confluent.connect.avro.AvroConverter' + CONNECT_VALUE_CONVERTER_SCHEMA_REGISTRY_URL: 'http://schema-registry:8085' + CONNECT_INTERNAL_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_INTERNAL_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: '1' + CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: '1' + CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: '1' + CONNECT_PLUGIN_PATH: ' /usr/share/java/' + + ksqldb: + image: confluentinc/cp-ksqldb-server:${CONFLUENT_VERSION:-latest} + restart: unless-stopped + depends_on: + - kafka + - connect + - schema-registry + ports: + - 8088:8088 + environment: + KSQL_BOOTSTRAP_SERVERS: 'kafka:9092' + KSQL_LISTENERS: 'http://0.0.0.0:8088' + KSQL_KSQL_SERVICE_ID: 'ksql' + KSQL_KSQL_SCHEMA_REGISTRY_URL: 'http://schema-registry:8085' + KSQL_KSQL_CONNECT_URL: 'http://connect:8083' + KSQL_KSQL_SINK_PARTITIONS: '1' + KSQL_KSQL_LOGGING_PROCESSING_TOPIC_REPLICATION_FACTOR: '1' + + test-data: + image: gradle:8-jdk11 + command: "gradle --no-daemon testInjectData -x installFrontend -x assembleFrontend" + restart: unless-stopped + working_dir: /app + volumes: + - ./:/app:z + links: + - kafka + - schema-registry + + kafkacat: + image: confluentinc/cp-kafkacat:${CONFLUENT_KAFKACAT_VERSION:-latest} + restart: unless-stopped + depends_on: + - kafka + command: + - bash + - -c + - | + kafkacat -P -b kafka:9092 -t json << EOF + {"_id":"5c4b2b45ab234c86955f0802","index":0,"guid":"d3637b06-9940-4958-9f82-639001c14c34"} + {"_id":"5c4b2b459ffa9bb0c0c249e1","index":1,"guid":"08612fb5-40a7-45e5-9ff2-beb89a1b2835"} + {"_id":"5c4b2b4545d7cbc7bf8b6e3e","index":2,"guid":"4880280a-cf8b-4884-881e-7b64ebf2afd0"} + {"_id":"5c4b2b45dab381e6b3024c6d","index":3,"guid":"36d04c26-0dae-4a8e-a66e-bde9b3b6a745"} + {"_id":"5c4b2b45d1103ce30dfe1947","index":4,"guid":"14d53f2c-def3-406f-9dfb-c29963fdc37e"} + {"_id":"5c4b2b45d6d3b5c51d3dacb7","index":5,"guid":"a20cfc3a-934a-4b93-9a03-008ec651b5a4"} + EOF + + kafkacat -P -b kafka:9092 -t csv << EOF + 1,Sauncho,Attfield,sattfield0@netlog.com,Male,221.119.13.246 + 2,Luci,Harp,lharp1@wufoo.com,Female,161.14.184.150 + 3,Hanna,McQuillan,hmcquillan2@mozilla.com,Female,214.67.74.80 + 4,Melba,Lecky,mlecky3@uiuc.edu,Female,158.112.18.189 + 5,Mordecai,Hurdiss,mhurdiss4@rambler.ru,Male,175.123.45.143 + EOF + + kafkacat -b kafka:9092 -o beginning -G json-consumer json + links: + - kafka \ No newline at end of file