feat: add harness runner (scenario executor, alert receiver, action executor, judge, revert)

This commit is contained in:
seon committed 2026-09-24 02:06:08 +09:00
1 parent 8f5378b6ea
commit 5daf13b669
16 files changed
+2550

No files matched your search

+113
View File
@@ -0,0 +1,113 @@
package main
import (
"context"
"fmt"
"net"
"strings"
"time"
"github.com/jackc/pgx/v5"
)
// runStep executes one injection/revert step; the returned map is recorded as injections.params and reused by the revert.
func (a *App) runStep(ctx context.Context, sc *Scenario, st Step, runID string, prev map[string]any) (map[string]any, error) {
res := map[string]any{"method": st.Method}
var err error
switch st.Method {
case "none":
case "chaos":
name := strings.ToLower(sc.ID) + "-" + runID[len(runID)-6:]
var ref map[string]any
ref, err = a.applyChaos(ctx, st.Manifest, name)
for k, v := range ref {
res[k] = v
}
case "chaos_delete":
err = a.deleteChaos(ctx, prev)
case "kubectl_scale":
res["namespace"], res["deployment"], res["replicas"] = st.Namespace, st.Deployment, st.Replicas
err = a.scaleDeployment(ctx, st.Namespace, st.Deployment, st.Replicas)
case "ssh":
res["host"], res["command"] = st.Host, st.Command
var out string
out, err = a.sshRun(ctx, st.Host, st.Command)
res["output"] = truncate(out, 2000)
case "gcloud":
var r map[string]any
r, err = a.gcpOp(ctx, st.Op, st.Instance, st.MIG, st.Size)
for k, v := range r {
res[k] = v
}
case "psql":
res["host"], res["database"], res["sql"] = st.Host, st.Database, st.SQL
var out string
out, err = a.psqlExec(ctx, st.Host, st.Database, st.SQL)
res["output"] = truncate(out, 2000)
default:
err = fmt.Errorf("unknown step method %q", st.Method)
}
if err != nil {
res["error"] = err.Error()
return res, err
}
if st.WaitS > 0 {
select {
case <-time.After(time.Duration(st.WaitS) * time.Second):
case <-ctx.Done():
return res, ctx.Err()
}
}
return res, nil
}
// psqlExec runs SQL on db-primary / db-replica as the harness role over hostssl (pg_hba: harness from 10.20.0.0/20).
func (a *App) psqlExec(ctx context.Context, host, database, sqlText string) (string, error) {
ip := host
if net.ParseIP(host) == nil {
in, err := a.resolveInstance(ctx, host)
if err != nil {
return "", err
}
ip = in.IP
}
pw, err := readFileTrim(a.cfg.DBPassFile)
if err != nil {
return "", fmt.Errorf("db password: %w", err)
}
if database == "" {
database = "postgres"
}
cfg, err := pgx.ParseConfig(fmt.Sprintf("postgres://harness@%s:5432/%s?sslmode=require&connect_timeout=10", ip, database))
if err != nil {
return "", err
}
cfg.Password = pw
conn, err := pgx.ConnectConfig(ctx, cfg)
if err != nil {
return "", err
}
defer conn.Close(ctx)
rows, err := conn.Query(ctx, sqlText)
if err != nil {
return "", err
}
defer rows.Close()
var sb strings.Builder
for rows.Next() {
vals, err := rows.Values()
if err != nil {
return sb.String(), err
}
sb.WriteString(fmt.Sprint(vals...))
sb.WriteString("\n")
}
return sb.String(), rows.Err()
}
func truncate(s string, n int) string {
if len(s) > n {
return s[:n] + "...(truncated)"
}
return s
}