COCOS-101 - Switch to self-contained algorithms as executables (#119)

* Switch to self-contained algorithms as executables

Transitioned from using Python scripts to self-contained binary executables for running algorithms, improving modularity and reducing dependencies. This change removes the reliance on a Python environment, as evident by the removal of Python setup and packages from the build configuration. The service now creates temporary executable files for algorithm runs, handling all permissions and cleanup, enhancing security and maintaining clean execution states. A warning is logged if computation fails, aiding in debugging. Additionally, updated manual tests to reflect these changes in the agent's handling of algorithms.

Refactors:
- Removed Python runtime const since it's no longer needed.
- Updated documentation and test commands to reflect the change from .py to .bin for algorithm files.

Build config:
- Removed Python and pip packages to reduce the build size and complexity.

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

* Update agent service.go file with new constants and file permission

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

* Refine singular usage of 'algorithm' across modules

Standardized terminology throughout the project to refer to 'algorithm' in the singular form rather than plural. Streamlined various documentations, string constants, function names, and variable names to bring cohesiveness and eliminate ambiguity when handling algorithms across README files, CLI interfaces, and internal API representations.

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

* Fix state names and indices in state_string.go

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

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
This commit is contained in:
Sammy Kerata Oina
2024-04-29 16:48:17 +03:00
committed by GitHub
parent 4b5000d107
commit 8d082567d7
12 changed files with 66 additions and 94 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ based on the [Confidential Computing][cc] and [Trusted Execution Environments (T
</p>
With Cocos AI it becomes possible to run AI/ML workloads on combined datasets from multiple organizations
while guaranteeing the privacy and security of the data and the algorithms.
while guaranteeing the privacy and security of the data and the algorithm.
Data is always encrypted, protected by hardware secure enclaves (Trusted Execution Environments),
attested via secure remote attestation protocols, and invisible to cloud processors or any other
3rd party to which computation is offloaded.
+1 -1
View File
@@ -26,7 +26,7 @@ type Computation struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Datasets Datasets `json:"datasets,omitempty"`
Algorithm Algorithm `json:"algorithms,omitempty"`
Algorithm Algorithm `json:"algorithm,omitempty"`
ResultConsumers []string `json:"result_consumers,omitempty"`
AgentConfig AgentConfig `json:"agent_config,omitempty"`
}
+30 -12
View File
@@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"log/slog"
"os"
"os/exec"
"slices"
@@ -20,8 +21,12 @@ import (
var _ Service = (*agentService)(nil)
// ReportDataSize is the size of the report data expected by the attestation service.
const ReportDataSize = 64
const (
// ReportDataSize is the size of the report data expected by the attestation service.
ReportDataSize = 64
socketPath = "unix_socket"
algoFilePermission = 0o700
)
var (
// ErrMalformedEntity indicates malformed entity specification (e.g.
@@ -67,11 +72,6 @@ type agentService struct {
eventSvc events.Service // Service for publishing events related to computation.
}
const (
socketPath = "unix_socket"
pyRuntime = "python3"
)
var _ Service = (*agentService)(nil)
// New instantiates the agent service implementation.
@@ -204,6 +204,7 @@ func (as *agentService) runComputation() {
result, err := run(as.algorithm, as.datasets[0])
if err != nil {
as.runError = err
as.sm.logger.Warn(fmt.Sprintf("computation failed with error: %s", err.Error()))
as.publishEvent("failed", json.RawMessage{})()
return
}
@@ -234,17 +235,34 @@ func run(algoContent, dataContent []byte) ([]byte, error) {
go socket.AcceptConnection(listener, dataChannel, errorChannel)
// Construct the Python script content with CSV data as a command-line argument
script := string(algoContent)
f, err := os.CreateTemp("", "algorithm")
if err != nil {
return nil, fmt.Errorf("error creating algorithm file: %v", err)
}
defer os.Remove(f.Name())
if _, err := f.Write(algoContent); err != nil {
return nil, fmt.Errorf("error writing algorithm to file: %v", err)
}
if err := os.Chmod(f.Name(), algoFilePermission); err != nil {
return nil, fmt.Errorf("error changing file permissions: %v", err)
}
if err := f.Close(); err != nil {
return nil, fmt.Errorf("error closing file: %v", err)
}
// Construct the executable with CSV data as a command-line argument
data := string(dataContent)
cmd := exec.Command(pyRuntime, "-c", script, data, socketPath)
cmd := exec.Command(f.Name(), data, socketPath)
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("error starting Python script: %v", err)
return nil, fmt.Errorf("error starting algorithm: %v", err)
}
if err := cmd.Wait(); err != nil {
return nil, fmt.Errorf("python script execution error: %v", err)
return nil, fmt.Errorf("algorithm execution error: %v", err)
}
select {
+2 -2
View File
@@ -17,9 +17,9 @@ func _() {
_ = x[complete-6]
}
const _state_name = "idlereceivingManifestsreceivingAlgorithmsreceivingDatarunningresultsReadycomplete"
const _state_name = "idlereceivingManifestreceivingAlgorithmreceivingDatarunningresultsReadycomplete"
var _state_index = [...]uint8{0, 4, 22, 41, 54, 61, 73, 81}
var _state_index = [...]uint8{0, 4, 21, 39, 52, 59, 71, 79}
func (i state) String() string {
if i < 0 || i >= state(len(_state_index)-1) {
+1 -1
View File
@@ -1,6 +1,6 @@
# Agent CLI
This repository contains the command-line interface (CLI) tool for interacting with the Agent and manager service. The CLI allows you to perform various tasks such as running computations, uploading algorithms and datasets, and retrieving results.
This repository contains the command-line interface (CLI) tool for interacting with the Agent and manager service. The CLI allows you to perform various tasks such as running computations, uploading algorithm and datasets, and retrieving results.
## Build
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"github.com/ultravioletrs/cocos/agent"
)
func (cli *CLI) NewAlgorithmsCmd() *cobra.Command {
func (cli *CLI) NewAlgorithmCmd() *cobra.Command {
return &cobra.Command{
Use: "algo",
Short: "Upload an algorithm binary",
+1 -1
View File
@@ -84,7 +84,7 @@ func main() {
}
// Agent Commands
rootCmd.AddCommand(cliSVC.NewAlgorithmsCmd())
rootCmd.AddCommand(cliSVC.NewAlgorithmCmd())
rootCmd.AddCommand(cliSVC.NewDatasetsCmd())
rootCmd.AddCommand(cliSVC.NewResultsCmd())
attestaionCmd := cliSVC.NewAttestationCmd()
-4
View File
@@ -42,7 +42,3 @@ BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y
# host-qemu for gitlab testing
BR2_PACKAGE_HOST_QEMU=y
BR2_PACKAGE_HOST_QEMU_SYSTEM_MODE=y
# Python
BR2_PACKAGE_PYTHON3=y
BR2_PACKAGE_PYTHON_PIP=y
+1 -1
View File
@@ -3,6 +3,6 @@ config BR2_PACKAGE_AGENT
default y
help
Confidential Computing Agent is a state machine capable of
receiving data and algorithms, running computations, and
receiving datasets and algorithm, running computations, and
fetching the attestation report from within the
Confidential VM.
+8 -7
View File
@@ -4,14 +4,15 @@
Throughout the tests, we assume that our current working directory is the root of the `cocos` repository, both on the host machine and in the VM.
### Python requirements
### Algorithm requirements
Do this in the VM.
```sh
pip3 install pandas scikit-learn
Agent accepts the algorithm as a binary that take in two command line arguments.
```shell
algorithm-file <dataset as string> <unix socket path>
```
The algorithm program should return the results to a socket and an example can be seen in this [file](./algo/lin_reg.py).
### Agent-CLI interaction
Agent is started automatically in the VM when launched but requires configuration and manifest to be passed by manager. Alternatively you can pass configuration using this [simplified script](./agent-config/main.go)
@@ -32,8 +33,8 @@ go run cmd/cli/main.go attestation get '<report_data>'
go run cmd/cli/main.go attestation validate '<attesation>' --report_data '<report_data>'
# Run the CLI program with algorithm input
go run cmd/cli/main.go algo test/manual/algo/lin_reg.py Algorithm1 AlgorithmProvider1
# 2023/09/21 10:43:53 Uploading algorithm binary: test/manual/algo/lin_reg.py
go run cmd/cli/main.go algo test/manual/algo/lin_reg.bin Algorithm1 AlgorithmProvider1
# 2023/09/21 10:43:53 Uploading algorithm binary: test/manual/algo/lin_reg.bin
# Run the CLI program with dataset input
go run cmd/cli/main.go data test/manual/data/iris.csv Dataset1 Provider1
+11 -63
View File
@@ -13,64 +13,12 @@ import (
"strconv"
"github.com/mdlayher/vsock"
"github.com/ultravioletrs/cocos/pkg/manager"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/manager"
pkgmanager "github.com/ultravioletrs/cocos/pkg/manager"
"google.golang.org/protobuf/proto"
)
const VsockConfigPort uint32 = 9999
type AgentConfig struct {
LogLevel string `json:"log_level"`
InstanceID string `json:"instance_id"`
Host string `json:"host"`
Port string `json:"port"`
CertFile string `json:"cert_file"`
KeyFile string `json:"server_key"`
AttestedTls bool `json:"attested_tls"`
}
type Computation struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Datasets Datasets `json:"datasets,omitempty"`
Algorithms Algorithms `json:"algorithms,omitempty"`
ResultConsumers []string `json:"result_consumers,omitempty"`
AgentConfig AgentConfig `json:"agent_config,omitempty"`
}
func (d *Datasets) String() string {
dat, err := json.Marshal(d)
if err != nil {
return ""
}
return string(dat)
}
func (a *Algorithms) String() string {
dat, err := json.Marshal(a)
if err != nil {
return ""
}
return string(dat)
}
type Dataset struct {
Dataset []byte `json:"-"`
Provider string `json:"provider,omitempty"`
ID string `json:"id,omitempty"`
}
type Datasets []Dataset
type Algorithm struct {
Algorithm []byte `json:"-"`
Provider string `json:"provider,omitempty"`
ID string `json:"id,omitempty"`
}
type Algorithms []Algorithm
func main() {
attestedTLS := false
@@ -85,16 +33,16 @@ func main() {
log.Fatalf("usage: %s <attested-tls>", os.Args[0])
}
l, err := vsock.Listen(9997, nil)
l, err := vsock.Listen(manager.ManagerVsockPort, nil)
if err != nil {
log.Fatal(err)
}
ac := Computation{
ac := agent.Computation{
ID: "123",
Datasets: Datasets{Dataset{ID: "1", Provider: "pr1"}},
Algorithms: Algorithms{Algorithm{ID: "1", Provider: "pr1"}},
Datasets: agent.Datasets{agent.Dataset{ID: "1", Provider: "pr1"}},
Algorithm: agent.Algorithm{ID: "1", Provider: "pr1"},
ResultConsumers: []string{"1"},
AgentConfig: AgentConfig{
AgentConfig: agent.AgentConfig{
LogLevel: "debug",
Port: "7002",
AttestedTls: attestedTLS,
@@ -115,7 +63,7 @@ func main() {
continue
}
conn.Close()
var mes manager.ClientStreamMessage
var mes pkgmanager.ClientStreamMessage
if err := proto.Unmarshal(b[:n], &mes); err != nil {
log.Println(err)
}
@@ -123,8 +71,8 @@ func main() {
}
}
func SendAgentConfig(cid uint32, ac Computation) error {
conn, err := vsock.Dial(cid, VsockConfigPort, nil)
func SendAgentConfig(cid uint32, ac agent.Computation) error {
conn, err := vsock.Dial(cid, manager.VsockConfigPort, nil)
if err != nil {
return err
}
+9
View File
@@ -0,0 +1,9 @@
# Algorithm
Agent accepts binaries programs. To use the python program you need to bundle or compile it.
In this example we'll use [pyinstaller](https://pypi.org/project/pyinstaller/)
```shell
pip install -U pyinstaller
pyinstaller lin_reg.py
```