From c44fedb48882063519d97eae25b4c0d829fe3550 Mon Sep 17 00:00:00 2001 From: lorsan Date: Sun, 3 May 2026 19:17:55 +0300 Subject: [PATCH 1/6] refactor: change structure project --- {cmd/agent => agent/cmd}/main.go | 4 +++- {internal/agent => agent/internal}/app/app.go | 17 ++++++++--------- .../agent => agent/internal}/rpc/client.go | 2 +- .../internal}/service/agent_service/agent.go | 12 ++++++------ .../service/agent_service/agent_test.go | 2 +- .../internal}/service/collector/collector.go | 4 ++-- .../internal}/service/docker_service/bad.go | 2 +- .../internal}/service/docker_service/docker.go | 4 ++-- .../service/docker_service/docker_test.go | 0 .../internal}/utils/config_yaml/config.go | 0 .../utils/config_yaml/config_yaml_test.go | 0 .../internal}/utils/settings/settings.go | 0 api/gen/homeops/hub.pb.go | 4 ++-- api/gen/homeops/hub_grpc.pb.go | 4 ++-- cmd/telegram/main.go | 1 - {cmd/hub => hub/cmd}/main.go | 4 +++- {internal/hub => hub/internal}/app/app.go | 14 +++++++------- .../hub => hub/internal}/domain/structure.go | 2 +- {internal/hub => hub/internal}/embed.go | 2 +- .../20260415151037_create_agent_table.down.sql | 0 .../20260415151037_create_agent_table.up.sql | 0 .../hub => hub/internal}/migrator/migrator.go | 1 + {internal/hub => hub/internal}/rpc/server.go | 2 +- .../internal}/service/hub_service/hub.go | 6 +++--- .../internal}/service/hub_service/mapper.go | 4 ++-- {internal/hub => hub/internal}/store/mapper.go | 16 ++++++++-------- .../internal}/store/sqlc/gen/agent.sql.go | 2 +- .../hub => hub/internal}/store/sqlc/gen/db.go | 0 .../internal}/store/sqlc/gen/models.go | 0 .../internal}/store/sqlc/queries/agent.sql | 0 {internal/hub => hub/internal}/store/store.go | 4 ++-- .../hub => hub/internal}/utils/hasher/id.go | 2 +- {internal/shared => shared}/config/config.go | 0 .../shared => shared}/config/config_test.go | 0 {internal => shared}/domain/agent.go | 0 {internal => shared}/domain/mapper.go | 0 {internal/shared => shared}/log/init.go | 0 sqlc.yaml | 6 +++--- 38 files changed, 62 insertions(+), 59 deletions(-) rename {cmd/agent => agent/cmd}/main.go (59%) rename {internal/agent => agent/internal}/app/app.go (76%) rename {internal/agent => agent/internal}/rpc/client.go (94%) rename {internal/agent => agent/internal}/service/agent_service/agent.go (77%) rename {internal/agent => agent/internal}/service/agent_service/agent_test.go (90%) rename {internal/agent => agent/internal}/service/collector/collector.go (84%) rename {internal/agent => agent/internal}/service/docker_service/bad.go (85%) rename {internal/agent => agent/internal}/service/docker_service/docker.go (89%) rename {internal/agent => agent/internal}/service/docker_service/docker_test.go (100%) rename {internal/agent => agent/internal}/utils/config_yaml/config.go (100%) rename {internal/agent => agent/internal}/utils/config_yaml/config_yaml_test.go (100%) rename {internal/agent => agent/internal}/utils/settings/settings.go (100%) delete mode 100644 cmd/telegram/main.go rename {cmd/hub => hub/cmd}/main.go (50%) rename {internal/hub => hub/internal}/app/app.go (84%) rename {internal/hub => hub/internal}/domain/structure.go (89%) rename {internal/hub => hub/internal}/embed.go (80%) rename {internal/hub => hub/internal}/migrations/20260415151037_create_agent_table.down.sql (100%) rename {internal/hub => hub/internal}/migrations/20260415151037_create_agent_table.up.sql (100%) rename {internal/hub => hub/internal}/migrator/migrator.go (99%) rename {internal/hub => hub/internal}/rpc/server.go (96%) rename {internal/hub => hub/internal}/service/hub_service/hub.go (93%) rename {internal/hub => hub/internal}/service/hub_service/mapper.go (77%) rename {internal/hub => hub/internal}/store/mapper.go (75%) rename {internal/hub => hub/internal}/store/sqlc/gen/agent.sql.go (99%) rename {internal/hub => hub/internal}/store/sqlc/gen/db.go (100%) rename {internal/hub => hub/internal}/store/sqlc/gen/models.go (100%) rename {internal/hub => hub/internal}/store/sqlc/queries/agent.sql (100%) rename {internal/hub => hub/internal}/store/store.go (86%) rename {internal/hub => hub/internal}/utils/hasher/id.go (90%) rename {internal/shared => shared}/config/config.go (100%) rename {internal/shared => shared}/config/config_test.go (100%) rename {internal => shared}/domain/agent.go (100%) rename {internal => shared}/domain/mapper.go (100%) rename {internal/shared => shared}/log/init.go (100%) diff --git a/cmd/agent/main.go b/agent/cmd/main.go similarity index 59% rename from cmd/agent/main.go rename to agent/cmd/main.go index 67febdd..21839cc 100644 --- a/cmd/agent/main.go +++ b/agent/cmd/main.go @@ -1,6 +1,8 @@ package main -import "github.com/lorsanstand/HomeOps-Hub/internal/agent/app" +import ( + "github.com/lorsanstand/HomeOps-Hub/agent/internal/app" +) func main() { start, err := app.NewApp() diff --git a/internal/agent/app/app.go b/agent/internal/app/app.go similarity index 76% rename from internal/agent/app/app.go rename to agent/internal/app/app.go index a588b6d..679bca8 100644 --- a/internal/agent/app/app.go +++ b/agent/internal/app/app.go @@ -5,13 +5,13 @@ import ( standartlog "log" "github.com/docker/docker/client" - "github.com/lorsanstand/HomeOps-Hub/internal/agent/rpc" - "github.com/lorsanstand/HomeOps-Hub/internal/agent/service/agent_service" - "github.com/lorsanstand/HomeOps-Hub/internal/agent/service/collector" - "github.com/lorsanstand/HomeOps-Hub/internal/agent/service/docker_service" - "github.com/lorsanstand/HomeOps-Hub/internal/agent/utils/config_yaml" - "github.com/lorsanstand/HomeOps-Hub/internal/agent/utils/settings" - log2 "github.com/lorsanstand/HomeOps-Hub/internal/shared/log" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/rpc" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/service/agent_service" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/service/collector" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/service/docker_service" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/config_yaml" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/settings" + log2 "github.com/lorsanstand/HomeOps-Hub/shared/log" "github.com/rs/zerolog" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -25,7 +25,6 @@ type App struct { } func NewApp() (*App, error) { - cfg, err := config_yaml.NewConfig() if err != nil { standartlog.Fatalf("failed to get config: %v", err) @@ -49,7 +48,7 @@ func (a *App) Run() { GRPCConn, err := grpc.NewClient(a.cfg.GetGRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { - a.log.Error().Err(err).Msg("failed to get hub connections") + a.log.Error().Err(err).Msg("failed to get agent connections") return } diff --git a/internal/agent/rpc/client.go b/agent/internal/rpc/client.go similarity index 94% rename from internal/agent/rpc/client.go rename to agent/internal/rpc/client.go index a945085..35c8d38 100644 --- a/internal/agent/rpc/client.go +++ b/agent/internal/rpc/client.go @@ -4,7 +4,7 @@ import ( "context" pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" "github.com/rs/zerolog" "google.golang.org/grpc" ) diff --git a/internal/agent/service/agent_service/agent.go b/agent/internal/service/agent_service/agent.go similarity index 77% rename from internal/agent/service/agent_service/agent.go rename to agent/internal/service/agent_service/agent.go index af9f050..ad49af3 100644 --- a/internal/agent/service/agent_service/agent.go +++ b/agent/internal/service/agent_service/agent.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/lorsanstand/HomeOps-Hub/internal/agent/utils/config_yaml" - "github.com/lorsanstand/HomeOps-Hub/internal/agent/utils/settings" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/config_yaml" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/settings" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" "github.com/rs/zerolog" ) @@ -36,7 +36,7 @@ func NewAgentService( cfg *config_yaml.AgentConfig, logger zerolog.Logger, ) *AgentService { - logger = logger.With().Str("component", "agent.service.agent_serivce").Logger() + logger = logger.With().Str("component", "cmd.service.agent_serivce").Logger() return &AgentService{collect: collector, conn: conn, cfg: cfg, log: logger, settings: settings} } @@ -49,12 +49,12 @@ func (a *AgentService) RegisterAgentConn(ctx context.Context) { data, err := a.conn.RegisterAgent(ctx, AgentData) if err != nil { - a.log.Error().Err(err).Msg("failed register agent") + a.log.Error().Err(err).Msg("failed register cmd") return } if err = a.settings.Insert(settings.Settings{AgentID: data.AgentID}); err != nil { - a.log.Warn().Err(err).Msg("failed to save agent id") + a.log.Warn().Err(err).Msg("failed to save cmd id") } fmt.Println(data) } diff --git a/internal/agent/service/agent_service/agent_test.go b/agent/internal/service/agent_service/agent_test.go similarity index 90% rename from internal/agent/service/agent_service/agent_test.go rename to agent/internal/service/agent_service/agent_test.go index b6852ac..5c3bb2c 100644 --- a/internal/agent/service/agent_service/agent_test.go +++ b/agent/internal/service/agent_service/agent_test.go @@ -3,7 +3,7 @@ package agent_service import ( "context" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" ) type CollectorMock struct { diff --git a/internal/agent/service/collector/collector.go b/agent/internal/service/collector/collector.go similarity index 84% rename from internal/agent/service/collector/collector.go rename to agent/internal/service/collector/collector.go index 93cea10..36aac43 100644 --- a/internal/agent/service/collector/collector.go +++ b/agent/internal/service/collector/collector.go @@ -4,7 +4,7 @@ import ( "os" "runtime" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" "github.com/rs/zerolog" ) @@ -18,7 +18,7 @@ type Collector struct { } func NewCollector(docker Docker, logger zerolog.Logger) *Collector { - logger = logger.With().Str("component", "agent.service.collector").Logger() + logger = logger.With().Str("component", "cmd.service.collector").Logger() return &Collector{log: logger, dockerReader: docker} } diff --git a/internal/agent/service/docker_service/bad.go b/agent/internal/service/docker_service/bad.go similarity index 85% rename from internal/agent/service/docker_service/bad.go rename to agent/internal/service/docker_service/bad.go index 6c48587..ed02d48 100644 --- a/internal/agent/service/docker_service/bad.go +++ b/agent/internal/service/docker_service/bad.go @@ -1,7 +1,7 @@ package docker_service import ( - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" ) type BadDocker struct { diff --git a/internal/agent/service/docker_service/docker.go b/agent/internal/service/docker_service/docker.go similarity index 89% rename from internal/agent/service/docker_service/docker.go rename to agent/internal/service/docker_service/docker.go index 487b1e1..43c950b 100644 --- a/internal/agent/service/docker_service/docker.go +++ b/agent/internal/service/docker_service/docker.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" "github.com/rs/zerolog" ) @@ -23,7 +23,7 @@ type DockerService struct { func NewDockerService(api dockerAPI, logger zerolog.Logger) *DockerService { return &DockerService{ dockerClient: api, - log: logger.With().Str("component", "agent.serivce.docker").Logger(), + log: logger.With().Str("component", "cmd.serivce.docker").Logger(), } } diff --git a/internal/agent/service/docker_service/docker_test.go b/agent/internal/service/docker_service/docker_test.go similarity index 100% rename from internal/agent/service/docker_service/docker_test.go rename to agent/internal/service/docker_service/docker_test.go diff --git a/internal/agent/utils/config_yaml/config.go b/agent/internal/utils/config_yaml/config.go similarity index 100% rename from internal/agent/utils/config_yaml/config.go rename to agent/internal/utils/config_yaml/config.go diff --git a/internal/agent/utils/config_yaml/config_yaml_test.go b/agent/internal/utils/config_yaml/config_yaml_test.go similarity index 100% rename from internal/agent/utils/config_yaml/config_yaml_test.go rename to agent/internal/utils/config_yaml/config_yaml_test.go diff --git a/internal/agent/utils/settings/settings.go b/agent/internal/utils/settings/settings.go similarity index 100% rename from internal/agent/utils/settings/settings.go rename to agent/internal/utils/settings/settings.go diff --git a/api/gen/homeops/hub.pb.go b/api/gen/homeops/hub.pb.go index 284f28c..e8aad80 100644 --- a/api/gen/homeops/hub.pb.go +++ b/api/gen/homeops/hub.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.36.11 // protoc v7.34.1 -// source: homeops/hub.proto +// source: homeops/cmd.proto package homeops @@ -326,7 +326,7 @@ var File_homeops_hub_proto protoreflect.FileDescriptor const file_homeops_hub_proto_rawDesc = "" + "\n" + - "\x11homeops/hub.proto\x1a\x1bgoogle/protobuf/empty.proto\"\"\n" + + "\x11homeops/cmd.proto\x1a\x1bgoogle/protobuf/empty.proto\"\"\n" + "\fPongResponse\x12\x12\n" + "\x04pong\x18\x01 \x01(\tR\x04pong\"\xb6\x01\n" + "\x14RegisterAgentRequest\x12\x19\n" + diff --git a/api/gen/homeops/hub_grpc.pb.go b/api/gen/homeops/hub_grpc.pb.go index e032d6c..c5a605c 100644 --- a/api/gen/homeops/hub_grpc.pb.go +++ b/api/gen/homeops/hub_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc v7.34.1 -// source: homeops/hub.proto +// source: homeops/cmd.proto package homeops @@ -156,5 +156,5 @@ var Hub_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "homeops/hub.proto", + Metadata: "homeops/cmd.proto", } diff --git a/cmd/telegram/main.go b/cmd/telegram/main.go deleted file mode 100644 index 06ab7d0..0000000 --- a/cmd/telegram/main.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/cmd/hub/main.go b/hub/cmd/main.go similarity index 50% rename from cmd/hub/main.go rename to hub/cmd/main.go index afc06ec..5e2033c 100644 --- a/cmd/hub/main.go +++ b/hub/cmd/main.go @@ -1,6 +1,8 @@ package main -import "github.com/lorsanstand/HomeOps-Hub/internal/hub/app" +import ( + "github.com/lorsanstand/HomeOps-Hub/hub/internal/app" +) func main() { start := app.NewApp() diff --git a/internal/hub/app/app.go b/hub/internal/app/app.go similarity index 84% rename from internal/hub/app/app.go rename to hub/internal/app/app.go index 56652b0..5f1a1b5 100644 --- a/internal/hub/app/app.go +++ b/hub/internal/app/app.go @@ -8,13 +8,13 @@ import ( "net" "github.com/jackc/pgx/v5/pgxpool" - hubdir "github.com/lorsanstand/HomeOps-Hub/internal/hub" - "github.com/lorsanstand/HomeOps-Hub/internal/hub/migrator" - grpcserv "github.com/lorsanstand/HomeOps-Hub/internal/hub/rpc" - "github.com/lorsanstand/HomeOps-Hub/internal/hub/service/hub_service" - "github.com/lorsanstand/HomeOps-Hub/internal/hub/store" - "github.com/lorsanstand/HomeOps-Hub/internal/shared/config" - "github.com/lorsanstand/HomeOps-Hub/internal/shared/log" + hubdir "github.com/lorsanstand/HomeOps-Hub/hub/internal" + "github.com/lorsanstand/HomeOps-Hub/hub/internal/migrator" + grpcserv "github.com/lorsanstand/HomeOps-Hub/hub/internal/rpc" + "github.com/lorsanstand/HomeOps-Hub/hub/internal/service/hub_service" + "github.com/lorsanstand/HomeOps-Hub/hub/internal/store" + "github.com/lorsanstand/HomeOps-Hub/shared/config" + "github.com/lorsanstand/HomeOps-Hub/shared/log" "github.com/rs/zerolog" ) diff --git a/internal/hub/domain/structure.go b/hub/internal/domain/structure.go similarity index 89% rename from internal/hub/domain/structure.go rename to hub/internal/domain/structure.go index 795fe89..79bea4a 100644 --- a/internal/hub/domain/structure.go +++ b/hub/internal/domain/structure.go @@ -3,7 +3,7 @@ package domain import ( "time" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" ) type CreateAgentModel struct { diff --git a/internal/hub/embed.go b/hub/internal/embed.go similarity index 80% rename from internal/hub/embed.go rename to hub/internal/embed.go index 1938b80..e4740ca 100644 --- a/internal/hub/embed.go +++ b/hub/internal/embed.go @@ -1,4 +1,4 @@ -package hub +package internal import "embed" diff --git a/internal/hub/migrations/20260415151037_create_agent_table.down.sql b/hub/internal/migrations/20260415151037_create_agent_table.down.sql similarity index 100% rename from internal/hub/migrations/20260415151037_create_agent_table.down.sql rename to hub/internal/migrations/20260415151037_create_agent_table.down.sql diff --git a/internal/hub/migrations/20260415151037_create_agent_table.up.sql b/hub/internal/migrations/20260415151037_create_agent_table.up.sql similarity index 100% rename from internal/hub/migrations/20260415151037_create_agent_table.up.sql rename to hub/internal/migrations/20260415151037_create_agent_table.up.sql diff --git a/internal/hub/migrator/migrator.go b/hub/internal/migrator/migrator.go similarity index 99% rename from internal/hub/migrator/migrator.go rename to hub/internal/migrator/migrator.go index 22338f8..9f1effe 100644 --- a/internal/hub/migrator/migrator.go +++ b/hub/internal/migrator/migrator.go @@ -5,6 +5,7 @@ import ( "embed" "errors" "fmt" + _ "github.com/jackc/pgx/v5/stdlib" "github.com/golang-migrate/migrate/v4" diff --git a/internal/hub/rpc/server.go b/hub/internal/rpc/server.go similarity index 96% rename from internal/hub/rpc/server.go rename to hub/internal/rpc/server.go index 1a5aade..da37823 100644 --- a/internal/hub/rpc/server.go +++ b/hub/internal/rpc/server.go @@ -4,7 +4,7 @@ import ( "context" pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" "github.com/rs/zerolog" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" diff --git a/internal/hub/service/hub_service/hub.go b/hub/internal/service/hub_service/hub.go similarity index 93% rename from internal/hub/service/hub_service/hub.go rename to hub/internal/service/hub_service/hub.go index aa86b1a..f086429 100644 --- a/internal/hub/service/hub_service/hub.go +++ b/hub/internal/service/hub_service/hub.go @@ -6,9 +6,9 @@ import ( "errors" "fmt" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" - domainHub "github.com/lorsanstand/HomeOps-Hub/internal/hub/domain" - "github.com/lorsanstand/HomeOps-Hub/internal/hub/utils/hasher" + domainHub "github.com/lorsanstand/HomeOps-Hub/hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/hub/internal/utils/hasher" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" "github.com/rs/zerolog" ) diff --git a/internal/hub/service/hub_service/mapper.go b/hub/internal/service/hub_service/mapper.go similarity index 77% rename from internal/hub/service/hub_service/mapper.go rename to hub/internal/service/hub_service/mapper.go index 6a020d8..e28c843 100644 --- a/internal/hub/service/hub_service/mapper.go +++ b/hub/internal/service/hub_service/mapper.go @@ -1,8 +1,8 @@ package hub_service import ( - "github.com/lorsanstand/HomeOps-Hub/internal/domain" - domainHub "github.com/lorsanstand/HomeOps-Hub/internal/hub/domain" + domainHub "github.com/lorsanstand/HomeOps-Hub/hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" ) func toCreateAgentModel(agent domain.RegisterAgentRequest) domainHub.CreateAgentModel { diff --git a/internal/hub/store/mapper.go b/hub/internal/store/mapper.go similarity index 75% rename from internal/hub/store/mapper.go rename to hub/internal/store/mapper.go index cbce0c3..90e2290 100644 --- a/internal/hub/store/mapper.go +++ b/hub/internal/store/mapper.go @@ -3,13 +3,13 @@ package store import ( "encoding/json" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" - domainHub "github.com/lorsanstand/HomeOps-Hub/internal/hub/domain" - "github.com/lorsanstand/HomeOps-Hub/internal/hub/store/sqlc/gen" + domainHub "github.com/lorsanstand/HomeOps-Hub/hub/internal/domain" + gen2 "github.com/lorsanstand/HomeOps-Hub/hub/internal/store/sqlc/gen" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" ) -func toDBAgent(agent domainHub.CreateAgentModel) gen.CreateAgentParams { - return gen.CreateAgentParams{ +func toDBAgent(agent domainHub.CreateAgentModel) gen2.CreateAgentParams { + return gen2.CreateAgentParams{ AgentID: agent.AgentID, AgentName: &agent.AgentName, Architecture: agent.Architecture, @@ -20,8 +20,8 @@ func toDBAgent(agent domainHub.CreateAgentModel) gen.CreateAgentParams { } } -func toUpdateDBAgent(agent domainHub.CreateAgentModel) gen.UpdateAgentByIDParams { - return gen.UpdateAgentByIDParams{ +func toUpdateDBAgent(agent domainHub.CreateAgentModel) gen2.UpdateAgentByIDParams { + return gen2.UpdateAgentByIDParams{ AgentID: agent.AgentID, AgentName: &agent.AgentName, Architecture: agent.Architecture, @@ -41,7 +41,7 @@ func toJsonCapabilities(caps []domain.Capability) []byte { return data } -func toAgentModel(dbAgent gen.Agent) domainHub.AgentModel { +func toAgentModel(dbAgent gen2.Agent) domainHub.AgentModel { var dbAgentName string if dbAgent.AgentName != nil { dbAgentName = *dbAgent.AgentName diff --git a/internal/hub/store/sqlc/gen/agent.sql.go b/hub/internal/store/sqlc/gen/agent.sql.go similarity index 99% rename from internal/hub/store/sqlc/gen/agent.sql.go rename to hub/internal/store/sqlc/gen/agent.sql.go index 6a6e23c..3d55d93 100644 --- a/internal/hub/store/sqlc/gen/agent.sql.go +++ b/hub/internal/store/sqlc/gen/agent.sql.go @@ -1,7 +1,7 @@ // Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.30.0 -// source: agent.sql +// source: cmd.sql package gen diff --git a/internal/hub/store/sqlc/gen/db.go b/hub/internal/store/sqlc/gen/db.go similarity index 100% rename from internal/hub/store/sqlc/gen/db.go rename to hub/internal/store/sqlc/gen/db.go diff --git a/internal/hub/store/sqlc/gen/models.go b/hub/internal/store/sqlc/gen/models.go similarity index 100% rename from internal/hub/store/sqlc/gen/models.go rename to hub/internal/store/sqlc/gen/models.go diff --git a/internal/hub/store/sqlc/queries/agent.sql b/hub/internal/store/sqlc/queries/agent.sql similarity index 100% rename from internal/hub/store/sqlc/queries/agent.sql rename to hub/internal/store/sqlc/queries/agent.sql diff --git a/internal/hub/store/store.go b/hub/internal/store/store.go similarity index 86% rename from internal/hub/store/store.go rename to hub/internal/store/store.go index 02936d4..851eec9 100644 --- a/internal/hub/store/store.go +++ b/hub/internal/store/store.go @@ -4,8 +4,8 @@ import ( "context" "github.com/jackc/pgx/v5/pgxpool" - domainHub "github.com/lorsanstand/HomeOps-Hub/internal/hub/domain" - "github.com/lorsanstand/HomeOps-Hub/internal/hub/store/sqlc/gen" + domainHub "github.com/lorsanstand/HomeOps-Hub/hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/hub/internal/store/sqlc/gen" ) type HubStore struct { diff --git a/internal/hub/utils/hasher/id.go b/hub/internal/utils/hasher/id.go similarity index 90% rename from internal/hub/utils/hasher/id.go rename to hub/internal/utils/hasher/id.go index e28b504..21f94f8 100644 --- a/internal/hub/utils/hasher/id.go +++ b/hub/internal/utils/hasher/id.go @@ -6,7 +6,7 @@ import ( "encoding/hex" "fmt" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" ) func newSalt(n int) ([]byte, error) { diff --git a/internal/shared/config/config.go b/shared/config/config.go similarity index 100% rename from internal/shared/config/config.go rename to shared/config/config.go diff --git a/internal/shared/config/config_test.go b/shared/config/config_test.go similarity index 100% rename from internal/shared/config/config_test.go rename to shared/config/config_test.go diff --git a/internal/domain/agent.go b/shared/domain/agent.go similarity index 100% rename from internal/domain/agent.go rename to shared/domain/agent.go diff --git a/internal/domain/mapper.go b/shared/domain/mapper.go similarity index 100% rename from internal/domain/mapper.go rename to shared/domain/mapper.go diff --git a/internal/shared/log/init.go b/shared/log/init.go similarity index 100% rename from internal/shared/log/init.go rename to shared/log/init.go diff --git a/sqlc.yaml b/sqlc.yaml index 4e02b14..5c97fd8 100644 --- a/sqlc.yaml +++ b/sqlc.yaml @@ -2,11 +2,11 @@ version: "2" sql: - engine: "postgresql" - queries: "internal/hub/store/sqlc/queries" - schema: "./internal/hub/migrations/" + queries: "hub/internal/store/sqlc/queries" + schema: "./hub/internal/migrations/" gen: go: sql_package: "pgx/v5" package: "gen" - out: "internal/hub/store/sqlc/gen" + out: "hub/internal/store/sqlc/gen" emit_pointers_for_null_types: true \ No newline at end of file From 4dfbe1ad7f4d53bd45710cfc1b406e105e90e536 Mon Sep 17 00:00:00 2001 From: lorsan Date: Sun, 3 May 2026 22:29:14 +0300 Subject: [PATCH 2/6] refactor: change system logging --- .gitignore | 2 +- agent/cmd/main.go | 2 +- agent/internal/app/app.go | 21 +++++++++------- agent/internal/rpc/client.go | 11 ++------- agent/internal/service/agent_service/agent.go | 24 ++++++++++++------- agent/internal/service/collector/collector.go | 2 +- .../internal/service/docker_service/docker.go | 4 ++-- agent/internal/utils/config_yaml/config.go | 11 +++++++-- 8 files changed, 44 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index d19b928..65cbde7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ .env -agent.dev.yaml \ No newline at end of file +config.yaml \ No newline at end of file diff --git a/agent/cmd/main.go b/agent/cmd/main.go index 21839cc..9fcdbb1 100644 --- a/agent/cmd/main.go +++ b/agent/cmd/main.go @@ -7,7 +7,7 @@ import ( func main() { start, err := app.NewApp() if err != nil { - return + panic(err) } start.Run() } diff --git a/agent/internal/app/app.go b/agent/internal/app/app.go index 679bca8..e6b7736 100644 --- a/agent/internal/app/app.go +++ b/agent/internal/app/app.go @@ -11,7 +11,7 @@ import ( "github.com/lorsanstand/HomeOps-Hub/agent/internal/service/docker_service" "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/config_yaml" "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/settings" - log2 "github.com/lorsanstand/HomeOps-Hub/shared/log" + "github.com/lorsanstand/HomeOps-Hub/shared/log" "github.com/rs/zerolog" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -31,16 +31,16 @@ func NewApp() (*App, error) { return nil, err } - log := log2.NewLogger(cfg) - log = log.With().Str("component", "agent.app").Logger() + logger := log.NewLogger(cfg) + logger = logger.With().Str("component", "internal.app").Logger() sett, err := settings.ReadSettings(cfg.SettingsPath) if err != nil { - log.Error().Err(err).Msg("failed to get settings") + logger.Error().Err(err).Msg("failed to get settings") return nil, err } - return &App{cfg: cfg, log: log, settings: sett}, nil + return &App{cfg: cfg, log: logger, settings: sett}, nil } func (a *App) Run() { @@ -48,11 +48,12 @@ func (a *App) Run() { GRPCConn, err := grpc.NewClient(a.cfg.GetGRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { - a.log.Error().Err(err).Msg("failed to get agent connections") + a.log.Error().Err(err).Msg("failed to connection hub") return } + a.log.Info().Msg("connection to the hub successful") - conn := rpc.NewConnectAgent(GRPCConn, a.log) + conn := rpc.NewConnectAgent(GRPCConn) defer conn.Close() var DockerService collector.Docker @@ -62,11 +63,15 @@ func (a *App) Run() { a.log.Warn().Err(err).Msg("failed to get docker API") DockerService = docker_service.NewBadDocker("not_installed") } else { + a.log.Info().Msg("successfully to get docker API") DockerService = docker_service.NewDockerService(DockerClient, a.log) } collect := collector.NewCollector(DockerService, a.log) agent := agent_service.NewAgentService(collect, conn, a.settings, a.cfg, a.log) - agent.RegisterAgentConn(ctx) + if err := agent.RegisterAgentConn(ctx); err != nil { + a.log.Error().Err(err).Msg("failed to agent registration") + } + a.log.Info().Msg("agent registration complete") } diff --git a/agent/internal/rpc/client.go b/agent/internal/rpc/client.go index 35c8d38..23f9ea3 100644 --- a/agent/internal/rpc/client.go +++ b/agent/internal/rpc/client.go @@ -5,26 +5,20 @@ import ( pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" "github.com/lorsanstand/HomeOps-Hub/shared/domain" - "github.com/rs/zerolog" "google.golang.org/grpc" ) type Connection struct { hub pb.HubClient conn *grpc.ClientConn - log zerolog.Logger } -func NewConnectAgent(conn *grpc.ClientConn, logger zerolog.Logger) *Connection { - logger = logger.With().Str("component", "agent.rpc").Logger() - +func NewConnectAgent(conn *grpc.ClientConn) *Connection { client := pb.NewHubClient(conn) - - return &Connection{hub: client, conn: conn, log: logger} + return &Connection{hub: client, conn: conn} } func (c *Connection) Close() error { - c.log.Warn().Msg("connection close") return c.conn.Close() } @@ -34,6 +28,5 @@ func (c *Connection) Hub() pb.HubClient { func (c *Connection) RegisterAgent(ctx context.Context, RegisterData domain.RegisterAgentRequest) (domain.RegisterAgentResponse, error) { ResponseData, err := c.Hub().RegisterAgent(ctx, new(domain.ToGRPCAgentRequest(RegisterData))) - c.log.Info().Msg("register agent") return domain.ToDomainAgentResponse(ResponseData), err } diff --git a/agent/internal/service/agent_service/agent.go b/agent/internal/service/agent_service/agent.go index ad49af3..1601516 100644 --- a/agent/internal/service/agent_service/agent.go +++ b/agent/internal/service/agent_service/agent.go @@ -10,8 +10,6 @@ import ( "github.com/rs/zerolog" ) -const AgentVersion = "0.0" - type Collector interface { GatherInfoSystem() (domain.HostInfo, []domain.Capability) } @@ -36,25 +34,33 @@ func NewAgentService( cfg *config_yaml.AgentConfig, logger zerolog.Logger, ) *AgentService { - logger = logger.With().Str("component", "cmd.service.agent_serivce").Logger() + logger = logger.With().Str("component", "internal.service.agent_serivce").Logger() return &AgentService{collect: collector, conn: conn, cfg: cfg, log: logger, settings: settings} } -func (a *AgentService) RegisterAgentConn(ctx context.Context) { +func (a *AgentService) RegisterAgentConn(ctx context.Context) error { + a.log.Debug().Msg("getting info by system") info, caps := a.collect.GatherInfoSystem() + a.log.Debug().Msg("create request data for register agent") AgentID := a.settings.AgentID AgentName := a.cfg.AppName - AgentData := domain.RegisterAgentRequest{AgentId: AgentID, AgentName: AgentName, Host: info, Capabilities: caps, AgentVersion: AgentVersion} + AgentData := domain.RegisterAgentRequest{ + AgentId: AgentID, + AgentName: AgentName, + Host: info, + Capabilities: caps, + AgentVersion: a.cfg.GetAgentVersion(), + } data, err := a.conn.RegisterAgent(ctx, AgentData) if err != nil { - a.log.Error().Err(err).Msg("failed register cmd") - return + return fmt.Errorf("register agent: %w", err) } if err = a.settings.Insert(settings.Settings{AgentID: data.AgentID}); err != nil { - a.log.Warn().Err(err).Msg("failed to save cmd id") + return fmt.Errorf("save agent ID: %w", err) } - fmt.Println(data) + + return nil } diff --git a/agent/internal/service/collector/collector.go b/agent/internal/service/collector/collector.go index 36aac43..e0351ed 100644 --- a/agent/internal/service/collector/collector.go +++ b/agent/internal/service/collector/collector.go @@ -18,7 +18,7 @@ type Collector struct { } func NewCollector(docker Docker, logger zerolog.Logger) *Collector { - logger = logger.With().Str("component", "cmd.service.collector").Logger() + logger = logger.With().Str("component", "internal.service.collector").Logger() return &Collector{log: logger, dockerReader: docker} } diff --git a/agent/internal/service/docker_service/docker.go b/agent/internal/service/docker_service/docker.go index 43c950b..825878c 100644 --- a/agent/internal/service/docker_service/docker.go +++ b/agent/internal/service/docker_service/docker.go @@ -23,13 +23,13 @@ type DockerService struct { func NewDockerService(api dockerAPI, logger zerolog.Logger) *DockerService { return &DockerService{ dockerClient: api, - log: logger.With().Str("component", "cmd.serivce.docker").Logger(), + log: logger.With().Str("component", "internal.serivce.docker").Logger(), } } func (d *DockerService) CheckDockerDaemon(ctx context.Context) error { _, err := d.dockerClient.Ping(ctx) - d.log.Debug().Msg("check docker") + d.log.Debug().Msg("ping docker") return err } diff --git a/agent/internal/utils/config_yaml/config.go b/agent/internal/utils/config_yaml/config.go index 9d24106..f8d4b1b 100644 --- a/agent/internal/utils/config_yaml/config.go +++ b/agent/internal/utils/config_yaml/config.go @@ -8,6 +8,9 @@ import ( "gopkg.in/yaml.v3" ) +const MODE = "DEV" +const AgentVersion = "0.0" + type AgentConfig struct { AppName string `yaml:"app_name"` HubConnect struct { @@ -19,7 +22,7 @@ type AgentConfig struct { } func NewConfig() (*AgentConfig, error) { - yamlFile, err := os.ReadFile("agent.dev.yaml") + yamlFile, err := os.ReadFile("config.yaml") if err != nil { return nil, fmt.Errorf("failed open file: %v", err) } @@ -42,7 +45,11 @@ func (c *AgentConfig) GetLogLevel() zerolog.Level { } func (c *AgentConfig) GetMode() string { - return "DEV" + return MODE +} + +func (c *AgentConfig) GetAgentVersion() string { + return AgentVersion } func (c *AgentConfig) GetGRPCAddress() string { From 8d9e748d7a942b9747c2e81a79a69736dbbda3ed Mon Sep 17 00:00:00 2001 From: lorsan Date: Mon, 4 May 2026 08:52:13 +0300 Subject: [PATCH 3/6] refactor: change system logging in hub --- hub/internal/app/app.go | 2 +- hub/internal/rpc/server.go | 6 +++--- hub/internal/service/hub_service/hub.go | 24 ++++++++++-------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/hub/internal/app/app.go b/hub/internal/app/app.go index 5f1a1b5..9b825c3 100644 --- a/hub/internal/app/app.go +++ b/hub/internal/app/app.go @@ -27,6 +27,7 @@ func NewApp() *App { cfg, err := config.NewConfig() if err != nil { standartlog.Fatalf("failed get config: %v", err) + return nil } logger := log.NewLogger(cfg) @@ -80,7 +81,6 @@ func (a *App) Run() { func (a *App) hubServe(hubService *hub_service.HubService) error { address := fmt.Sprintf("0.0.0.0:%v", a.cfg.Port) - a.log.Info().Str("address", address).Msg("starting gRPC server") server := grpcserv.NewHubHandler(hubService, a.log) diff --git a/hub/internal/rpc/server.go b/hub/internal/rpc/server.go index da37823..d9de289 100644 --- a/hub/internal/rpc/server.go +++ b/hub/internal/rpc/server.go @@ -38,13 +38,13 @@ func (h *HubHandler) Ping(ctx context.Context, _ *emptypb.Empty) (*pb.PongRespon } func (h *HubHandler) RegisterAgent(ctx context.Context, request *pb.RegisterAgentRequest) (*pb.RegisterAgentResponse, error) { - h.log.Debug().Str("agentId", request.AgentId).Str("agentName", request.AgentName).Msg("register agent request received") + h.log.Debug().Str("agentID", request.AgentId).Str("agentName", request.AgentName).Msg("register agent request received") data := domain.ToDomainAgentRequest(request) resp, err := h.hub.RegisterAgent(ctx, data) if err != nil { - h.log.Error().Err(err).Str("agentId", request.AgentId).Msg("register agent request failed") + h.log.Error().Err(err).Str("agentID", request.AgentId).Msg("register agent request failed") return domain.ToGRPCAgentResponse(resp), err } - h.log.Debug().Str("agentId", resp.AgentID).Msg("register agent request completed") + h.log.Info().Str("agentID", resp.AgentID).Msg("register agent request completed") return domain.ToGRPCAgentResponse(resp), nil } diff --git a/hub/internal/service/hub_service/hub.go b/hub/internal/service/hub_service/hub.go index f086429..d9b7cae 100644 --- a/hub/internal/service/hub_service/hub.go +++ b/hub/internal/service/hub_service/hub.go @@ -12,6 +12,8 @@ import ( "github.com/rs/zerolog" ) +const HEARTBEAT = 5 + type Store interface { NewAgent(ctx context.Context, agent domainHub.CreateAgentModel) error GetAgentByAgentID(ctx context.Context, AgentID string) (domainHub.AgentModel, error) @@ -28,32 +30,29 @@ func NewHubService(store Store, logger zerolog.Logger) *HubService { } func (h *HubService) RegisterAgent(ctx context.Context, data domain.RegisterAgentRequest) (domain.RegisterAgentResponse, error) { - h.log.Debug().Str("agentId", data.AgentId).Str("agentName", data.AgentName).Msg("started registering agent") + h.log.Debug().Str("agentID", data.AgentId).Str("agentName", data.AgentName).Msg("started registering agent") agent, err := h.store.GetAgentByAgentID(ctx, data.AgentId) if err != nil && !errors.Is(err, sql.ErrNoRows) { - h.log.Error().Err(err).Str("agentId", data.AgentId).Msg("failed to get agent from database") return domain.RegisterAgentResponse{}, fmt.Errorf("failed select agent to db: %w", err) } if data.AgentId != "" && !errors.Is(err, sql.ErrNoRows) { - h.log.Debug().Str("agentId", agent.AgentID).Str("agentName", data.AgentName).Msg("agent exists, updating") + h.log.Debug().Str("agentID", agent.AgentID).Str("agentName", data.AgentName).Msg("agent exists, updating") data.AgentId = agent.AgentID agentStore := toCreateAgentModel(data) if err := h.store.UpdateAgentByID(ctx, agent.ID, agentStore); err != nil { - h.log.Error().Err(err).Str("agentId", agent.AgentID).Msg("failed to update agent in database") - return domain.RegisterAgentResponse{}, err + return domain.RegisterAgentResponse{}, fmt.Errorf("update agent in db: %w", err) } - h.log.Info().Str("agentId", agent.AgentID).Msg("agent updated successfully") - return domain.RegisterAgentResponse{AgentID: agent.AgentID, Heartbeat: 5}, nil + h.log.Debug().Str("agentId", agent.AgentID).Msg("agent updated successfully") + return domain.RegisterAgentResponse{AgentID: agent.AgentID, Heartbeat: HEARTBEAT}, nil } AgentID, err := hasher.MakeID(data.Host, data.AgentName) if err != nil { - h.log.Error().Err(err).Str("agentName", data.AgentName).Str("hostname", data.Host.Hostname).Msg("failed to generate agent id") - return domain.RegisterAgentResponse{}, err + return domain.RegisterAgentResponse{}, fmt.Errorf("generate agent ID: %w", err) } data.AgentId = AgentID @@ -61,10 +60,7 @@ func (h *HubService) RegisterAgent(ctx context.Context, data domain.RegisterAgen agentStore := toCreateAgentModel(data) if err := h.store.NewAgent(ctx, agentStore); err != nil { - h.log.Error().Err(err).Str("agentId", AgentID).Str("agentName", data.AgentName).Msg("failed to create new agent in database") - return domain.RegisterAgentResponse{}, err + return domain.RegisterAgentResponse{}, fmt.Errorf("insert new agent: %w", err) } - - h.log.Info().Str("agentId", AgentID).Str("agentName", data.AgentName).Str("hostname", data.Host.Hostname).Msg("agent registered successfully") - return domain.RegisterAgentResponse{AgentID: AgentID, Heartbeat: 5}, nil + return domain.RegisterAgentResponse{AgentID: AgentID, Heartbeat: HEARTBEAT}, nil } From 322f586ba4e330fb589cd9221196588ba727885d Mon Sep 17 00:00:00 2001 From: lorsan Date: Mon, 4 May 2026 09:37:37 +0300 Subject: [PATCH 4/6] refactor: change structure settings to interface --- agent/internal/service/agent_service/agent.go | 15 ++++++++++----- agent/internal/utils/settings/settings.go | 8 +++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/agent/internal/service/agent_service/agent.go b/agent/internal/service/agent_service/agent.go index 1601516..17e7330 100644 --- a/agent/internal/service/agent_service/agent.go +++ b/agent/internal/service/agent_service/agent.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/config_yaml" - "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/settings" "github.com/lorsanstand/HomeOps-Hub/shared/domain" "github.com/rs/zerolog" ) @@ -14,6 +13,11 @@ type Collector interface { GatherInfoSystem() (domain.HostInfo, []domain.Capability) } +type Settings interface { + InsertAgentID(agentID string) error + GetAgentID() string +} + type HubConnection interface { RegisterAgent(ctx context.Context, RegisterData domain.RegisterAgentRequest) (domain.RegisterAgentResponse, error) } @@ -24,13 +28,13 @@ type AgentService struct { log zerolog.Logger cfg *config_yaml.AgentConfig heartBeat int - settings *settings.Settings + settings Settings } func NewAgentService( collector Collector, conn HubConnection, - settings *settings.Settings, + settings Settings, cfg *config_yaml.AgentConfig, logger zerolog.Logger, ) *AgentService { @@ -43,7 +47,7 @@ func (a *AgentService) RegisterAgentConn(ctx context.Context) error { a.log.Debug().Msg("getting info by system") info, caps := a.collect.GatherInfoSystem() a.log.Debug().Msg("create request data for register agent") - AgentID := a.settings.AgentID + AgentID := a.settings.GetAgentID() AgentName := a.cfg.AppName AgentData := domain.RegisterAgentRequest{ AgentId: AgentID, @@ -58,9 +62,10 @@ func (a *AgentService) RegisterAgentConn(ctx context.Context) error { return fmt.Errorf("register agent: %w", err) } - if err = a.settings.Insert(settings.Settings{AgentID: data.AgentID}); err != nil { + if err = a.settings.InsertAgentID(data.AgentID); err != nil { return fmt.Errorf("save agent ID: %w", err) } + a.log.Info().Str("AgentID", data.AgentID).Msg("agent registration end") return nil } diff --git a/agent/internal/utils/settings/settings.go b/agent/internal/utils/settings/settings.go index 1632780..b7ca57c 100644 --- a/agent/internal/utils/settings/settings.go +++ b/agent/internal/utils/settings/settings.go @@ -51,16 +51,22 @@ func ReadSettings(path string) (*Settings, error) { return &settings, nil } -func (s *Settings) Insert(sett Settings) error { +func (s *Settings) InsertAgentID(agentID string) error { file, err := os.OpenFile(s.path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } defer file.Close() + sett := Settings{AgentID: agentID} + if err = json.NewEncoder(file).Encode(sett); err != nil { return err } return nil } + +func (s *Settings) GetAgentID() string { + return s.AgentID +} From ea5cca09f567c320656c656ed1cf2f08c3f53c89 Mon Sep 17 00:00:00 2001 From: lorsan Date: Mon, 4 May 2026 09:40:09 +0300 Subject: [PATCH 5/6] refactor: add agent name in logger --- agent/internal/app/app.go | 1 + agent/internal/service/agent_service/agent.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/agent/internal/app/app.go b/agent/internal/app/app.go index e6b7736..0f0a1a7 100644 --- a/agent/internal/app/app.go +++ b/agent/internal/app/app.go @@ -33,6 +33,7 @@ func NewApp() (*App, error) { logger := log.NewLogger(cfg) logger = logger.With().Str("component", "internal.app").Logger() + logger = logger.With().Str("name", cfg.AppName).Logger() sett, err := settings.ReadSettings(cfg.SettingsPath) if err != nil { diff --git a/agent/internal/service/agent_service/agent.go b/agent/internal/service/agent_service/agent.go index 17e7330..7609223 100644 --- a/agent/internal/service/agent_service/agent.go +++ b/agent/internal/service/agent_service/agent.go @@ -65,7 +65,7 @@ func (a *AgentService) RegisterAgentConn(ctx context.Context) error { if err = a.settings.InsertAgentID(data.AgentID); err != nil { return fmt.Errorf("save agent ID: %w", err) } - a.log.Info().Str("AgentID", data.AgentID).Msg("agent registration end") + a.log.Info().Str("agentID", data.AgentID).Msg("agent registration end") return nil } From 7a53c1dc9fae92ce723ec597449e38ca51723271 Mon Sep 17 00:00:00 2001 From: lorsan Date: Tue, 5 May 2026 14:34:00 +0300 Subject: [PATCH 6/6] refactor: defer close connection or file --- .gitignore | 3 +- .golangci.yml | 47 ++ .idea/golinter.xml | 7 + ....sync-conflict-20260504-202326-XNSB2YU.xml | 83 +++ agent/internal/app/app.go | 6 +- agent/internal/service/agent_service/agent.go | 2 +- .../service/agent_service/agent_test.go | 84 +++ .../service/docker_service/docker_test.go | 10 +- agent/internal/utils/settings/settings.go | 20 +- api/gen/homeops/hub.pb.go | 524 +++++++++++++++++- api/gen/homeops/hub_grpc.pb.go | 43 +- api/proto/homeops/hub.proto | 44 ++ hub/internal/app/app.go | 5 +- hub/internal/migrator/migrator.go | 9 +- hub/internal/service/hub_service/hub.go | 10 +- hub/internal/service/hub_service/mapper.go | 2 +- hub/internal/store/mapper.go | 6 +- shared/domain/agent.go | 2 +- shared/domain/mapper.go | 4 +- 19 files changed, 859 insertions(+), 52 deletions(-) create mode 100644 .golangci.yml create mode 100644 .idea/golinter.xml create mode 100644 .idea/workspace.sync-conflict-20260504-202326-XNSB2YU.xml diff --git a/.gitignore b/.gitignore index 65cbde7..0adaa22 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env -config.yaml \ No newline at end of file +config.yaml +.idea \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..2cfb250 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,47 @@ +version: "2" + +run: + # Сколько времени давать линтеру на проверку (для больших проектов лучше ставить 5m) + timeout: 3m + # Проверять ли тесты + tests: true + +linters: + # Отключаем все по умолчанию, чтобы включить только нужные нам + disable-all: true + enable: + # --- ОБЯЗАТЕЛЬНЫЕ (База) --- + - errcheck # Проверяет, не забыл ли ты обработать ошибку (if err != nil) + - govet # Официальный инструмент Go, ищет подозрительные конструкции + - ineffassign # Находит переменные, которым присвоили значение, но не использовали + - staticcheck # Огромный набор проверок на логические ошибки + - unused # Ищет неиспользуемый код (функции, переменные, типы) + + # --- ОБУЧАЮЩИЕ (Помогут новичку) --- + - revive # Замена старому golint: следит за именованием и комментариями + - errname # Проверяет, что ошибки названы по стандарту (например, ErrSentinel) + - goconst # Находит строки, которые часто повторяются (подскажет сделать константу) + - makezero # Следит, чтобы ты не делал лишних аллокаций в slice + + settings: + revive: + # Настраиваем правила + rules: + # Отключаем обязательные комментарии для экспортируемых сущностей + - name: exported + disabled: true + # Отключаем требование комментариев к пакетам + - name: package-comments + disabled: true + # Оставляем полезное: проверка именования (var_name -> varName) + - name: var-naming + severity: warning + +issues: + # Не ограничивай количество ошибок, пока учишься + max-issues-per-linter: 0 + max-same-issues: 0 + + # Исключаем некоторые папки (например, сгенерированный код) + exclude-dirs: + - vendor \ No newline at end of file diff --git a/.idea/golinter.xml b/.idea/golinter.xml new file mode 100644 index 0000000..1ccf3ec --- /dev/null +++ b/.idea/golinter.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/workspace.sync-conflict-20260504-202326-XNSB2YU.xml b/.idea/workspace.sync-conflict-20260504-202326-XNSB2YU.xml new file mode 100644 index 0000000..ef52743 --- /dev/null +++ b/.idea/workspace.sync-conflict-20260504-202326-XNSB2YU.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + { + "associatedIndex": 6 +} + + + + + + + + + + + + + + + + + + + 1775479991162 + + + + + + \ No newline at end of file diff --git a/agent/internal/app/app.go b/agent/internal/app/app.go index 0f0a1a7..d7b5f8e 100644 --- a/agent/internal/app/app.go +++ b/agent/internal/app/app.go @@ -55,7 +55,11 @@ func (a *App) Run() { a.log.Info().Msg("connection to the hub successful") conn := rpc.NewConnectAgent(GRPCConn) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + a.log.Warn().Err(err).Msg("failed to close rpc connection") + } + }() var DockerService collector.Docker diff --git a/agent/internal/service/agent_service/agent.go b/agent/internal/service/agent_service/agent.go index 7609223..6eb18b0 100644 --- a/agent/internal/service/agent_service/agent.go +++ b/agent/internal/service/agent_service/agent.go @@ -50,7 +50,7 @@ func (a *AgentService) RegisterAgentConn(ctx context.Context) error { AgentID := a.settings.GetAgentID() AgentName := a.cfg.AppName AgentData := domain.RegisterAgentRequest{ - AgentId: AgentID, + AgentID: AgentID, AgentName: AgentName, Host: info, Capabilities: caps, diff --git a/agent/internal/service/agent_service/agent_test.go b/agent/internal/service/agent_service/agent_test.go index 5c3bb2c..a229c03 100644 --- a/agent/internal/service/agent_service/agent_test.go +++ b/agent/internal/service/agent_service/agent_test.go @@ -2,8 +2,12 @@ package agent_service import ( "context" + "errors" + "testing" + "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/config_yaml" "github.com/lorsanstand/HomeOps-Hub/shared/domain" + "github.com/rs/zerolog" ) type CollectorMock struct { @@ -18,8 +22,88 @@ func (c *CollectorMock) GatherInfoSystem() (domain.HostInfo, []domain.Capability type ConnectionMock struct { regAgentErr error regResp domain.RegisterAgentResponse + regData domain.RegisterAgentRequest } func (c *ConnectionMock) RegisterAgent(ctx context.Context, RegisterData domain.RegisterAgentRequest) (domain.RegisterAgentResponse, error) { + c.regData = RegisterData return c.regResp, c.regAgentErr } + +type SettingsMock struct { + insertErr error + agentID string + countUse int +} + +func (s *SettingsMock) InsertAgentID(agentID string) error { + s.agentID = agentID + s.countUse++ + return s.insertErr +} + +func (s *SettingsMock) GetAgentID() string { + return s.agentID +} + +func TestAgentService_RegisterAgentConn(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + wantErr error + insertAgentIDUse int + settings SettingsMock + collector CollectorMock + conn ConnectionMock + cfg config_yaml.AgentConfig + }{ + { + name: "success", + wantErr: nil, + insertAgentIDUse: 1, + settings: SettingsMock{agentID: "", insertErr: nil}, + collector: CollectorMock{ + host: domain.HostInfo{System: "Linux", Hostname: "test", Arch: "x64"}, + caps: []domain.Capability{ + {Available: true, Version: "0", Name: "testCaps", Reason: ""}, + }, + }, + conn: ConnectionMock{regAgentErr: nil, regResp: domain.RegisterAgentResponse{AgentID: "123", Heartbeat: 4}}, + cfg: config_yaml.AgentConfig{AppName: "test"}, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ctx := context.Background() + + svc := NewAgentService(&tt.collector, + &tt.conn, + &tt.settings, + &tt.cfg, + zerolog.New(nil), + ) + + err := svc.RegisterAgentConn(ctx) + if !errors.Is(err, tt.wantErr) { + t.Fatalf("expected error %v, got: %v", tt.wantErr, err) + } + + if tt.insertAgentIDUse != tt.settings.countUse { + t.Errorf("expected count insert agent id %v, got: %v", tt.insertAgentIDUse, tt.settings.countUse) + } + + if tt.settings.agentID != tt.conn.regResp.AgentID { + t.Errorf("expected insert agent id %v, got: %v", tt.conn.regResp.AgentID, tt.settings.agentID) + } + + if tt.cfg.AppName != tt.conn.regData.AgentName { + t.Fatalf("expected agent name %v, got: %v", tt.cfg.AppName, tt.conn.regData.AgentName) + } + + }) + } +} diff --git a/agent/internal/service/docker_service/docker_test.go b/agent/internal/service/docker_service/docker_test.go index 25db239..c7b4170 100644 --- a/agent/internal/service/docker_service/docker_test.go +++ b/agent/internal/service/docker_service/docker_test.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog" ) -var testError error = errors.New("test") +var errTest error = errors.New("test") type DockerMock struct { pingErr error @@ -46,11 +46,11 @@ func TestCheckDockerDaemon(t *testing.T) { { name: "docker error", mock: DockerMock{ - pingErr: testError, + pingErr: errTest, containers: nil, containerErr: nil, }, - wantErr: testError, + wantErr: errTest, }, } @@ -98,10 +98,10 @@ func TestContainersList(t *testing.T) { mock: DockerMock{ pingErr: nil, containers: nil, - containerErr: testError, + containerErr: errTest, }, wantLen: 0, - wantErr: testError, + wantErr: errTest, }, { name: "docker empty container", diff --git a/agent/internal/utils/settings/settings.go b/agent/internal/utils/settings/settings.go index b7ca57c..35d7418 100644 --- a/agent/internal/utils/settings/settings.go +++ b/agent/internal/utils/settings/settings.go @@ -13,7 +13,7 @@ type Settings struct { path string } -func ReadSettings(path string) (*Settings, error) { +func ReadSettings(path string) (sett *Settings, err error) { if path == "" { homeDir, err := os.UserHomeDir() if err != nil { @@ -22,7 +22,7 @@ func ReadSettings(path string) (*Settings, error) { path = filepath.Join(homeDir, ".config", "homeops") } - err := os.Mkdir(path, 0755) + err = os.Mkdir(path, 0755) if err != nil { if !errors.Is(err, os.ErrExist) { return nil, err @@ -39,7 +39,12 @@ func ReadSettings(path string) (*Settings, error) { return nil, err } } else { - defer file.Close() + defer func() { + closeErr := file.Close() + if err == nil { + err = closeErr + } + }() err = json.NewDecoder(file).Decode(&settings) if err != nil && !errors.Is(err, io.EOF) { return nil, err @@ -51,12 +56,17 @@ func ReadSettings(path string) (*Settings, error) { return &settings, nil } -func (s *Settings) InsertAgentID(agentID string) error { +func (s *Settings) InsertAgentID(agentID string) (err error) { file, err := os.OpenFile(s.path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } - defer file.Close() + defer func() { + closeErr := file.Close() + if err == nil { + err = closeErr + } + }() sett := Settings{AgentID: agentID} diff --git a/api/gen/homeops/hub.pb.go b/api/gen/homeops/hub.pb.go index e8aad80..03f9fcc 100644 --- a/api/gen/homeops/hub.pb.go +++ b/api/gen/homeops/hub.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.36.11 // protoc v7.34.1 -// source: homeops/cmd.proto +// source: homeops/hub.proto package homeops @@ -322,11 +322,441 @@ func (x *RegisterAgentResponse) GetAgentId() string { return "" } +type ServerCommandRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Args map[string]string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + TimeoutSeconds int64 `protobuf:"varint,4,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ServerCommandRequest) Reset() { + *x = ServerCommandRequest{} + mi := &file_homeops_hub_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ServerCommandRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerCommandRequest) ProtoMessage() {} + +func (x *ServerCommandRequest) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerCommandRequest.ProtoReflect.Descriptor instead. +func (*ServerCommandRequest) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{5} +} + +func (x *ServerCommandRequest) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *ServerCommandRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ServerCommandRequest) GetArgs() map[string]string { + if x != nil { + return x.Args + } + return nil +} + +func (x *ServerCommandRequest) GetTimeoutSeconds() int64 { + if x != nil { + return x.TimeoutSeconds + } + return 0 +} + +type AgentEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + // Types that are valid to be assigned to Event: + // + // *AgentEvent_Heartbeat + // *AgentEvent_CommandResponse + // *AgentEvent_Alert + Event isAgentEvent_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentEvent) Reset() { + *x = AgentEvent{} + mi := &file_homeops_hub_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentEvent) ProtoMessage() {} + +func (x *AgentEvent) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentEvent.ProtoReflect.Descriptor instead. +func (*AgentEvent) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{6} +} + +func (x *AgentEvent) GetAgentId() string { + if x != nil { + return x.AgentId + } + return "" +} + +func (x *AgentEvent) GetEvent() isAgentEvent_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *AgentEvent) GetHeartbeat() *Heartbeat { + if x != nil { + if x, ok := x.Event.(*AgentEvent_Heartbeat); ok { + return x.Heartbeat + } + } + return nil +} + +func (x *AgentEvent) GetCommandResponse() *CommandResponse { + if x != nil { + if x, ok := x.Event.(*AgentEvent_CommandResponse); ok { + return x.CommandResponse + } + } + return nil +} + +func (x *AgentEvent) GetAlert() *Alert { + if x != nil { + if x, ok := x.Event.(*AgentEvent_Alert); ok { + return x.Alert + } + } + return nil +} + +type isAgentEvent_Event interface { + isAgentEvent_Event() +} + +type AgentEvent_Heartbeat struct { + Heartbeat *Heartbeat `protobuf:"bytes,2,opt,name=heartbeat,proto3,oneof"` +} + +type AgentEvent_CommandResponse struct { + CommandResponse *CommandResponse `protobuf:"bytes,3,opt,name=command_response,json=commandResponse,proto3,oneof"` +} + +type AgentEvent_Alert struct { + Alert *Alert `protobuf:"bytes,4,opt,name=alert,proto3,oneof"` +} + +func (*AgentEvent_Heartbeat) isAgentEvent_Event() {} + +func (*AgentEvent_CommandResponse) isAgentEvent_Event() {} + +func (*AgentEvent_Alert) isAgentEvent_Event() {} + +type Heartbeat struct { + state protoimpl.MessageState `protogen:"open.v1"` + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Metrics *SystemMetrics `protobuf:"bytes,2,opt,name=metrics,proto3" json:"metrics,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Heartbeat) Reset() { + *x = Heartbeat{} + mi := &file_homeops_hub_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Heartbeat) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Heartbeat) ProtoMessage() {} + +func (x *Heartbeat) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Heartbeat.ProtoReflect.Descriptor instead. +func (*Heartbeat) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{7} +} + +func (x *Heartbeat) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *Heartbeat) GetMetrics() *SystemMetrics { + if x != nil { + return x.Metrics + } + return nil +} + +type SystemMetrics struct { + state protoimpl.MessageState `protogen:"open.v1"` + CpuUsage float32 `protobuf:"fixed32,1,opt,name=cpu_usage,json=cpuUsage,proto3" json:"cpu_usage,omitempty"` + MemoryUsage float32 `protobuf:"fixed32,2,opt,name=memory_usage,json=memoryUsage,proto3" json:"memory_usage,omitempty"` + DiskUsage float32 `protobuf:"fixed32,3,opt,name=disk_usage,json=diskUsage,proto3" json:"disk_usage,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SystemMetrics) Reset() { + *x = SystemMetrics{} + mi := &file_homeops_hub_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SystemMetrics) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SystemMetrics) ProtoMessage() {} + +func (x *SystemMetrics) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SystemMetrics.ProtoReflect.Descriptor instead. +func (*SystemMetrics) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{8} +} + +func (x *SystemMetrics) GetCpuUsage() float32 { + if x != nil { + return x.CpuUsage + } + return 0 +} + +func (x *SystemMetrics) GetMemoryUsage() float32 { + if x != nil { + return x.MemoryUsage + } + return 0 +} + +func (x *SystemMetrics) GetDiskUsage() float32 { + if x != nil { + return x.DiskUsage + } + return 0 +} + +type CommandResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + Output string `protobuf:"bytes,3,opt,name=output,proto3" json:"output,omitempty"` + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` + ExecTimeMs int64 `protobuf:"varint,5,opt,name=exec_time_ms,json=execTimeMs,proto3" json:"exec_time_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CommandResponse) Reset() { + *x = CommandResponse{} + mi := &file_homeops_hub_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CommandResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommandResponse) ProtoMessage() {} + +func (x *CommandResponse) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CommandResponse.ProtoReflect.Descriptor instead. +func (*CommandResponse) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{9} +} + +func (x *CommandResponse) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *CommandResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *CommandResponse) GetOutput() string { + if x != nil { + return x.Output + } + return "" +} + +func (x *CommandResponse) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *CommandResponse) GetExecTimeMs() int64 { + if x != nil { + return x.ExecTimeMs + } + return 0 +} + +type Alert struct { + state protoimpl.MessageState `protogen:"open.v1"` + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Level string `protobuf:"bytes,2,opt,name=level,proto3" json:"level,omitempty"` + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Alert) Reset() { + *x = Alert{} + mi := &file_homeops_hub_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Alert) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Alert) ProtoMessage() {} + +func (x *Alert) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Alert.ProtoReflect.Descriptor instead. +func (*Alert) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{10} +} + +func (x *Alert) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *Alert) GetLevel() string { + if x != nil { + return x.Level + } + return "" +} + +func (x *Alert) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *Alert) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + var File_homeops_hub_proto protoreflect.FileDescriptor const file_homeops_hub_proto_rawDesc = "" + "\n" + - "\x11homeops/cmd.proto\x1a\x1bgoogle/protobuf/empty.proto\"\"\n" + + "\x11homeops/hub.proto\x1a\x1bgoogle/protobuf/empty.proto\"\"\n" + "\fPongResponse\x12\x12\n" + "\x04pong\x18\x01 \x01(\tR\x04pong\"\xb6\x01\n" + "\x14RegisterAgentRequest\x12\x19\n" + @@ -350,10 +780,49 @@ const file_homeops_hub_proto_rawDesc = "" + "\x06reason\x18\x04 \x01(\tR\x06reason\"n\n" + "\x15RegisterAgentResponse\x12:\n" + "\x19heartbeat_interval_second\x18\x01 \x01(\x03R\x17heartbeatIntervalSecond\x12\x19\n" + - "\bagent_id\x18\x02 \x01(\tR\aagentId2x\n" + + "\bagent_id\x18\x02 \x01(\tR\aagentId\"\xe0\x01\n" + + "\x14ServerCommandRequest\x12\x1d\n" + + "\n" + + "request_id\x18\x01 \x01(\tR\trequestId\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x123\n" + + "\x04args\x18\x03 \x03(\v2\x1f.ServerCommandRequest.ArgsEntryR\x04args\x12'\n" + + "\x0ftimeout_seconds\x18\x04 \x01(\x03R\x0etimeoutSeconds\x1a7\n" + + "\tArgsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xbb\x01\n" + + "\n" + + "AgentEvent\x12\x19\n" + + "\bagent_id\x18\x01 \x01(\tR\aagentId\x12*\n" + + "\theartbeat\x18\x02 \x01(\v2\n" + + ".HeartbeatH\x00R\theartbeat\x12=\n" + + "\x10command_response\x18\x03 \x01(\v2\x10.CommandResponseH\x00R\x0fcommandResponse\x12\x1e\n" + + "\x05alert\x18\x04 \x01(\v2\x06.AlertH\x00R\x05alertB\a\n" + + "\x05event\"S\n" + + "\tHeartbeat\x12\x1c\n" + + "\ttimestamp\x18\x01 \x01(\x03R\ttimestamp\x12(\n" + + "\ametrics\x18\x02 \x01(\v2\x0e.SystemMetricsR\ametrics\"n\n" + + "\rSystemMetrics\x12\x1b\n" + + "\tcpu_usage\x18\x01 \x01(\x02R\bcpuUsage\x12!\n" + + "\fmemory_usage\x18\x02 \x01(\x02R\vmemoryUsage\x12\x1d\n" + + "\n" + + "disk_usage\x18\x03 \x01(\x02R\tdiskUsage\"\x9a\x01\n" + + "\x0fCommandResponse\x12\x1d\n" + + "\n" + + "request_id\x18\x01 \x01(\tR\trequestId\x12\x18\n" + + "\asuccess\x18\x02 \x01(\bR\asuccess\x12\x16\n" + + "\x06output\x18\x03 \x01(\tR\x06output\x12\x14\n" + + "\x05error\x18\x04 \x01(\tR\x05error\x12 \n" + + "\fexec_time_ms\x18\x05 \x01(\x03R\n" + + "execTimeMs\"s\n" + + "\x05Alert\x12\x1c\n" + + "\ttimestamp\x18\x01 \x01(\x03R\ttimestamp\x12\x14\n" + + "\x05level\x18\x02 \x01(\tR\x05level\x12\x14\n" + + "\x05title\x18\x03 \x01(\tR\x05title\x12 \n" + + "\vdescription\x18\x04 \x01(\tR\vdescription2\xb6\x01\n" + "\x03Hub\x12/\n" + "\x04Ping\x12\x16.google.protobuf.Empty\x1a\r.PongResponse\"\x00\x12@\n" + - "\rRegisterAgent\x12\x15.RegisterAgentRequest\x1a\x16.RegisterAgentResponse\"\x00B HostInfo - 3, // 1: RegisterAgentRequest.capability:type_name -> Capability - 5, // 2: Hub.Ping:input_type -> google.protobuf.Empty - 1, // 3: Hub.RegisterAgent:input_type -> RegisterAgentRequest - 0, // 4: Hub.Ping:output_type -> PongResponse - 4, // 5: Hub.RegisterAgent:output_type -> RegisterAgentResponse - 4, // [4:6] is the sub-list for method output_type - 2, // [2:4] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 2, // 0: RegisterAgentRequest.host:type_name -> HostInfo + 3, // 1: RegisterAgentRequest.capability:type_name -> Capability + 11, // 2: ServerCommandRequest.args:type_name -> ServerCommandRequest.ArgsEntry + 7, // 3: AgentEvent.heartbeat:type_name -> Heartbeat + 9, // 4: AgentEvent.command_response:type_name -> CommandResponse + 10, // 5: AgentEvent.alert:type_name -> Alert + 8, // 6: Heartbeat.metrics:type_name -> SystemMetrics + 12, // 7: Hub.Ping:input_type -> google.protobuf.Empty + 1, // 8: Hub.RegisterAgent:input_type -> RegisterAgentRequest + 6, // 9: Hub.StreamConnection:input_type -> AgentEvent + 0, // 10: Hub.Ping:output_type -> PongResponse + 4, // 11: Hub.RegisterAgent:output_type -> RegisterAgentResponse + 5, // 12: Hub.StreamConnection:output_type -> ServerCommandRequest + 10, // [10:13] is the sub-list for method output_type + 7, // [7:10] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_homeops_hub_proto_init() } @@ -395,13 +878,18 @@ func file_homeops_hub_proto_init() { if File_homeops_hub_proto != nil { return } + file_homeops_hub_proto_msgTypes[6].OneofWrappers = []any{ + (*AgentEvent_Heartbeat)(nil), + (*AgentEvent_CommandResponse)(nil), + (*AgentEvent_Alert)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_homeops_hub_proto_rawDesc), len(file_homeops_hub_proto_rawDesc)), NumEnums: 0, - NumMessages: 5, + NumMessages: 12, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/homeops/hub_grpc.pb.go b/api/gen/homeops/hub_grpc.pb.go index c5a605c..d689399 100644 --- a/api/gen/homeops/hub_grpc.pb.go +++ b/api/gen/homeops/hub_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc v7.34.1 -// source: homeops/cmd.proto +// source: homeops/hub.proto package homeops @@ -20,8 +20,9 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - Hub_Ping_FullMethodName = "/Hub/Ping" - Hub_RegisterAgent_FullMethodName = "/Hub/RegisterAgent" + Hub_Ping_FullMethodName = "/Hub/Ping" + Hub_RegisterAgent_FullMethodName = "/Hub/RegisterAgent" + Hub_StreamConnection_FullMethodName = "/Hub/StreamConnection" ) // HubClient is the client API for Hub service. @@ -30,6 +31,7 @@ const ( type HubClient interface { Ping(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PongResponse, error) RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) + StreamConnection(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[AgentEvent, ServerCommandRequest], error) } type hubClient struct { @@ -60,12 +62,26 @@ func (c *hubClient) RegisterAgent(ctx context.Context, in *RegisterAgentRequest, return out, nil } +func (c *hubClient) StreamConnection(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[AgentEvent, ServerCommandRequest], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Hub_ServiceDesc.Streams[0], Hub_StreamConnection_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[AgentEvent, ServerCommandRequest]{ClientStream: stream} + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Hub_StreamConnectionClient = grpc.BidiStreamingClient[AgentEvent, ServerCommandRequest] + // HubServer is the server API for Hub service. // All implementations must embed UnimplementedHubServer // for forward compatibility. type HubServer interface { Ping(context.Context, *emptypb.Empty) (*PongResponse, error) RegisterAgent(context.Context, *RegisterAgentRequest) (*RegisterAgentResponse, error) + StreamConnection(grpc.BidiStreamingServer[AgentEvent, ServerCommandRequest]) error mustEmbedUnimplementedHubServer() } @@ -82,6 +98,9 @@ func (UnimplementedHubServer) Ping(context.Context, *emptypb.Empty) (*PongRespon func (UnimplementedHubServer) RegisterAgent(context.Context, *RegisterAgentRequest) (*RegisterAgentResponse, error) { return nil, status.Error(codes.Unimplemented, "method RegisterAgent not implemented") } +func (UnimplementedHubServer) StreamConnection(grpc.BidiStreamingServer[AgentEvent, ServerCommandRequest]) error { + return status.Error(codes.Unimplemented, "method StreamConnection not implemented") +} func (UnimplementedHubServer) mustEmbedUnimplementedHubServer() {} func (UnimplementedHubServer) testEmbeddedByValue() {} @@ -139,6 +158,13 @@ func _Hub_RegisterAgent_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Hub_StreamConnection_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(HubServer).StreamConnection(&grpc.GenericServerStream[AgentEvent, ServerCommandRequest]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Hub_StreamConnectionServer = grpc.BidiStreamingServer[AgentEvent, ServerCommandRequest] + // Hub_ServiceDesc is the grpc.ServiceDesc for Hub service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -155,6 +181,13 @@ var Hub_ServiceDesc = grpc.ServiceDesc{ Handler: _Hub_RegisterAgent_Handler, }, }, - Streams: []grpc.StreamDesc{}, - Metadata: "homeops/cmd.proto", + Streams: []grpc.StreamDesc{ + { + StreamName: "StreamConnection", + Handler: _Hub_StreamConnection_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "homeops/hub.proto", } diff --git a/api/proto/homeops/hub.proto b/api/proto/homeops/hub.proto index e7f32d9..301b992 100644 --- a/api/proto/homeops/hub.proto +++ b/api/proto/homeops/hub.proto @@ -7,6 +7,7 @@ option go_package = "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops;homeops" service Hub { rpc Ping (google.protobuf.Empty) returns (PongResponse) {} rpc RegisterAgent (RegisterAgentRequest) returns (RegisterAgentResponse) {} + rpc StreamConnection (stream AgentEvent) returns (stream ServerCommandRequest) {} } message PongResponse { @@ -37,4 +38,47 @@ message Capability { message RegisterAgentResponse { int64 heartbeat_interval_second = 1; string agent_id = 2; +} + +message ServerCommandRequest { + string request_id = 1; + string name = 2; + map args = 3; + int64 timeout_seconds = 4; +} + +message AgentEvent { + string agent_id = 1; + + oneof event { + Heartbeat heartbeat = 2; + CommandResponse command_response = 3; + Alert alert = 4; + } +} + +message Heartbeat { + int64 timestamp = 1; + SystemMetrics metrics = 2; +} + +message SystemMetrics { + float cpu_usage = 1; + float memory_usage = 2; + float disk_usage = 3; +} + +message CommandResponse { + string request_id = 1; + bool success = 2; + string output = 3; + string error = 4; + int64 exec_time_ms = 5; +} + +message Alert { + int64 timestamp = 1; + string level = 2; + string title = 3; + string description = 4; } \ No newline at end of file diff --git a/hub/internal/app/app.go b/hub/internal/app/app.go index 9b825c3..c1db0e3 100644 --- a/hub/internal/app/app.go +++ b/hub/internal/app/app.go @@ -43,7 +43,6 @@ func (a *App) Run() { a.log.Error().Err(err).Msg("failed to connect to the database for migrations") return } - defer migratePGConn.Close() mgrt, err := migrator.NewMigrator(hubdir.MigrationsFS, "migrations") if err != nil { @@ -57,7 +56,9 @@ func (a *App) Run() { return } a.log.Info().Msg("migrations applied successfully") - migratePGConn.Close() + if err := migratePGConn.Close(); err != nil { + a.log.Warn().Err(err).Msg("failed to close migrate postgres connection") + } a.log.Info().Msg("creating database connection pool") pool, err := pgxpool.New(ctx, a.cfg.GetURLPostgres()) diff --git a/hub/internal/migrator/migrator.go b/hub/internal/migrator/migrator.go index 9f1effe..159bd8e 100644 --- a/hub/internal/migrator/migrator.go +++ b/hub/internal/migrator/migrator.go @@ -26,7 +26,7 @@ func NewMigrator(sqlFiles embed.FS, dirname string) (*Migrator, error) { return &Migrator{srcDriver: d}, nil } -func (m *Migrator) ApplyMigrations(db *sql.DB) error { +func (m *Migrator) ApplyMigrations(db *sql.DB) (err error) { driver, err := postgres.WithInstance(db, &postgres.Config{}) if err != nil { return fmt.Errorf("unable to create db instance: %w", err) @@ -37,7 +37,12 @@ func (m *Migrator) ApplyMigrations(db *sql.DB) error { return fmt.Errorf("unable to create migration: %w", err) } - defer migrator.Close() + defer func() { + closeErr, _ := migrator.Close() + if err == nil { + err = closeErr + } + }() if err = migrator.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) { return fmt.Errorf("unable to apply migrations: %w", err) diff --git a/hub/internal/service/hub_service/hub.go b/hub/internal/service/hub_service/hub.go index d9b7cae..00d7181 100644 --- a/hub/internal/service/hub_service/hub.go +++ b/hub/internal/service/hub_service/hub.go @@ -30,16 +30,16 @@ func NewHubService(store Store, logger zerolog.Logger) *HubService { } func (h *HubService) RegisterAgent(ctx context.Context, data domain.RegisterAgentRequest) (domain.RegisterAgentResponse, error) { - h.log.Debug().Str("agentID", data.AgentId).Str("agentName", data.AgentName).Msg("started registering agent") - agent, err := h.store.GetAgentByAgentID(ctx, data.AgentId) + h.log.Debug().Str("agentID", data.AgentID).Str("agentName", data.AgentName).Msg("started registering agent") + agent, err := h.store.GetAgentByAgentID(ctx, data.AgentID) if err != nil && !errors.Is(err, sql.ErrNoRows) { return domain.RegisterAgentResponse{}, fmt.Errorf("failed select agent to db: %w", err) } - if data.AgentId != "" && !errors.Is(err, sql.ErrNoRows) { + if data.AgentID != "" && !errors.Is(err, sql.ErrNoRows) { h.log.Debug().Str("agentID", agent.AgentID).Str("agentName", data.AgentName).Msg("agent exists, updating") - data.AgentId = agent.AgentID + data.AgentID = agent.AgentID agentStore := toCreateAgentModel(data) @@ -55,7 +55,7 @@ func (h *HubService) RegisterAgent(ctx context.Context, data domain.RegisterAgen return domain.RegisterAgentResponse{}, fmt.Errorf("generate agent ID: %w", err) } - data.AgentId = AgentID + data.AgentID = AgentID agentStore := toCreateAgentModel(data) diff --git a/hub/internal/service/hub_service/mapper.go b/hub/internal/service/hub_service/mapper.go index e28c843..9013dab 100644 --- a/hub/internal/service/hub_service/mapper.go +++ b/hub/internal/service/hub_service/mapper.go @@ -7,7 +7,7 @@ import ( func toCreateAgentModel(agent domain.RegisterAgentRequest) domainHub.CreateAgentModel { return domainHub.CreateAgentModel{ - AgentID: agent.AgentId, + AgentID: agent.AgentID, AgentName: agent.AgentName, Architecture: agent.Host.Arch, System: agent.Host.System, diff --git a/hub/internal/store/mapper.go b/hub/internal/store/mapper.go index 90e2290..b843735 100644 --- a/hub/internal/store/mapper.go +++ b/hub/internal/store/mapper.go @@ -16,7 +16,7 @@ func toDBAgent(agent domainHub.CreateAgentModel) gen2.CreateAgentParams { System: agent.System, Hostname: agent.Hostname, Version: agent.Version, - Capabilities: toJsonCapabilities(agent.Capabilities), + Capabilities: toJSONCapabilities(agent.Capabilities), } } @@ -28,11 +28,11 @@ func toUpdateDBAgent(agent domainHub.CreateAgentModel) gen2.UpdateAgentByIDParam System: agent.System, Hostname: agent.Hostname, Version: agent.Version, - Capabilities: toJsonCapabilities(agent.Capabilities), + Capabilities: toJSONCapabilities(agent.Capabilities), } } -func toJsonCapabilities(caps []domain.Capability) []byte { +func toJSONCapabilities(caps []domain.Capability) []byte { data, err := json.Marshal(caps) if err != nil { // Note: Error is silently handled - consider logging in production diff --git a/shared/domain/agent.go b/shared/domain/agent.go index 8a5f062..2a24ae5 100644 --- a/shared/domain/agent.go +++ b/shared/domain/agent.go @@ -1,7 +1,7 @@ package domain type RegisterAgentRequest struct { - AgentId string + AgentID string AgentName string AgentVersion string Host HostInfo diff --git a/shared/domain/mapper.go b/shared/domain/mapper.go index 2812e8e..587d361 100644 --- a/shared/domain/mapper.go +++ b/shared/domain/mapper.go @@ -10,7 +10,7 @@ func ToDomainAgentRequest(request *pb.RegisterAgentRequest) RegisterAgentRequest } return RegisterAgentRequest{ - AgentId: request.AgentId, + AgentID: request.AgentId, AgentName: request.AgentName, Host: HostInfo{ System: request.Host.System, @@ -53,7 +53,7 @@ func ToDomainCapabilities(capability []*pb.Capability) []Capability { func ToGRPCAgentRequest(request RegisterAgentRequest) pb.RegisterAgentRequest { return pb.RegisterAgentRequest{ - AgentId: request.AgentId, + AgentId: request.AgentID, AgentName: request.AgentName, Host: &pb.HostInfo{ Hostname: request.Host.Hostname,