Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
36 changes: 16 additions & 20 deletions internal/experiments/experiments.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ package experiments

import (
"context"
"encoding/json"
"fmt"

"github.com/operantai/secops-chaos/internal/k8s"
"github.com/operantai/secops-chaos/internal/output"
"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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
38 changes: 32 additions & 6 deletions internal/output/output.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -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))
}