mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-23 04:10:25 +00:00
8eb1fac9ad
* Refactor and update dependencies in the project - Updated go.sum to replace `github.com/absmach/magistrala` with `github.com/absmach/supermq` across various modules. - Removed VSock configuration from environment variables and QEMU arguments. - Updated QEMU configuration and related tests to remove references to guest CID and VSock. - Added new HTTP transport layer for API endpoints in the manager. - Introduced Prometheus monitoring configuration with alert rules and Alertmanager setup. - Updated service and VM interfaces to remove unused methods and references. - Refactored tests to align with the new structure and dependencies. Signed-off-by: Sammy Oina <sammyoina@gmail.com> * Add MaxVMs configuration and enforce limit on VM creation Signed-off-by: Sammy Oina <sammyoina@gmail.com> * Add comprehensive tests for HTTP transport handlers and endpoints Signed-off-by: Sammy Oina <sammyoina@gmail.com> * Add test case for exceeding maximum number of VMs in TestRun Signed-off-by: Sammy Oina <sammyoina@gmail.com> * Improve error handling in TestHandlerWithCustomRouter to ensure response writing is checked Signed-off-by: Sammy Oina <sammyoina@gmail.com> * Update dependencies to latest versions - Upgrade cel.dev/expr from v0.23.0 to v0.24.0 - Upgrade github.com/absmach/supermq from v0.16.0 to v0.17.0 - Upgrade github.com/cenkalti/backoff from v4.3.0 to v5.0.2 - Upgrade github.com/cncf/xds/go to v0.0.0-20250501225837-2ac532fd4443 - Upgrade github.com/go-chi/chi/v5 from v5.2.1 to v5.2.2 - Upgrade github.com/go-jose/go-jose/v3 from v3.0.3 to v3.0.4 - Upgrade github.com/gofrs/uuid/v5 from v5.3.0 to v5.3.2 - Upgrade github.com/prometheus/client_golang from v1.22.0 to v1.23.0 - Upgrade github.com/prometheus/client_model from v0.6.1 to v0.6.2 - Upgrade github.com/prometheus/common from v0.62.0 to v0.65.0 - Upgrade github.com/prometheus/procfs from v0.15.1 to v0.16.1 - Upgrade go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp from v0.60.0 to v0.62.0 - Upgrade go.opentelemetry.io/otel/exporters/otlp/otlptrace from v1.36.0 to v1.37.0 - Upgrade golang.org/x/crypto from v0.39.0 to v0.40.0 - Upgrade golang.org/x/sys from v0.33.0 to v0.34.0 - Upgrade golang.org/x/text from v0.26.0 to v0.27.0 - Upgrade golang.org/x/time from v0.11.0 to v0.12.0 - Upgrade google.golang.org/grpc from v1.73.0 to v1.74.2 Signed-off-by: Sammy Oina <sammyoina@gmail.com> --------- Signed-off-by: Sammy Oina <sammyoina@gmail.com>
234 lines
5.1 KiB
Go
234 lines
5.1 KiB
Go
// Copyright (c) Ultraviolet
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package qemu
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/gofrs/uuid"
|
|
"github.com/ultravioletrs/cocos/internal"
|
|
"github.com/ultravioletrs/cocos/manager/vm"
|
|
"github.com/ultravioletrs/cocos/pkg/manager"
|
|
)
|
|
|
|
const (
|
|
firmwareVars = "OVMF_VARS"
|
|
KernelFile = "bzImage"
|
|
rootfsFile = "rootfs.cpio"
|
|
tmpDir = "/tmp"
|
|
interval = 5 * time.Second
|
|
shutdownTimeout = 30 * time.Second
|
|
)
|
|
|
|
type VMInfo struct {
|
|
Config Config
|
|
LaunchTCB uint64 `env:"LAUNCH_TCB" envDefault:"0"`
|
|
}
|
|
|
|
type qemuVM struct {
|
|
vmi VMInfo
|
|
cmd *exec.Cmd
|
|
cvmId string
|
|
logger *slog.Logger
|
|
vm.StateMachine
|
|
}
|
|
|
|
func NewVM(config interface{}, cvmId string, logger *slog.Logger) vm.VM {
|
|
return &qemuVM{
|
|
vmi: config.(VMInfo),
|
|
cvmId: cvmId,
|
|
StateMachine: vm.NewStateMachine(),
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (v *qemuVM) Start() (err error) {
|
|
defer func() {
|
|
if err == nil {
|
|
go v.checkVMProcessPeriodically()
|
|
}
|
|
}()
|
|
// Create unique qemu device identifiers
|
|
id, err := uuid.NewV4()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v.vmi.Config.NetDevConfig.ID = fmt.Sprintf("%s-%s", v.vmi.Config.NetDevConfig.ID, id)
|
|
v.vmi.Config.SEVSNPConfig.ID = fmt.Sprintf("%s-%s", v.vmi.Config.SEVSNPConfig.ID, id)
|
|
v.vmi.Config.TDXConfig.ID = fmt.Sprintf("%s-%s", v.vmi.Config.TDXConfig.ID, id)
|
|
|
|
if !v.vmi.Config.EnableSEVSNP && !v.vmi.Config.EnableTDX {
|
|
// Copy firmware vars file.
|
|
srcFile := v.vmi.Config.OVMFVarsConfig.File
|
|
dstFile := fmt.Sprintf("%s/%s-%s.fd", tmpDir, firmwareVars, id)
|
|
err = internal.CopyFile(srcFile, dstFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v.vmi.Config.OVMFVarsConfig.File = dstFile
|
|
}
|
|
|
|
exe, args, err := v.executableAndArgs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v.cmd = exec.Command(exe, args...)
|
|
v.cmd.Stdout = &vm.Stdout{StateMachine: v.StateMachine, Logger: v.logger.With(slog.String("cvm", v.cvmId))}
|
|
v.cmd.Stderr = &vm.Stderr{StateMachine: v.StateMachine, Logger: v.logger.With(slog.String("cvm", v.cvmId))}
|
|
|
|
return v.cmd.Start()
|
|
}
|
|
|
|
func (v *qemuVM) Stop() error {
|
|
defer func() {
|
|
err := v.StateMachine.Transition(manager.StopComputationRun)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}()
|
|
err := v.cmd.Process.Signal(syscall.SIGTERM)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send SIGTERM: %v", err)
|
|
}
|
|
|
|
if v.vmi.Config.CertsMount != "" {
|
|
if err := os.RemoveAll(v.vmi.Config.CertsMount); err != nil {
|
|
return fmt.Errorf("failed to remove certs mount: %v", err)
|
|
}
|
|
}
|
|
|
|
if v.vmi.Config.EnvMount != "" {
|
|
if err := os.RemoveAll(v.vmi.Config.EnvMount); err != nil {
|
|
return fmt.Errorf("failed to remove env mount: %v", err)
|
|
}
|
|
}
|
|
|
|
done := make(chan error, 1)
|
|
go func() {
|
|
_, err := v.cmd.Process.Wait()
|
|
done <- err
|
|
}()
|
|
|
|
select {
|
|
case err := <-done:
|
|
return err
|
|
case <-time.After(shutdownTimeout):
|
|
err := v.cmd.Process.Kill()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to kill process: %v", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (v *qemuVM) SetProcess(pid int) error {
|
|
process, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
exe, args, err := v.executableAndArgs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v.cmd = exec.Command(exe, args...)
|
|
v.cmd.Process = process
|
|
return nil
|
|
}
|
|
|
|
func (v *qemuVM) GetProcess() int {
|
|
return v.cmd.Process.Pid
|
|
}
|
|
|
|
func (v *qemuVM) executableAndArgs() (string, []string, error) {
|
|
exe, err := exec.LookPath(v.vmi.Config.QemuBinPath)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
args := v.vmi.Config.ConstructQemuArgs()
|
|
|
|
if v.vmi.Config.UseSudo {
|
|
args = append([]string{exe}, args...)
|
|
exe = "sudo"
|
|
}
|
|
|
|
return exe, args, nil
|
|
}
|
|
|
|
func (v *qemuVM) checkVMProcessPeriodically() {
|
|
for {
|
|
if !processExists(v.GetProcess()) {
|
|
break
|
|
}
|
|
time.Sleep(interval)
|
|
}
|
|
}
|
|
|
|
func processExists(pid int) bool {
|
|
process, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// On Unix systems, FindProcess always succeeds and returns a Process for the given pid, regardless of whether the process exists.
|
|
// To test whether the process actually exists, see whether p.Signal(syscall.Signal(0)) reports an error.
|
|
if err = process.Signal(syscall.Signal(0)); err == nil {
|
|
return true
|
|
}
|
|
if err == syscall.ESRCH {
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (v *qemuVM) GetConfig() interface{} {
|
|
return v.vmi
|
|
}
|
|
|
|
func SEVSNPEnabled(cpuinfo, kernelParam string) bool {
|
|
return strings.Contains(cpuinfo, "sev_snp") && strings.TrimSpace(kernelParam) == "Y"
|
|
}
|
|
|
|
func TDXEnabled(cpuinfo, kernelParam string) bool {
|
|
return strings.Contains(cpuinfo, "tdx_host_platform") && strings.TrimSpace(kernelParam) == "Y"
|
|
}
|
|
|
|
func SEVSNPEnabledOnHost() bool {
|
|
cpuinfo, err := os.ReadFile("/proc/cpuinfo")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
kernelParam, err := os.ReadFile("/sys/module/kvm_amd/parameters/sev_snp")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return SEVSNPEnabled(string(cpuinfo), string(kernelParam))
|
|
}
|
|
|
|
func TDXEnabledOnHost() bool {
|
|
cpuinfo, err := os.ReadFile("/proc/cpuinfo")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
kernelParam, err := os.ReadFile("/sys/module/kvm_intel/parameters/tdx")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return TDXEnabled(string(cpuinfo), string(kernelParam))
|
|
}
|