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 }