114 lines
2.8 KiB
Go
114 lines
2.8 KiB
Go
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
|
|
}
|