diff --git a/README.md b/README.md index 0653290..7c0fcb4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # secops-chaos +[![Build](https://github.com/OperantAI/secops-chaos/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/OperantAI/secops-chaos/actions/workflows/build.yml) + ### Usage ``` sh diff --git a/internal/experiments/experiments.go b/internal/experiments/experiments.go index 209a460..fe72d38 100644 --- a/internal/experiments/experiments.go +++ b/internal/experiments/experiments.go @@ -5,7 +5,6 @@ package experiments import ( "context" - "encoding/json" "fmt" "github.com/operantai/secops-chaos/internal/k8s" @@ -13,10 +12,12 @@ import ( "k8s.io/client-go/kubernetes" ) +// Experiments is a list of all experiments var Experiments = []Experiment{ &PrivilegedContainer{}, } +// Experiment is the interface for an experiment type Experiment interface { // Name returns the name of the experiment Name() string @@ -38,17 +39,18 @@ type Runner struct { experimentsConfig map[string]*ExperimentConfig } -type JSONOutput struct { - K8sVersion string `json:"k8s_version"` - Results []*Outcome `json:"results"` -} - +// Outcome is the result of an experiment type Outcome struct { Experiment string `json:"experiment"` Category string `json:"category"` Success bool `json:"success"` } +type JSONOutput struct { + K8sVersion string `json:"k8s_version"` + Results []*Outcome `json:"results"` +} + // NewRunner returns a new Runner func NewRunner(ctx context.Context, namespace string, allNamespaces bool, experiments []string) *Runner { // Create a new Kubernetes client @@ -98,38 +100,32 @@ func (r *Runner) Run() { } // RunVerifiers runs all verifiers in the Runner for the provided experiments -func (r *Runner) RunVerifiers(outputJSON bool) { - headers := []string{"Experiment", "Category", "Result"} - rows := [][]string{} +func (r *Runner) RunVerifiers(writeJSON bool) { + table := output.NewTable([]string{"Experiment", "Category", "Result"}) outcomes := []*Outcome{} for _, e := range Experiments { outcome, err := e.Verify(r.ctx, r.client, r.experimentsConfig[e.Name()]) if err != nil { output.WriteError("Verifier %s failed: %s", e.Name(), err) } - if outputJSON { + if writeJSON { outcomes = append(outcomes, outcome) } else { - rows = append(rows, []string{outcome.Experiment, outcome.Category, fmt.Sprintf("%t", outcome.Success)}) + table.AddRow([]string{outcome.Experiment, outcome.Category, fmt.Sprintf("%t", outcome.Success)}) } } - if outputJSON { + if writeJSON { k8sVersion, err := k8s.GetK8sVersion(r.client) if err != nil { output.WriteError("Failed to get Kubernetes version: %s", err) } - out := JSONOutput{ + output.WriteJSON(JSONOutput{ K8sVersion: k8sVersion.String(), Results: outcomes, - } - jsonOutput, err := json.MarshalIndent(out, "", " ") - if err != nil { - output.WriteError("Failed to marshal JSON: %s", err) - } - fmt.Println(string(jsonOutput)) + }) return } - output.WriteTable(headers, rows) + table.Render() } // Cleanup cleans up all experiments in the Runner diff --git a/internal/output/output.go b/internal/output/output.go index a9fa779..84ff615 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -1,11 +1,12 @@ package output import ( + "encoding/json" "fmt" "os" "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/lipgloss/table" + ltable "github.com/charmbracelet/lipgloss/table" ) const ( @@ -43,12 +44,37 @@ func WriteFatal(msg string, args ...interface{}) { os.Exit(1) } -func WriteTable(headers []string, rows [][]string) { - t := table.New(). +type table struct { + headers []string + rows [][]string +} + +// NewTable creates a new table with the given headers +func NewTable(headers []string) *table { + return &table{headers: headers} +} + +// AddRow adds a row to the table +func (t *table) AddRow(row []string) { + t.rows = append(t.rows, row) +} + +// Render renders the table, printing it to stdout +func (t *table) Render() { + tbl := ltable.New(). Border(lipgloss.NormalBorder()). BorderStyle(lipgloss.NewStyle().Foreground(TableBorderColor)). - Headers(headers...). - Rows(rows...) + Headers(t.headers...). + Rows(t.rows...) + + fmt.Println(tbl) +} - fmt.Println(t) +// WriteJSON writes the given output as pretty printed JSON to stdout +func WriteJSON(output interface{}) { + jsonOutput, err := json.MarshalIndent(output, "", " ") + if err != nil { + WriteError("Failed to marshal JSON: %s", err) + } + fmt.Println(string(jsonOutput)) }