From 26ccc5f00501eab2f10019ec97180b903b834e32 Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Wed, 27 Dec 2023 00:01:27 +0700 Subject: [PATCH] Added: monitor service with event bus for notifications --- .github/workflows/docker-publish-bot.yml | 85 +++++++++++++++++ .github/workflows/docker-publish-monitor.yml | 85 +++++++++++++++++ .github/workflows/docker-publish.yml | 85 ----------------- Makefile | 11 ++- cmd/bot/main.go | 9 ++ cmd/monitor/main.go | 9 ++ cmd/service-monitor-tgbot/main.go | 23 ----- internal/bot/app.go | 93 ------------------- internal/botx/app.go | 18 ++++ internal/{ => botx}/config/config.go | 5 - internal/{ => botx}/config/defaults.go | 3 - internal/{ => botx}/config/module.go | 6 -- internal/{ => botx}/infrastructure/module.go | 0 .../{ => botx}/infrastructure/telegram.go | 2 +- internal/{bot => botx}/messages.go | 4 +- internal/monitorx/app.go | 74 +++++++++++++++ internal/monitorx/config/config.go | 14 +++ internal/monitorx/config/defaults.go | 9 ++ internal/monitorx/config/module.go | 29 ++++++ internal/monitorx/eventbus/eventbus.go | 42 +++++++++ internal/monitorx/eventbus/redis.go | 47 ++++++++++ internal/monitorx/eventbus/types.go | 5 + internal/{ => monitorx}/monitor/module.go | 4 +- .../{ => monitorx}/monitor/probes/http.go | 0 internal/{ => monitorx}/monitor/probes/tcp.go | 0 .../{ => monitorx}/monitor/probes/types.go | 0 internal/{ => monitorx}/monitor/services.go | 2 +- internal/{ => monitorx}/monitor/types.go | 0 internal/{ => monitorx}/storage/memory.go | 0 internal/{ => monitorx}/storage/module.go | 0 internal/{ => monitorx}/storage/redis.go | 0 internal/{ => monitorx}/storage/service.go | 0 .../{ => monitorx}/storage/service_test.go | 0 internal/{ => monitorx}/storage/types.go | 0 internal/{ => monitorx}/storage/yaml.go | 0 35 files changed, 441 insertions(+), 223 deletions(-) create mode 100644 .github/workflows/docker-publish-bot.yml create mode 100644 .github/workflows/docker-publish-monitor.yml delete mode 100644 .github/workflows/docker-publish.yml create mode 100644 cmd/bot/main.go create mode 100644 cmd/monitor/main.go delete mode 100644 cmd/service-monitor-tgbot/main.go delete mode 100644 internal/bot/app.go create mode 100644 internal/botx/app.go rename internal/{ => botx}/config/config.go (86%) rename internal/{ => botx}/config/defaults.go (85%) rename internal/{ => botx}/config/module.go (74%) rename internal/{ => botx}/infrastructure/module.go (100%) rename internal/{ => botx}/infrastructure/telegram.go (95%) rename internal/{bot => botx}/messages.go (94%) create mode 100644 internal/monitorx/app.go create mode 100644 internal/monitorx/config/config.go create mode 100644 internal/monitorx/config/defaults.go create mode 100644 internal/monitorx/config/module.go create mode 100644 internal/monitorx/eventbus/eventbus.go create mode 100644 internal/monitorx/eventbus/redis.go create mode 100644 internal/monitorx/eventbus/types.go rename internal/{ => monitorx}/monitor/module.go (95%) rename internal/{ => monitorx}/monitor/probes/http.go (100%) rename internal/{ => monitorx}/monitor/probes/tcp.go (100%) rename internal/{ => monitorx}/monitor/probes/types.go (100%) rename internal/{ => monitorx}/monitor/services.go (94%) rename internal/{ => monitorx}/monitor/types.go (100%) rename internal/{ => monitorx}/storage/memory.go (100%) rename internal/{ => monitorx}/storage/module.go (100%) rename internal/{ => monitorx}/storage/redis.go (100%) rename internal/{ => monitorx}/storage/service.go (100%) rename internal/{ => monitorx}/storage/service_test.go (100%) rename internal/{ => monitorx}/storage/types.go (100%) rename internal/{ => monitorx}/storage/yaml.go (100%) diff --git a/.github/workflows/docker-publish-bot.yml b/.github/workflows/docker-publish-bot.yml new file mode 100644 index 0000000..c1a3286 --- /dev/null +++ b/.github/workflows/docker-publish-bot.yml @@ -0,0 +1,85 @@ +name: Build Telegram Bot + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: capcom6/service-monitor-bot + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Log into Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v3 + with: + file: build/package/Dockerfile + build-args: APP=bot + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # cache-from: type=gha + # cache-to: type=gha,mode=max + + # deploy: + # runs-on: ubuntu-latest + # # run only in v* tags + # if: startsWith(github.ref, 'refs/tags/v') + # needs: + # - build + + # env: + # AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}} + # AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}} + + # steps: + # - name: Checkout code + # uses: actions/checkout@v3 + + # - name: Install Terraform + # uses: hashicorp/setup-terraform@v2 + # with: + # terraform_version: 1.4.6 + + # - name: Initialize Terraform + # working-directory: deployments/docker-swarm-terraform + # run: terraform init + + # - name: Deploy Docker service to Swarm + # working-directory: deployments/docker-swarm-terraform + # env: + # CPU_LIMIT: ${{ vars.CPU_LIMIT }} + # MEMORY_LIMIT: ${{ vars.MEMORY_LIMIT }} + # run: | + # eval "$(ssh-agent -s)" + # ssh-add <(echo "${{ secrets.SSH_PRIVATE_KEY }}") + # terraform apply -auto-approve -input=false \ + # -var 'swarm-manager-host=${{ secrets.SWARM_MANAGER_HOST }}' \ + # -var 'app-name=${{ vars.APP_NAME }}' \ + # -var "app-version=${GITHUB_REF#refs/tags/v}" \ + # -var 'app-config-b64=${{ secrets.APP_CONFIG_B64 }}' \ + # -var 'app-env-json-b64=${{ secrets.APP_ENV_JSON_B64 }}' \ + # -var "cpu-limit=${CPU_LIMIT:-1000000000}" \ + # -var "memory-limit=${MEMORY_LIMIT:-128000000}" diff --git a/.github/workflows/docker-publish-monitor.yml b/.github/workflows/docker-publish-monitor.yml new file mode 100644 index 0000000..44d6063 --- /dev/null +++ b/.github/workflows/docker-publish-monitor.yml @@ -0,0 +1,85 @@ +name: Build Monitor Core + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: capcom6/service-monitor-core + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Log into Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v3 + with: + file: build/package/Dockerfile + build-args: APP=monitor + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # cache-from: type=gha + # cache-to: type=gha,mode=max + + # deploy: + # runs-on: ubuntu-latest + # # run only in v* tags + # if: startsWith(github.ref, 'refs/tags/v') + # needs: + # - build + + # env: + # AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}} + # AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}} + + # steps: + # - name: Checkout code + # uses: actions/checkout@v3 + + # - name: Install Terraform + # uses: hashicorp/setup-terraform@v2 + # with: + # terraform_version: 1.4.6 + + # - name: Initialize Terraform + # working-directory: deployments/docker-swarm-terraform + # run: terraform init + + # - name: Deploy Docker service to Swarm + # working-directory: deployments/docker-swarm-terraform + # env: + # CPU_LIMIT: ${{ vars.CPU_LIMIT }} + # MEMORY_LIMIT: ${{ vars.MEMORY_LIMIT }} + # run: | + # eval "$(ssh-agent -s)" + # ssh-add <(echo "${{ secrets.SSH_PRIVATE_KEY }}") + # terraform apply -auto-approve -input=false \ + # -var 'swarm-manager-host=${{ secrets.SWARM_MANAGER_HOST }}' \ + # -var 'app-name=${{ vars.APP_NAME }}' \ + # -var "app-version=${GITHUB_REF#refs/tags/v}" \ + # -var 'app-config-b64=${{ secrets.APP_CONFIG_B64 }}' \ + # -var 'app-env-json-b64=${{ secrets.APP_ENV_JSON_B64 }}' \ + # -var "cpu-limit=${CPU_LIMIT:-1000000000}" \ + # -var "memory-limit=${MEMORY_LIMIT:-128000000}" diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml deleted file mode 100644 index 3c914fb..0000000 --- a/.github/workflows/docker-publish.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Docker - -on: - push: - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Docker meta - id: meta - uses: docker/metadata-action@v3 - with: - images: capcom6/service-monitor-tgbot - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Log into Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_PASSWORD }} - - - name: Build and push Docker image - uses: docker/build-push-action@v3 - with: - file: build/package/Dockerfile - build-args: APP=service-monitor-tgbot - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - # cache-from: type=gha - # cache-to: type=gha,mode=max - - deploy: - runs-on: ubuntu-latest - # run only in v* tags - if: startsWith(github.ref, 'refs/tags/v') - needs: - - build - - env: - AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}} - AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}} - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install Terraform - uses: hashicorp/setup-terraform@v2 - with: - terraform_version: 1.4.6 - - - name: Initialize Terraform - working-directory: deployments/docker-swarm-terraform - run: terraform init - - - name: Deploy Docker service to Swarm - working-directory: deployments/docker-swarm-terraform - env: - CPU_LIMIT: ${{ vars.CPU_LIMIT }} - MEMORY_LIMIT: ${{ vars.MEMORY_LIMIT }} - run: | - eval "$(ssh-agent -s)" - ssh-add <(echo "${{ secrets.SSH_PRIVATE_KEY }}") - terraform apply -auto-approve -input=false \ - -var 'swarm-manager-host=${{ secrets.SWARM_MANAGER_HOST }}' \ - -var 'app-name=${{ vars.APP_NAME }}' \ - -var "app-version=${GITHUB_REF#refs/tags/v}" \ - -var 'app-config-b64=${{ secrets.APP_CONFIG_B64 }}' \ - -var 'app-env-json-b64=${{ secrets.APP_ENV_JSON_B64 }}' \ - -var "cpu-limit=${CPU_LIMIT:-1000000000}" \ - -var "memory-limit=${MEMORY_LIMIT:-128000000}" diff --git a/Makefile b/Makefile index f409217..5a983ba 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,12 @@ endif run: go run ./cmd/$(project_name)/main.go +monitor: + CONFIG_PATH=./configs/monitor.yml go run ./cmd/monitor/main.go + +bot: + go run ./cmd/bot/main.go + init-dev: go mod download \ && go install github.com/cosmtrek/air@latest @@ -37,7 +43,8 @@ view-docs: php -S 127.0.0.1:8080 -t ./api docker-build: - docker build -f build/package/Dockerfile -t $(image_name) --build-arg APP=$(project_name) . + docker build -f build/package/Dockerfile -t capcom6/service-monior-core --build-arg APP=monitor . + docker build -f build/package/Dockerfile -t capcom6/service-monior-bot --build-arg APP=bot . docker: docker-compose -f deployments/docker-compose/docker-compose.yml up --build @@ -45,4 +52,4 @@ docker: docker-dev: docker-compose -f deployments/docker-compose/docker-compose.dev.yml up --build -.PHONY: init air db-upgrade db-upgrade-raw test api-docs view-docs +.PHONY: run monitor bot init-dev init air db-upgrade db-upgrade-raw test api-docs view-docs docker docker-dev diff --git a/cmd/bot/main.go b/cmd/bot/main.go new file mode 100644 index 0000000..8bde578 --- /dev/null +++ b/cmd/bot/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/capcom6/service-monitor-tgbot/internal/botx" +) + +func main() { + botx.Run() +} diff --git a/cmd/monitor/main.go b/cmd/monitor/main.go new file mode 100644 index 0000000..17abf23 --- /dev/null +++ b/cmd/monitor/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/capcom6/service-monitor-tgbot/internal/monitorx" +) + +func main() { + monitorx.Run() +} diff --git a/cmd/service-monitor-tgbot/main.go b/cmd/service-monitor-tgbot/main.go deleted file mode 100644 index 6341124..0000000 --- a/cmd/service-monitor-tgbot/main.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2023 Aleksandr Soloshenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/capcom6/service-monitor-tgbot/internal/bot" -) - -func main() { - bot.Run() -} diff --git a/internal/bot/app.go b/internal/bot/app.go deleted file mode 100644 index d45fe96..0000000 --- a/internal/bot/app.go +++ /dev/null @@ -1,93 +0,0 @@ -package bot - -import ( - "context" - "sync" - - "github.com/capcom6/go-infra-fx/logger" - "github.com/capcom6/service-monitor-tgbot/internal/config" - "github.com/capcom6/service-monitor-tgbot/internal/infrastructure" - "github.com/capcom6/service-monitor-tgbot/internal/monitor" - "github.com/capcom6/service-monitor-tgbot/internal/storage" - "go.uber.org/fx" - "go.uber.org/fx/fxevent" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -var Module = fx.Module( - "bot", - logger.Module, - config.Module, - infrastructure.Module, - monitor.Module, - storage.Module, - fx.Provide(NewMessages), -) - -func Run() { - fx.New( - Module, - fx.WithLogger(func(logger *zap.Logger) fxevent.Logger { - logOption := fxevent.ZapLogger{Logger: logger} - logOption.UseLogLevel(zapcore.DebugLevel) - return &logOption - }), - fx.Invoke(func(lc fx.Lifecycle, logger *zap.Logger, bot *infrastructure.TelegramBot, monitorMod *monitor.MonitorModule, messages *Messages) error { - ctx, cancel := context.WithCancel(context.Background()) - wg := &sync.WaitGroup{} - lc.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - logger.Info("Started") - return nil - }, - OnStop: func(ctx context.Context) error { - cancel() - wg.Wait() - logger.Info("Stopped") - return nil - }, - }) - - ch, err := monitorMod.Monitor(ctx) - if err != nil { - return err - } - - wg.Add(1) - go func() { - defer wg.Done() - for v := range ch { - logger.Debug("probe", zap.String("name", v.Name), zap.String("state", string(v.State)), zap.Error(v.Error)) - - msg := "" - if v.State == monitor.ServiceOffline { - context := OfflineContext{ - OnlineContext: OnlineContext{ - Name: bot.EscapeText(v.Name), - }, - Error: bot.EscapeText(v.Error.Error()), - } - msg, err = messages.Render(TemplateOffline, context) - } else { - context := OnlineContext{ - Name: bot.EscapeText(v.Name), - } - msg, err = messages.Render(TemplateOnline, context) - } - - if err != nil { - logger.Error("can't render template", zap.Error(err)) - continue - } - - if err := bot.SendMessage(msg); err != nil { - logger.Error("can't send message", zap.Error(err)) - } - } - }() - - return nil - }), - ).Run() -} diff --git a/internal/botx/app.go b/internal/botx/app.go new file mode 100644 index 0000000..bcc7a61 --- /dev/null +++ b/internal/botx/app.go @@ -0,0 +1,18 @@ +package botx + +import ( + "fmt" + + "go.uber.org/fx" +) + +func Run() { + module := fx.Module( + "bot", + fx.Invoke(func() { + fmt.Println("Bot") + }), + ) + + fx.New(module).Run() +} diff --git a/internal/config/config.go b/internal/botx/config/config.go similarity index 86% rename from internal/config/config.go rename to internal/botx/config/config.go index 5c15ed1..c27e3fa 100644 --- a/internal/config/config.go +++ b/internal/botx/config/config.go @@ -2,7 +2,6 @@ package config type Config struct { Telegram Telegram `yaml:"telegram"` - Storage Storage `yaml:"storage"` } type Telegram struct { @@ -14,7 +13,3 @@ type Telegram struct { } type TelegramMessages map[string]string - -type Storage struct { - DSN string `yaml:"dsn"` -} diff --git a/internal/config/defaults.go b/internal/botx/config/defaults.go similarity index 85% rename from internal/config/defaults.go rename to internal/botx/config/defaults.go index 367feda..bb5da03 100644 --- a/internal/config/defaults.go +++ b/internal/botx/config/defaults.go @@ -8,8 +8,5 @@ var ( defaultConfig Config = Config{ Telegram: Telegram{Token: "", ChatID: 0, WebhookURL: "", Debug: false, Messages: defaultTelegramMessages}, - Storage: Storage{ - DSN: "file://./config.yaml", - }, } ) diff --git a/internal/config/module.go b/internal/botx/config/module.go similarity index 74% rename from internal/config/module.go rename to internal/botx/config/module.go index 8216b2b..a07cb07 100644 --- a/internal/config/module.go +++ b/internal/botx/config/module.go @@ -2,7 +2,6 @@ package config import ( "github.com/capcom6/go-infra-fx/config" - "github.com/capcom6/service-monitor-tgbot/internal/storage" "go.uber.org/fx" "go.uber.org/zap" ) @@ -21,9 +20,4 @@ var Module = fx.Module( fx.Provide(func(cfg Config) TelegramMessages { return cfg.Telegram.Messages }), - fx.Provide(func(cfg Config) storage.Config { - return storage.Config{ - DSN: cfg.Storage.DSN, - } - }), ) diff --git a/internal/infrastructure/module.go b/internal/botx/infrastructure/module.go similarity index 100% rename from internal/infrastructure/module.go rename to internal/botx/infrastructure/module.go diff --git a/internal/infrastructure/telegram.go b/internal/botx/infrastructure/telegram.go similarity index 95% rename from internal/infrastructure/telegram.go rename to internal/botx/infrastructure/telegram.go index 932e3bc..64b13f9 100644 --- a/internal/infrastructure/telegram.go +++ b/internal/botx/infrastructure/telegram.go @@ -1,7 +1,7 @@ package infrastructure import ( - "github.com/capcom6/service-monitor-tgbot/internal/config" + "github.com/capcom6/service-monitor-tgbot/internal/botx/config" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" "go.uber.org/fx" "go.uber.org/zap" diff --git a/internal/bot/messages.go b/internal/botx/messages.go similarity index 94% rename from internal/bot/messages.go rename to internal/botx/messages.go index f021902..6a74272 100644 --- a/internal/bot/messages.go +++ b/internal/botx/messages.go @@ -1,4 +1,4 @@ -package bot +package botx import ( "fmt" @@ -6,7 +6,7 @@ import ( "sync" "text/template" - "github.com/capcom6/service-monitor-tgbot/internal/config" + "github.com/capcom6/service-monitor-tgbot/internal/botx/config" ) type TemplateName string diff --git a/internal/monitorx/app.go b/internal/monitorx/app.go new file mode 100644 index 0000000..a80b161 --- /dev/null +++ b/internal/monitorx/app.go @@ -0,0 +1,74 @@ +package monitorx + +import ( + "context" + "sync" + "time" + + "github.com/capcom6/go-infra-fx/logger" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/config" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/eventbus" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/monitor" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/storage" + "go.uber.org/fx" + "go.uber.org/fx/fxevent" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func Run() { + module := fx.Module( + "bot", + logger.Module, + config.Module, + monitor.Module, + storage.Module, + eventbus.Module, + fx.Invoke(func(lc fx.Lifecycle, logger *zap.Logger, monitorMod *monitor.MonitorModule, eventbus eventbus.EventBus) error { + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + lc.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + logger.Info("Started") + return nil + }, + OnStop: func(ctx context.Context) error { + cancel() + wg.Wait() + logger.Info("Stopped") + return nil + }, + }) + + ch, err := monitorMod.Monitor(ctx) + if err != nil { + return err + } + + wg.Add(1) + go func() { + defer wg.Done() + for v := range ch { + logger.Debug("probe", zap.Any("state", v)) + + ctx, cancel := context.WithTimeout(ctx, 1*time.Second) + if err := eventbus.Send(ctx, v); err != nil { + logger.Error("failed to send event", zap.Error(err)) + } + cancel() + } + }() + + return nil + }), + ) + + fx.New( + module, + fx.WithLogger(func(logger *zap.Logger) fxevent.Logger { + logOption := fxevent.ZapLogger{Logger: logger} + logOption.UseLogLevel(zapcore.DebugLevel) + return &logOption + }), + ).Run() +} diff --git a/internal/monitorx/config/config.go b/internal/monitorx/config/config.go new file mode 100644 index 0000000..c22d058 --- /dev/null +++ b/internal/monitorx/config/config.go @@ -0,0 +1,14 @@ +package config + +type Config struct { + Storage Storage `yaml:"storage"` + EventBus EventBus `yaml:"eventBus"` +} + +type Storage struct { + DSN string `yaml:"dsn"` +} + +type EventBus struct { + DSN string `yaml:"dsn"` +} diff --git a/internal/monitorx/config/defaults.go b/internal/monitorx/config/defaults.go new file mode 100644 index 0000000..a8f259d --- /dev/null +++ b/internal/monitorx/config/defaults.go @@ -0,0 +1,9 @@ +package config + +var ( + defaultConfig Config = Config{ + Storage: Storage{ + DSN: "file://./config.yaml", + }, + } +) diff --git a/internal/monitorx/config/module.go b/internal/monitorx/config/module.go new file mode 100644 index 0000000..a3f6328 --- /dev/null +++ b/internal/monitorx/config/module.go @@ -0,0 +1,29 @@ +package config + +import ( + "github.com/capcom6/go-infra-fx/config" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/eventbus" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/storage" + "go.uber.org/fx" + "go.uber.org/zap" +) + +var Module = fx.Module( + "appconfig", + fx.Decorate(func(log *zap.Logger) *zap.Logger { + return log.Named("appconfig") + }), + fx.Provide(func() (Config, error) { + return defaultConfig, config.LoadConfig(&defaultConfig) + }), + fx.Provide(func(cfg Config) storage.Config { + return storage.Config{ + DSN: cfg.Storage.DSN, + } + }), + fx.Provide(func(cfg Config) eventbus.Config { + return eventbus.Config{ + DSN: cfg.EventBus.DSN, + } + }), +) diff --git a/internal/monitorx/eventbus/eventbus.go b/internal/monitorx/eventbus/eventbus.go new file mode 100644 index 0000000..1578b43 --- /dev/null +++ b/internal/monitorx/eventbus/eventbus.go @@ -0,0 +1,42 @@ +package eventbus + +import ( + "context" + "errors" + "fmt" + "net/url" + + "go.uber.org/fx" + "go.uber.org/zap" +) + +type EventBus interface { + Send(ctx context.Context, event interface{}) error +} + +type Params struct { + fx.In + + Config Config + Logger *zap.Logger +} + +var Module = fx.Module( + "storage", + fx.Provide(func(p Params) (EventBus, error) { + return New(p.Config) + }), +) + +func New(cfg Config) (EventBus, error) { + u, err := url.Parse(cfg.DSN) + if err != nil { + return nil, fmt.Errorf("invalid dsn: %w", err) + } + + if u.Scheme == "redis" || u.Scheme == "rediss" { + return newRedisBus(u) + } + + return nil, errors.New("unknown scheme") +} diff --git a/internal/monitorx/eventbus/redis.go b/internal/monitorx/eventbus/redis.go new file mode 100644 index 0000000..0496ab6 --- /dev/null +++ b/internal/monitorx/eventbus/redis.go @@ -0,0 +1,47 @@ +package eventbus + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/url" + + "github.com/redis/go-redis/v9" +) + +type redisBus struct { + channelName string + + client *redis.Client +} + +func newRedisBus(u *url.URL) (*redisBus, error) { + channelName := u.Query().Get("channel") + if channelName == "" { + return nil, errors.New("channel name is required") + } + + query := u.Query() + query.Del("channel") + u.RawQuery = query.Encode() + + opts, err := redis.ParseURL(u.String()) + if err != nil { + return nil, err + } + + return &redisBus{ + channelName: channelName, + client: redis.NewClient(opts), + }, nil +} + +func (b *redisBus) Send(ctx context.Context, event interface{}) error { + payload, err := json.Marshal(event) + if err != nil { + return fmt.Errorf("failed to marshal event: %w", err) + } + + return b.client.Publish(ctx, b.channelName, string(payload)).Err() +} diff --git a/internal/monitorx/eventbus/types.go b/internal/monitorx/eventbus/types.go new file mode 100644 index 0000000..51b33d3 --- /dev/null +++ b/internal/monitorx/eventbus/types.go @@ -0,0 +1,5 @@ +package eventbus + +type Config struct { + DSN string +} diff --git a/internal/monitor/module.go b/internal/monitorx/monitor/module.go similarity index 95% rename from internal/monitor/module.go rename to internal/monitorx/monitor/module.go index 4a70a13..44c1144 100644 --- a/internal/monitor/module.go +++ b/internal/monitorx/monitor/module.go @@ -5,8 +5,8 @@ import ( "fmt" "sync" - "github.com/capcom6/service-monitor-tgbot/internal/monitor/probes" - "github.com/capcom6/service-monitor-tgbot/internal/storage" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/monitor/probes" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/storage" "github.com/capcom6/service-monitor-tgbot/pkg/collections" "go.uber.org/fx" "go.uber.org/zap" diff --git a/internal/monitor/probes/http.go b/internal/monitorx/monitor/probes/http.go similarity index 100% rename from internal/monitor/probes/http.go rename to internal/monitorx/monitor/probes/http.go diff --git a/internal/monitor/probes/tcp.go b/internal/monitorx/monitor/probes/tcp.go similarity index 100% rename from internal/monitor/probes/tcp.go rename to internal/monitorx/monitor/probes/tcp.go diff --git a/internal/monitor/probes/types.go b/internal/monitorx/monitor/probes/types.go similarity index 100% rename from internal/monitor/probes/types.go rename to internal/monitorx/monitor/probes/types.go diff --git a/internal/monitor/services.go b/internal/monitorx/monitor/services.go similarity index 94% rename from internal/monitor/services.go rename to internal/monitorx/monitor/services.go index 488424f..c82519e 100644 --- a/internal/monitor/services.go +++ b/internal/monitorx/monitor/services.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/capcom6/service-monitor-tgbot/internal/monitor/probes" + "github.com/capcom6/service-monitor-tgbot/internal/monitorx/monitor/probes" ) type ServiceMonitorConfig struct { diff --git a/internal/monitor/types.go b/internal/monitorx/monitor/types.go similarity index 100% rename from internal/monitor/types.go rename to internal/monitorx/monitor/types.go diff --git a/internal/storage/memory.go b/internal/monitorx/storage/memory.go similarity index 100% rename from internal/storage/memory.go rename to internal/monitorx/storage/memory.go diff --git a/internal/storage/module.go b/internal/monitorx/storage/module.go similarity index 100% rename from internal/storage/module.go rename to internal/monitorx/storage/module.go diff --git a/internal/storage/redis.go b/internal/monitorx/storage/redis.go similarity index 100% rename from internal/storage/redis.go rename to internal/monitorx/storage/redis.go diff --git a/internal/storage/service.go b/internal/monitorx/storage/service.go similarity index 100% rename from internal/storage/service.go rename to internal/monitorx/storage/service.go diff --git a/internal/storage/service_test.go b/internal/monitorx/storage/service_test.go similarity index 100% rename from internal/storage/service_test.go rename to internal/monitorx/storage/service_test.go diff --git a/internal/storage/types.go b/internal/monitorx/storage/types.go similarity index 100% rename from internal/storage/types.go rename to internal/monitorx/storage/types.go diff --git a/internal/storage/yaml.go b/internal/monitorx/storage/yaml.go similarity index 100% rename from internal/storage/yaml.go rename to internal/monitorx/storage/yaml.go