mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-09-27 12:04:54 +02:00
feat: expose platform variables in osmedeus eval command
Export platform detection functions (DetectDocker, DetectKubernetes, DetectCloudProvider) from internal/executor to allow reuse in CLI. Inject PlatformOS, PlatformArch, PlatformInDocker, PlatformInKubernetes, and PlatformCloudProvider variables in func eval command context.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -223,6 +224,13 @@ func (e *Executor) injectBuiltinVariables(cfg *config.Config, params map[string]
|
||||
// Version info
|
||||
execCtx.SetVariable("Version", core.VERSION)
|
||||
|
||||
// Platform detection variables
|
||||
execCtx.SetVariable("PlatformOS", runtime.GOOS)
|
||||
execCtx.SetVariable("PlatformArch", runtime.GOARCH)
|
||||
execCtx.SetVariable("PlatformInDocker", DetectDocker())
|
||||
execCtx.SetVariable("PlatformInKubernetes", DetectKubernetes())
|
||||
execCtx.SetVariable("PlatformCloudProvider", DetectCloudProvider())
|
||||
|
||||
// Target-based variables
|
||||
target := params["target"]
|
||||
targetFileParam := params["target_file"]
|
||||
@@ -373,7 +381,7 @@ func (e *Executor) debugLogTargetVariables(execCtx *core.ExecutionContext) {
|
||||
|
||||
execCtx.Logger.Debug("Target variables",
|
||||
zap.String("workflow", execCtx.WorkflowName),
|
||||
zap.String("run_id", execCtx.RunUUID),
|
||||
zap.String("run_uuid", execCtx.RunUUID),
|
||||
zap.String("Target", getStr("Target")),
|
||||
zap.String("TargetSpace", getStr("TargetSpace")),
|
||||
zap.String("Output", getStr("Output")),
|
||||
@@ -725,13 +733,16 @@ func (e *Executor) ExecuteModule(ctx context.Context, module *core.Workflow, par
|
||||
}
|
||||
|
||||
// Create execution context
|
||||
runID := uuid.New().String()[:8]
|
||||
runUUID := e.dbRunUUID
|
||||
if runUUID == "" {
|
||||
runUUID = uuid.New().String() // Full UUID when not in server mode
|
||||
}
|
||||
e.logger.Debug("Created execution context",
|
||||
zap.String("run_id", runID),
|
||||
zap.String("run_uuid", runUUID),
|
||||
zap.String("target", params["target"]),
|
||||
)
|
||||
execCtx := core.NewExecutionContext(module.Name, core.KindModule, runID, params["target"])
|
||||
execCtx.Logger = logger.WithWorkflow(module.Name, runID)
|
||||
execCtx := core.NewExecutionContext(module.Name, core.KindModule, runUUID, params["target"])
|
||||
execCtx.Logger = logger.WithWorkflow(module.Name, runUUID)
|
||||
|
||||
// Create and setup runner based on workflow configuration
|
||||
binaryPath, _ := os.Executable()
|
||||
@@ -860,7 +871,7 @@ func (e *Executor) ExecuteModule(ctx context.Context, module *core.Workflow, par
|
||||
result := &core.WorkflowResult{
|
||||
WorkflowName: module.Name,
|
||||
WorkflowKind: core.KindModule,
|
||||
RunUUID: runID,
|
||||
RunUUID: runUUID,
|
||||
Target: params["target"],
|
||||
Status: core.RunStatusRunning,
|
||||
StartTime: time.Now(),
|
||||
@@ -1378,13 +1389,16 @@ func (e *Executor) ExecuteFlow(ctx context.Context, flow *core.Workflow, params
|
||||
}
|
||||
|
||||
// Create execution context
|
||||
runID := uuid.New().String()[:8]
|
||||
runUUID := e.dbRunUUID
|
||||
if runUUID == "" {
|
||||
runUUID = uuid.New().String() // Full UUID when not in server mode
|
||||
}
|
||||
e.logger.Debug("Created flow execution context",
|
||||
zap.String("run_id", runID),
|
||||
zap.String("run_uuid", runUUID),
|
||||
zap.String("target", params["target"]),
|
||||
)
|
||||
execCtx := core.NewExecutionContext(flow.Name, core.KindFlow, runID, params["target"])
|
||||
execCtx.Logger = logger.WithWorkflow(flow.Name, runID)
|
||||
execCtx := core.NewExecutionContext(flow.Name, core.KindFlow, runUUID, params["target"])
|
||||
execCtx.Logger = logger.WithWorkflow(flow.Name, runUUID)
|
||||
|
||||
// Inject builtin variables
|
||||
e.logger.Debug("Injecting builtin variables for flow")
|
||||
@@ -1455,7 +1469,7 @@ func (e *Executor) ExecuteFlow(ctx context.Context, flow *core.Workflow, params
|
||||
result := &core.WorkflowResult{
|
||||
WorkflowName: flow.Name,
|
||||
WorkflowKind: core.KindFlow,
|
||||
RunUUID: runID,
|
||||
RunUUID: runUUID,
|
||||
Target: params["target"],
|
||||
Status: core.RunStatusRunning,
|
||||
StartTime: time.Now(),
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package executor
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DetectDocker checks if running inside a Docker container
|
||||
func DetectDocker() bool {
|
||||
// Method 1: Check for /.dockerenv file
|
||||
if _, err := os.Stat("/.dockerenv"); err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// Method 2: Check /proc/1/cgroup for /docker/ (Linux only)
|
||||
if runtime.GOOS == "linux" {
|
||||
data, err := os.ReadFile("/proc/1/cgroup")
|
||||
if err == nil && strings.Contains(string(data), "/docker/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DetectKubernetes checks if running inside a Kubernetes pod
|
||||
func DetectKubernetes() bool {
|
||||
// Method 1: Check for Kubernetes service account directory
|
||||
if _, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount"); err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// Method 2: Check /proc/1/cgroup for kubepods (Linux only)
|
||||
if runtime.GOOS == "linux" {
|
||||
data, err := os.ReadFile("/proc/1/cgroup")
|
||||
if err == nil {
|
||||
content := string(data)
|
||||
if strings.Contains(content, "/kubepods/") || strings.Contains(content, "/kubelet/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DetectCloudProvider detects AWS, GCP, Azure, or returns "local"
|
||||
func DetectCloudProvider() string {
|
||||
// Only works on Linux - check DMI information
|
||||
if runtime.GOOS != "linux" {
|
||||
return "local"
|
||||
}
|
||||
|
||||
// Check sys_vendor
|
||||
vendorPaths := []string{
|
||||
"/sys/class/dmi/id/sys_vendor",
|
||||
"/sys/devices/virtual/dmi/id/bios_vendor",
|
||||
}
|
||||
|
||||
for _, path := range vendorPaths {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
vendor := strings.ToLower(strings.TrimSpace(string(data)))
|
||||
|
||||
switch {
|
||||
case strings.Contains(vendor, "amazon"):
|
||||
return "aws"
|
||||
case strings.Contains(vendor, "google"):
|
||||
return "gcp"
|
||||
case strings.Contains(vendor, "microsoft"):
|
||||
return "azure"
|
||||
}
|
||||
}
|
||||
|
||||
return "local"
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package executor
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/j3ssie/osmedeus/v5/internal/config"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/core"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/template"
|
||||
)
|
||||
|
||||
func TestDetectDocker(t *testing.T) {
|
||||
result := DetectDocker()
|
||||
// On a non-Docker environment, this should be false
|
||||
t.Logf("DetectDocker() = %v", result)
|
||||
// This is primarily a smoke test - we just verify it doesn't panic
|
||||
}
|
||||
|
||||
func TestDetectKubernetes(t *testing.T) {
|
||||
result := DetectKubernetes()
|
||||
// On a non-Kubernetes environment, this should be false
|
||||
t.Logf("DetectKubernetes() = %v", result)
|
||||
// This is primarily a smoke test - we just verify it doesn't panic
|
||||
}
|
||||
|
||||
func TestDetectCloudProvider(t *testing.T) {
|
||||
result := DetectCloudProvider()
|
||||
t.Logf("DetectCloudProvider() = %s", result)
|
||||
// On a local machine, this should be "local"
|
||||
// This is primarily a smoke test - we just verify it doesn't panic
|
||||
}
|
||||
|
||||
func TestPlatformVariables(t *testing.T) {
|
||||
// Verify that runtime.GOOS and runtime.GOARCH return expected values
|
||||
t.Logf("runtime.GOOS = %s", runtime.GOOS)
|
||||
t.Logf("runtime.GOARCH = %s", runtime.GOARCH)
|
||||
|
||||
// Verify they are not empty
|
||||
if runtime.GOOS == "" {
|
||||
t.Error("runtime.GOOS is empty")
|
||||
}
|
||||
if runtime.GOARCH == "" {
|
||||
t.Error("runtime.GOARCH is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlatformVariablesInjection(t *testing.T) {
|
||||
// Create a minimal config
|
||||
cfg := &config.Config{
|
||||
BaseFolder: "/tmp",
|
||||
BinariesPath: "/tmp/bin",
|
||||
DataPath: "/tmp/data",
|
||||
WorkspacesPath: "/tmp/workspaces",
|
||||
}
|
||||
|
||||
// Create execution context
|
||||
execCtx := core.NewExecutionContext("test-workflow", core.KindModule, "test-run-uuid", "example.com")
|
||||
|
||||
// Create executor and inject variables
|
||||
e := NewExecutor()
|
||||
params := map[string]string{
|
||||
"target": "example.com",
|
||||
}
|
||||
e.injectBuiltinVariables(cfg, params, execCtx)
|
||||
|
||||
// Verify platform variables are set
|
||||
tests := []struct {
|
||||
name string
|
||||
expected interface{}
|
||||
}{
|
||||
{"PlatformOS", runtime.GOOS},
|
||||
{"PlatformArch", runtime.GOARCH},
|
||||
{"PlatformInDocker", DetectDocker()},
|
||||
{"PlatformInKubernetes", DetectKubernetes()},
|
||||
{"PlatformCloudProvider", DetectCloudProvider()},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
val, ok := execCtx.GetVariable(tt.name)
|
||||
if !ok {
|
||||
t.Errorf("variable %s not found in execution context", tt.name)
|
||||
return
|
||||
}
|
||||
if val != tt.expected {
|
||||
t.Errorf("variable %s = %v, expected %v", tt.name, val, tt.expected)
|
||||
}
|
||||
t.Logf("%s = %v", tt.name, val)
|
||||
})
|
||||
}
|
||||
|
||||
// Verify variables are in GetVariables() output
|
||||
allVars := execCtx.GetVariables()
|
||||
for _, tt := range tests {
|
||||
if _, ok := allVars[tt.name]; !ok {
|
||||
t.Errorf("variable %s not found in GetVariables() output", tt.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlatformVariablesTemplateRendering(t *testing.T) {
|
||||
// Create a minimal config
|
||||
cfg := &config.Config{
|
||||
BaseFolder: "/tmp",
|
||||
BinariesPath: "/tmp/bin",
|
||||
DataPath: "/tmp/data",
|
||||
WorkspacesPath: "/tmp/workspaces",
|
||||
}
|
||||
|
||||
// Create execution context
|
||||
execCtx := core.NewExecutionContext("test-workflow", core.KindModule, "test-run-uuid", "example.com")
|
||||
|
||||
// Create executor and inject variables
|
||||
e := NewExecutor()
|
||||
params := map[string]string{
|
||||
"target": "example.com",
|
||||
}
|
||||
e.injectBuiltinVariables(cfg, params, execCtx)
|
||||
|
||||
// Create template engine and test rendering
|
||||
engine := template.NewEngine()
|
||||
vars := execCtx.GetVariables()
|
||||
|
||||
// Debug: print all variables
|
||||
t.Logf("Total variables: %d", len(vars))
|
||||
for k, v := range vars {
|
||||
if k == "PlatformOS" || k == "PlatformArch" || k == "PlatformInDocker" || k == "PlatformInKubernetes" || k == "PlatformCloudProvider" {
|
||||
t.Logf("Variable %s = %v (type: %T)", k, v, v)
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
template string
|
||||
expected string
|
||||
}{
|
||||
{"echo {{PlatformOS}}", "echo " + runtime.GOOS},
|
||||
{"echo {{PlatformArch}}", "echo " + runtime.GOARCH},
|
||||
{"echo {{PlatformInDocker}}", "echo false"},
|
||||
{"echo {{PlatformInKubernetes}}", "echo false"},
|
||||
{"echo {{PlatformCloudProvider}}", "echo local"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.template, func(t *testing.T) {
|
||||
result, err := engine.Render(tt.template, vars)
|
||||
if err != nil {
|
||||
t.Errorf("error rendering template %q: %v", tt.template, err)
|
||||
return
|
||||
}
|
||||
if result != tt.expected {
|
||||
t.Errorf("render(%q) = %q, expected %q", tt.template, result, tt.expected)
|
||||
}
|
||||
t.Logf("render(%q) = %q", tt.template, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlatformVariablesWithStepDispatcher(t *testing.T) {
|
||||
// Create a minimal config
|
||||
cfg := &config.Config{
|
||||
BaseFolder: "/tmp",
|
||||
BinariesPath: "/tmp/bin",
|
||||
DataPath: "/tmp/data",
|
||||
WorkspacesPath: "/tmp/workspaces",
|
||||
}
|
||||
|
||||
// Create execution context
|
||||
execCtx := core.NewExecutionContext("test-workflow", core.KindModule, "test-run-uuid", "example.com")
|
||||
|
||||
// Create executor and inject variables
|
||||
e := NewExecutor()
|
||||
params := map[string]string{
|
||||
"target": "example.com",
|
||||
}
|
||||
e.injectBuiltinVariables(cfg, params, execCtx)
|
||||
|
||||
// Get the step dispatcher
|
||||
dispatcher := NewStepDispatcher()
|
||||
|
||||
// Create a test step
|
||||
step := &core.Step{
|
||||
Name: "test-platform-step",
|
||||
Type: core.StepTypeBash,
|
||||
Command: "echo {{PlatformOS}} {{PlatformArch}}",
|
||||
}
|
||||
|
||||
// Get the template engine from dispatcher and render
|
||||
vars := execCtx.GetVariables()
|
||||
engine := dispatcher.GetTemplateEngine()
|
||||
|
||||
result, err := engine.Render(step.Command, vars)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to render command: %v", err)
|
||||
}
|
||||
|
||||
expected := "echo " + runtime.GOOS + " " + runtime.GOARCH
|
||||
if result != expected {
|
||||
t.Errorf("Rendered command = %q, expected %q", result, expected)
|
||||
}
|
||||
t.Logf("Rendered command: %q", result)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
|
||||
"github.com/j3ssie/osmedeus/v5/internal/config"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/database"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/executor"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/functions"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/template"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/terminal"
|
||||
@@ -290,6 +292,14 @@ func runBulkFunctionEval(printer *terminal.Printer, script string) error {
|
||||
func executeFunctionForTarget(printer *terminal.Printer, script, target string) error {
|
||||
// Build context with target and params
|
||||
ctx := make(map[string]interface{})
|
||||
|
||||
// Inject platform detection variables (always available, even without target)
|
||||
ctx["PlatformOS"] = runtime.GOOS
|
||||
ctx["PlatformArch"] = runtime.GOARCH
|
||||
ctx["PlatformInDocker"] = executor.DetectDocker()
|
||||
ctx["PlatformInKubernetes"] = executor.DetectKubernetes()
|
||||
ctx["PlatformCloudProvider"] = executor.DetectCloudProvider()
|
||||
|
||||
if target != "" {
|
||||
ctx["target"] = target
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user