diff --git a/.gitignore b/.gitignore index d19b928..0adaa22 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env -agent.dev.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/cmd/agent/main.go b/agent/cmd/main.go similarity index 53% rename from cmd/agent/main.go rename to agent/cmd/main.go index 67febdd..9fcdbb1 100644 --- a/cmd/agent/main.go +++ b/agent/cmd/main.go @@ -1,11 +1,13 @@ 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() if err != nil { - return + panic(err) } start.Run() } diff --git a/internal/agent/app/app.go b/agent/internal/app/app.go similarity index 52% rename from internal/agent/app/app.go rename to agent/internal/app/app.go index a588b6d..d7b5f8e 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" + "github.com/lorsanstand/HomeOps-Hub/shared/log" "github.com/rs/zerolog" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -25,23 +25,23 @@ type App struct { } func NewApp() (*App, error) { - cfg, err := config_yaml.NewConfig() if err != nil { standartlog.Fatalf("failed to get config: %v", err) 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() + logger = logger.With().Str("name", cfg.AppName).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() { @@ -49,12 +49,17 @@ 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 connection hub") return } + a.log.Info().Msg("connection to the hub successful") - conn := rpc.NewConnectAgent(GRPCConn, a.log) - defer conn.Close() + conn := rpc.NewConnectAgent(GRPCConn) + defer func() { + if err := conn.Close(); err != nil { + a.log.Warn().Err(err).Msg("failed to close rpc connection") + } + }() var DockerService collector.Docker @@ -63,11 +68,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/internal/agent/rpc/client.go b/agent/internal/rpc/client.go similarity index 62% rename from internal/agent/rpc/client.go rename to agent/internal/rpc/client.go index a945085..23f9ea3 100644 --- a/internal/agent/rpc/client.go +++ b/agent/internal/rpc/client.go @@ -4,27 +4,21 @@ import ( "context" pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" - "github.com/lorsanstand/HomeOps-Hub/internal/domain" - "github.com/rs/zerolog" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" "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 new file mode 100644 index 0000000..6eb18b0 --- /dev/null +++ b/agent/internal/service/agent_service/agent.go @@ -0,0 +1,71 @@ +package agent_service + +import ( + "context" + "fmt" + + "github.com/lorsanstand/HomeOps-Hub/agent/internal/utils/config_yaml" + "github.com/lorsanstand/HomeOps-Hub/shared/domain" + "github.com/rs/zerolog" +) + +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) +} + +type AgentService struct { + collect Collector + conn HubConnection + log zerolog.Logger + cfg *config_yaml.AgentConfig + heartBeat int + settings Settings +} + +func NewAgentService( + collector Collector, + conn HubConnection, + settings Settings, + cfg *config_yaml.AgentConfig, + logger zerolog.Logger, +) *AgentService { + 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) 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.GetAgentID() + AgentName := a.cfg.AppName + 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 { + return fmt.Errorf("register agent: %w", err) + } + + 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/service/agent_service/agent_test.go b/agent/internal/service/agent_service/agent_test.go new file mode 100644 index 0000000..a229c03 --- /dev/null +++ b/agent/internal/service/agent_service/agent_test.go @@ -0,0 +1,109 @@ +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 { + host domain.HostInfo + caps []domain.Capability +} + +func (c *CollectorMock) GatherInfoSystem() (domain.HostInfo, []domain.Capability) { + return c.host, c.caps +} + +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/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..e0351ed 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", "internal.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 86% rename from internal/agent/service/docker_service/docker.go rename to agent/internal/service/docker_service/docker.go index 487b1e1..825878c 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,13 +23,13 @@ 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", "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/internal/agent/service/docker_service/docker_test.go b/agent/internal/service/docker_service/docker_test.go similarity index 94% rename from internal/agent/service/docker_service/docker_test.go rename to agent/internal/service/docker_service/docker_test.go index 25db239..c7b4170 100644 --- a/internal/agent/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/internal/agent/utils/config_yaml/config.go b/agent/internal/utils/config_yaml/config.go similarity index 83% rename from internal/agent/utils/config_yaml/config.go rename to agent/internal/utils/config_yaml/config.go index 9d24106..f8d4b1b 100644 --- a/internal/agent/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 { 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 69% rename from internal/agent/utils/settings/settings.go rename to agent/internal/utils/settings/settings.go index 1632780..35d7418 100644 --- a/internal/agent/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,19 @@ func ReadSettings(path string) (*Settings, error) { return &settings, nil } -func (s *Settings) Insert(sett Settings) 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} if err = json.NewEncoder(file).Encode(sett); err != nil { return err @@ -64,3 +76,7 @@ func (s *Settings) Insert(sett Settings) error { return nil } + +func (s *Settings) GetAgentID() string { + return s.AgentID +} 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 80% rename from internal/hub/app/app.go rename to hub/internal/app/app.go index 56652b0..c1db0e3 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" ) @@ -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) @@ -42,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 { @@ -56,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()) @@ -80,7 +82,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/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 87% rename from internal/hub/migrator/migrator.go rename to hub/internal/migrator/migrator.go index 22338f8..159bd8e 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" @@ -25,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) @@ -36,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/internal/hub/rpc/server.go b/hub/internal/rpc/server.go similarity index 83% rename from internal/hub/rpc/server.go rename to hub/internal/rpc/server.go index 1a5aade..d9de289 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" @@ -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/internal/hub/service/hub_service/hub.go b/hub/internal/service/hub_service/hub.go similarity index 51% rename from internal/hub/service/hub_service/hub.go rename to hub/internal/service/hub_service/hub.go index aa86b1a..00d7181 100644 --- a/internal/hub/service/hub_service/hub.go +++ b/hub/internal/service/hub_service/hub.go @@ -6,12 +6,14 @@ 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" ) +const HEARTBEAT = 5 + type Store interface { NewAgent(ctx context.Context, agent domainHub.CreateAgentModel) error GetAgentByAgentID(ctx context.Context, AgentID string) (domainHub.AgentModel, error) @@ -28,43 +30,37 @@ 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) { - 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") + 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) 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 + data.AgentID = AgentID 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 } diff --git a/internal/hub/service/hub_service/mapper.go b/hub/internal/service/hub_service/mapper.go similarity index 71% rename from internal/hub/service/hub_service/mapper.go rename to hub/internal/service/hub_service/mapper.go index 6a020d8..9013dab 100644 --- a/internal/hub/service/hub_service/mapper.go +++ b/hub/internal/service/hub_service/mapper.go @@ -1,13 +1,13 @@ 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 { return domainHub.CreateAgentModel{ - AgentID: agent.AgentId, + AgentID: agent.AgentID, AgentName: agent.AgentName, Architecture: agent.Host.Arch, System: agent.Host.System, diff --git a/internal/hub/store/mapper.go b/hub/internal/store/mapper.go similarity index 66% rename from internal/hub/store/mapper.go rename to hub/internal/store/mapper.go index cbce0c3..b843735 100644 --- a/internal/hub/store/mapper.go +++ b/hub/internal/store/mapper.go @@ -3,36 +3,36 @@ 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, System: agent.System, Hostname: agent.Hostname, Version: agent.Version, - Capabilities: toJsonCapabilities(agent.Capabilities), + Capabilities: toJSONCapabilities(agent.Capabilities), } } -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, 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 @@ -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/agent/service/agent_service/agent.go b/internal/agent/service/agent_service/agent.go deleted file mode 100644 index af9f050..0000000 --- a/internal/agent/service/agent_service/agent.go +++ /dev/null @@ -1,60 +0,0 @@ -package agent_service - -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/rs/zerolog" -) - -const AgentVersion = "0.0" - -type Collector interface { - GatherInfoSystem() (domain.HostInfo, []domain.Capability) -} - -type HubConnection interface { - RegisterAgent(ctx context.Context, RegisterData domain.RegisterAgentRequest) (domain.RegisterAgentResponse, error) -} - -type AgentService struct { - collect Collector - conn HubConnection - log zerolog.Logger - cfg *config_yaml.AgentConfig - heartBeat int - settings *settings.Settings -} - -func NewAgentService( - collector Collector, - conn HubConnection, - settings *settings.Settings, - cfg *config_yaml.AgentConfig, - logger zerolog.Logger, -) *AgentService { - logger = logger.With().Str("component", "agent.service.agent_serivce").Logger() - - return &AgentService{collect: collector, conn: conn, cfg: cfg, log: logger, settings: settings} -} - -func (a *AgentService) RegisterAgentConn(ctx context.Context) { - info, caps := a.collect.GatherInfoSystem() - AgentID := a.settings.AgentID - AgentName := a.cfg.AppName - AgentData := domain.RegisterAgentRequest{AgentId: AgentID, AgentName: AgentName, Host: info, Capabilities: caps, AgentVersion: AgentVersion} - - data, err := a.conn.RegisterAgent(ctx, AgentData) - if err != nil { - a.log.Error().Err(err).Msg("failed register agent") - return - } - - if err = a.settings.Insert(settings.Settings{AgentID: data.AgentID}); err != nil { - a.log.Warn().Err(err).Msg("failed to save agent id") - } - fmt.Println(data) -} diff --git a/internal/agent/service/agent_service/agent_test.go b/internal/agent/service/agent_service/agent_test.go deleted file mode 100644 index b6852ac..0000000 --- a/internal/agent/service/agent_service/agent_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package agent_service - -import ( - "context" - - "github.com/lorsanstand/HomeOps-Hub/internal/domain" -) - -type CollectorMock struct { - host domain.HostInfo - caps []domain.Capability -} - -func (c *CollectorMock) GatherInfoSystem() (domain.HostInfo, []domain.Capability) { - return c.host, c.caps -} - -type ConnectionMock struct { - regAgentErr error - regResp domain.RegisterAgentResponse -} - -func (c *ConnectionMock) RegisterAgent(ctx context.Context, RegisterData domain.RegisterAgentRequest) (domain.RegisterAgentResponse, error) { - return c.regResp, c.regAgentErr -} 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 94% rename from internal/domain/agent.go rename to shared/domain/agent.go index 8a5f062..2a24ae5 100644 --- a/internal/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/internal/domain/mapper.go b/shared/domain/mapper.go similarity index 97% rename from internal/domain/mapper.go rename to shared/domain/mapper.go index 2812e8e..587d361 100644 --- a/internal/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, 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