mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-08-07 07:14:50 +00:00
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>
This commit is contained in:
committed by
GitHub
parent
ee52551ca4
commit
a3265bc346
@@ -0,0 +1,271 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
pb "github.com/ultravioletrs/cocos/agent/runner"
|
||||
)
|
||||
|
||||
// MockEventService is a mock implementation of events.Service.
|
||||
type MockEventService struct {
|
||||
events []interface{}
|
||||
}
|
||||
|
||||
func (m *MockEventService) SendEvent(cmpID, event, status string, details json.RawMessage) {
|
||||
m.events = append(m.events, map[string]interface{}{
|
||||
"cmpID": cmpID,
|
||||
"event": event,
|
||||
"status": status,
|
||||
"details": details,
|
||||
})
|
||||
}
|
||||
|
||||
// TestNewRunnerService tests the creation of a new runner service.
|
||||
func TestNewRunnerService(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
|
||||
rs := New(logger, eventSvc)
|
||||
require.NotNil(t, rs)
|
||||
assert.NotNil(t, rs.logger)
|
||||
assert.NotNil(t, rs.eventSvc)
|
||||
assert.Nil(t, rs.currentAlgo)
|
||||
}
|
||||
|
||||
// TestRunWithBinaryAlgorithm tests running a binary algorithm.
|
||||
func TestRunWithBinaryAlgorithm(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-1",
|
||||
AlgoType: "bin",
|
||||
Algorithm: []byte("#!/bin/bash\necho 'test'"),
|
||||
Args: []string{"arg1", "arg2"},
|
||||
}
|
||||
|
||||
resp, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
assert.Equal(t, "test-1", resp.ComputationId)
|
||||
}
|
||||
|
||||
// TestRunWithPythonAlgorithm tests running a Python algorithm.
|
||||
func TestRunWithPythonAlgorithm(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-python",
|
||||
AlgoType: "python",
|
||||
Algorithm: []byte("print('hello')"),
|
||||
Args: []string{},
|
||||
Requirements: []byte("numpy==1.21.0"),
|
||||
}
|
||||
|
||||
resp, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
assert.Equal(t, "test-python", resp.ComputationId)
|
||||
}
|
||||
|
||||
// TestRunWithPythonAlgorithmNoRequirements tests running Python without requirements.
|
||||
func TestRunWithPythonAlgorithmNoRequirements(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-python-noreq",
|
||||
AlgoType: "python",
|
||||
Algorithm: []byte("print('hello')"),
|
||||
Args: []string{},
|
||||
}
|
||||
|
||||
resp, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
assert.Equal(t, "test-python-noreq", resp.ComputationId)
|
||||
}
|
||||
|
||||
// TestRunWithWasmAlgorithm tests running a WASM algorithm.
|
||||
func TestRunWithWasmAlgorithm(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-wasm",
|
||||
AlgoType: "wasm",
|
||||
Algorithm: []byte{0x00, 0x61, 0x73, 0x6d},
|
||||
Args: []string{},
|
||||
}
|
||||
|
||||
resp, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
assert.Equal(t, "test-wasm", resp.ComputationId)
|
||||
}
|
||||
|
||||
// TestRunWithDockerAlgorithm tests running a Docker algorithm.
|
||||
func TestRunWithDockerAlgorithm(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-docker",
|
||||
AlgoType: "docker",
|
||||
Algorithm: []byte("FROM ubuntu:latest\nRUN echo 'test'"),
|
||||
Args: []string{},
|
||||
}
|
||||
|
||||
resp, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
assert.Equal(t, "test-docker", resp.ComputationId)
|
||||
}
|
||||
|
||||
// TestRunWithUnsupportedAlgorithmType tests running with unsupported algorithm type.
|
||||
func TestRunWithUnsupportedAlgorithmType(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-unsupported",
|
||||
AlgoType: "unsupported",
|
||||
Algorithm: []byte("test"),
|
||||
Args: []string{},
|
||||
}
|
||||
|
||||
resp, err := rs.Run(context.Background(), req)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, resp)
|
||||
}
|
||||
|
||||
// TestRunAlreadyRunning tests running computation when one is already running.
|
||||
func TestRunAlreadyRunning(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
// Use a long-running bash script
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-running",
|
||||
AlgoType: "bin",
|
||||
Algorithm: []byte("#!/bin/bash\nsleep 30"),
|
||||
Args: []string{},
|
||||
}
|
||||
|
||||
// Start first computation (will run for 30 seconds)
|
||||
go func() {
|
||||
_, _ = rs.Run(context.Background(), req)
|
||||
}()
|
||||
|
||||
// Give it time to start
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Try to run another immediately - should fail
|
||||
resp, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
assert.Equal(t, "computation already running", resp.Error)
|
||||
}
|
||||
|
||||
// TestStopWhenRunning tests stopping a running computation.
|
||||
func TestStopWhenRunning(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-stop",
|
||||
AlgoType: "bin",
|
||||
Algorithm: []byte("#!/bin/bash\nsleep 10"),
|
||||
Args: []string{},
|
||||
}
|
||||
|
||||
_, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
|
||||
stopReq := &pb.StopRequest{
|
||||
ComputationId: "test-stop",
|
||||
}
|
||||
|
||||
stopResp, err := rs.Stop(context.Background(), stopReq)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, stopResp)
|
||||
}
|
||||
|
||||
// TestStopWhenNotRunning tests stopping when no computation is running.
|
||||
func TestStopWhenNotRunning(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
stopReq := &pb.StopRequest{
|
||||
ComputationId: "test-not-running",
|
||||
}
|
||||
|
||||
stopResp, err := rs.Stop(context.Background(), stopReq)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, stopResp)
|
||||
}
|
||||
|
||||
// TestConcurrentRun tests that concurrent runs are properly serialized.
|
||||
func TestConcurrentRun(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-concurrent",
|
||||
AlgoType: "bin",
|
||||
Algorithm: []byte("#!/bin/bash\nsleep 15"),
|
||||
Args: []string{},
|
||||
}
|
||||
|
||||
// Start first run in goroutine (will run for 15 seconds)
|
||||
go func() {
|
||||
_, _ = rs.Run(context.Background(), req)
|
||||
}()
|
||||
|
||||
// Give it time to actually start
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Concurrent attempt should fail
|
||||
resp2, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "computation already running", resp2.Error)
|
||||
}
|
||||
|
||||
// TestRunWithMultipleArgs tests running with multiple arguments.
|
||||
func TestRunWithMultipleArgs(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
eventSvc := &MockEventService{}
|
||||
rs := New(logger, eventSvc)
|
||||
|
||||
req := &pb.RunRequest{
|
||||
ComputationId: "test-multi-args",
|
||||
AlgoType: "bin",
|
||||
Algorithm: []byte("#!/bin/bash\necho $@"),
|
||||
Args: []string{"arg1", "arg2", "arg3", "arg4"},
|
||||
}
|
||||
|
||||
resp, err := rs.Run(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
assert.Equal(t, "test-multi-args", resp.ComputationId)
|
||||
}
|
||||
Reference in New Issue
Block a user