Files
cocos/cmd/log-forwarder/main.go
T
Sammy Kerata Oina a3265bc346 NOISSUE - Introduce computation runner, log forwarder, ingress, and egress proxy services. (#559)
* feat: Introduce computation runner, log forwarder, ingress, and egress proxy services.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: Update Go environment variable parsing and build system to use new architecture and repository.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: Update package sources to `sammyoina/cocos-ai` at a specific commit, add log-forwarder pre-start hook, and rename proxy binaries.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* chore: Update build system references to a specific commit and enhance logging for service connections and message processing.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* build: Update package source repositories and versions, migrate client logging to slog, and adjust ingress/egress proxy build and install steps.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* debug stuck

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* debug

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* debug

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: add HTTP/2 support to egress proxy and update build system to use specific commit hashes

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: enhance egress proxy CONNECT handling, update package sources, and add gRPC test utility

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: Update build system for various services to a specific commit from a new repository, change agent gRPC port to 7001, and add a gRPC test client.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: Migrate agent-internal gRPC communication to Unix sockets, set ingress proxy to port 7002, and update build hashes.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* refactor: Remove standalone ingress-proxy systemd service and update component versions.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* fix: Prevent computation re-initialization in agent and update component versions across several packages.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: update package versions and enable h2c support in ingress proxy.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: refactor ingress proxy to support HTTP/2 over Unix sockets and update component versions.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: Update build system package sources to `ultravioletrs/cocos` and reduce agent logging verbosity.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* refactor: improve error handling in proxy commands and remove unused gRPC test

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* test: add mock service state return value in handleRunReqChunks test

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* feat: add comprehensive tests for service and proxy components

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* fix linter

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* improve coverage

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* test: add gRPC client and ingress adapter tests, and update egress proxy tests.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* improve coverage

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

---------

Signed-off-by: Sammy Oina <sammyoina@gmail.com>
2026-02-09 10:38:21 +01:00

156 lines
3.7 KiB
Go

// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"fmt"
"log/slog"
"net"
"os"
"os/signal"
"syscall"
mglog "github.com/absmach/supermq/logger"
"github.com/caarlos0/env/v11"
"github.com/ultravioletrs/cocos/agent/cvms"
pb "github.com/ultravioletrs/cocos/agent/log"
"github.com/ultravioletrs/cocos/agent/log/service"
"github.com/ultravioletrs/cocos/pkg/clients"
cvmsgrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc/cvm"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
)
const (
svcName = "log-forwarder"
socketPath = "/run/cocos/log.sock"
envPrefixCVMGRPC = "AGENT_CVM_GRPC_"
)
type config struct {
LogLevel string `env:"LOG_FORWARDER_LOG_LEVEL" envAlternate:"AGENT_LOG_LEVEL" envDefault:"debug"`
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
var cfg config
if err := env.Parse(&cfg); err != nil {
fmt.Printf("failed to load %s configuration : %s\n", svcName, err)
os.Exit(1)
}
var exitCode int
defer mglog.ExitWithError(&exitCode)
var level slog.Level
if err := level.UnmarshalText([]byte(cfg.LogLevel)); err != nil {
fmt.Println(err)
exitCode = 1
return
}
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level}))
// Remove existing socket if it exists
if _, err := os.Stat(socketPath); err == nil {
if err := os.Remove(socketPath); err != nil {
logger.Error(fmt.Sprintf("failed to remove existing socket: %s", err))
exitCode = 1
return
}
}
dir := socketPath[:len(socketPath)-len("/log.sock")]
if err := os.MkdirAll(dir, 0o755); err != nil {
logger.Error(fmt.Sprintf("failed to create socket directory: %s", err))
exitCode = 1
return
}
lis, err := net.Listen("unix", socketPath)
if err != nil {
logger.Error(fmt.Sprintf("failed to listen on socket: %s", err))
exitCode = 1
return
}
if err := os.Chmod(socketPath, 0o777); err != nil {
logger.Error(fmt.Sprintf("failed to chmod socket: %s", err))
exitCode = 1
return
}
// Connect to Manager
cvmGrpcConfig := clients.StandardClientConfig{}
if err := env.ParseWithOptions(&cvmGrpcConfig, env.Options{Prefix: envPrefixCVMGRPC}); err != nil {
logger.Error(fmt.Sprintf("failed to load %s gRPC client configuration : %s", svcName, err))
exitCode = 1
return
}
cvmClient, cvmsClient, err := cvmsgrpc.NewCVMClient(cvmGrpcConfig)
if err != nil {
logger.Error(fmt.Sprintf("failed to connect to CVM manager: %s", err))
exitCode = 1
return
}
defer cvmClient.Close()
// Create stream to Manager
stream, err := cvmsClient.Process(ctx)
if err != nil {
logger.Error(fmt.Sprintf("failed to create stream to manager: %s", err))
exitCode = 1
return
}
logQueue := make(chan *cvms.ClientStreamMessage, 1000)
grpcServer := grpc.NewServer()
svc := service.New(logger, cvmsClient, logQueue)
pb.RegisterLogCollectorServer(grpcServer, svc)
// Log Consumer Goroutine
g.Go(func() error {
for {
select {
case <-ctx.Done():
return nil
case msg := <-logQueue:
if err := stream.Send(msg); err != nil {
logger.Error(fmt.Sprintf("failed to send log to manager: %s", err))
// Reconnect logic would go here
}
}
}
})
g.Go(func() error {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(ch)
select {
case <-ch:
logger.Info("Received signal, shutting down...")
cancel()
grpcServer.GracefulStop()
return nil
case <-ctx.Done():
return ctx.Err()
}
})
g.Go(func() error {
logger.Info(fmt.Sprintf("%s started on %s", svcName, socketPath))
return grpcServer.Serve(lis)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("%s terminated: %s", svcName, err))
}
}