# How the ChaosBlade Experiment Model Works Internally: Target-Action-Flag Architecture

> Explore the internal workings of the ChaosBlade experiment model. Understand how the target-action-flag architecture maps to SQLite records for persistent, reversible chaos experiments.

- Repository: [ChaosBlade/chaosblade](https://github.com/chaosblade-io/chaosblade)
- Tags: internals
- Published: 2026-02-27

---

**ChaosBlade represents every chaos experiment as a target-action-flag triple that maps to a SQLite record, enabling reversible conversion between CLI commands and persisted experiment state.**

The `chaosblade-io/chaosblade` repository implements a unified experiment model that decouples user-facing CLI syntax from internal execution logic. Understanding how the target-action-flag pattern persists in the data layer and reconstructs at runtime clarifies how ChaosBlade creates, destroys, and tracks experiments across Docker, Kubernetes, and host environments.

## Understanding the Target-Action-Flag Triple

The ChaosBlade experiment model consists of three hierarchical components that define what resource to attack, which fault to inject, and how to parameterize that fault.

### Target: The Resource Type

The **target** specifies the primary resource category under test. Valid values include `docker`, `k8s`, `network`, `cpu`, or `disk`. Internally, this maps to `spec.ExpModel.Target` and persists in the SQLite `experiment` table as the `command` column.

### Action: The Specific Fault

The **action** defines the precise failure mode to inject against the target. Examples include `delay` (network latency), `load` (CPU stress), or `remove` (container deletion). This value populates `spec.ExpModel.ActionName` and stores as the `sub_command` column in the database.

### Flag: Parameter Key-Value Pairs

**Flags** provide action-specific configuration through command-line key-value pairs such as `--time=3000` or `--interface=eth0`. The CLI collects these into `spec.ExpModel.ActionFlags` (a `map[string]string`), serializes them to a raw string for storage in the `flag` column, and parses them back during retrieval.

## Data Layer Representation in SQLite

ChaosBlade persists experiments using a lightweight SQLite backend defined in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go).

### The ExperimentModel Struct

The `ExperimentModel` struct maps directly to the database schema:

```go
type ExperimentModel struct {
    Uid        string // unique ID
    Command    string // target (e.g., "docker")
    SubCommand string // action (e.g., "remove")
    Flag       string // raw flag string, e.g. "--time=3000 --interface=eth0"
    Status     string
    Error      string
    CreateTime string
    UpdateTime string
}

```

### Persistence and Retrieval Flow

The data layer handles bidirectional conversion between structured models and raw storage:

- **Inserting** – `InsertExperimentModel` writes the `Flag` string directly to the database without parsing.
- **Querying** – `QueryExperimentModelsByCommand` retrieves the raw flag string, then delegates to `spec.ConvertCommandsToExpModel` to reconstruct the `ExpModel`.

## CLI to Model Conversion

When users execute `blade create`, the CLI constructs an `ExpModel` from Cobra command flags.

### Building the Model from CLI Input

The `createExpModel` function in [`cli/cmd/exp.go`](https://github.com/chaosblade-io/chaosblade/blob/main/cli/cmd/exp.go) transforms CLI input into the internal representation:

```go
func createExpModel(target, scope, actionName string, cmd *cobra.Command) *spec.ExpModel {
    expModel := &spec.ExpModel{
        Target:      target,
        Scope:       scope,
        ActionName:  actionName,
        ActionFlags: make(map[string]string, 0),
    }

    // Collect all non-false flag values from the cobra.Command
    cmd.Flags().VisitAll(func(flag *pflag.Flag) {
        if flag.Value.String() == "false" { return }
        expModel.ActionFlags[flag.Name] = flag.Value.String()
    })
    return expModel
}

```

### Parsing Raw Flag Strings

The external `chaosblade-spec-go` module provides the reverse conversion. The `ConvertCommandsToExpModel` function in [`spec/convert.go`](https://github.com/chaosblade-io/chaosblade/blob/main/spec/convert.go) parses stored flag strings:

```go
// ConvertCommandsToExpModel parses the raw flag string and returns a fully-filled ExpModel.
//   action  – the sub-command (e.g. "delay")
//   target  – the primary command (e.g. "network")
//   flags   – raw flag string, e.g. "--time=3000 --interface=eth0"
func ConvertCommandsToExpModel(action, target, flags string) *ExpModel {
    // 1. split flags on spaces while respecting quoted values
    // 2. support both "--key=value" and "--key value"
    // 3. populate map[string]string ActionFlags
    // 4. return &ExpModel{Target: target, ActionName: action, ActionFlags: parsed}
}

```

Validation tests in [`cli/cmd/destroy_test.go`](https://github.com/chaosblade-io/chaosblade/blob/main/cli/cmd/destroy_test.go) confirm the parsing logic:

```go
got := spec.ConvertCommandsToExpModel("network delay", "docker",
        "--time=3000 --interface=eth0")

```

This produces:

```go
&spec.ExpModel{
    Target:      "docker",
    ActionName:  "network delay",
    ActionFlags: map[string]string{"time":"3000","interface":"eth0"},
}

```

## Persistence and Retrieval in Practice

### Creating Experiments

The `createExperiment` function in [`cli/cmd/create.go`](https://github.com/chaosblade-io/chaosblade/blob/main/cli/cmd/create.go) orchestrates persistence:

```go
expModel := createExpModel(target, scope, actionCommandSpec.Name(), cmd)
uid, err := GetDS().InsertExperimentModel(&data.ExperimentModel{
    Uid:        uuid.NewString(),
    Command:    target,
    SubCommand: actionCommandSpec.Name(),
    Flag:       model2FlagString(expModel.ActionFlags), // joins map into raw string
    Status:     data.StatusPrepared,
})

```

The `model2FlagString` helper serializes the flag map to `"--key=value …"` format before storage.

### Destroying Experiments

The destroy flow reverses the process. In [`cli/cmd/destroy.go`](https://github.com/chaosblade-io/chaosblade/blob/main/cli/cmd/destroy.go), `getExecutorAndExpModelByRecord` retrieves and reconstructs the model:

```go
record, _ := GetDS().QueryExperimentModelByUid(uid)
expModel := spec.ConvertCommandsToExpModel(
    record.SubCommand, // action
    record.Command,    // target
    record.Flag)       // raw flag string

// Pass expModel to the appropriate executor
executor, _ := GetExecutor(expModel.Target)
executor.Exec(uid, ctx, expModel)

```

## Execution Path and Flag Iteration

Concrete executors receive the reconstructed `ExpModel` and translate `ActionFlags` into system commands. For example, in [`exec/docker/executor.go`](https://github.com/chaosblade-io/chaosblade/blob/main/exec/docker/executor.go):

```go
for k, v := range model.ActionFlags {
    argsArray = append(argsArray, fmt.Sprintf("--%s=%s", k, v))
}

```

This iteration constructs the final ChaosBlade command line, ensuring that flags specified during creation (e.g., `--container-id=abc123`) propagate accurately through the entire lifecycle.

## Summary

- **ChaosBlade experiment model** uses a target-action-flag triple to define chaos experiments consistently across CLI, storage, and execution layers.
- **Data persistence** stores the raw flag string in SQLite ([`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go)), while target and action map to `command` and `sub_command` columns.
- **Model conversion** relies on `createExpModel` (CLI → struct) and `spec.ConvertCommandsToExpModel` (string → struct) to maintain bidirectional integrity.
- **Lifecycle management** creates experiments via [`cli/cmd/create.go`](https://github.com/chaosblade-io/chaosblade/blob/main/cli/cmd/create.go), retrieves them via [`cli/cmd/destroy.go`](https://github.com/chaosblade-io/chaosblade/blob/main/cli/cmd/destroy.go), and executes them through target-specific executors that iterate over `ActionFlags`.

## Frequently Asked Questions

### How does ChaosBlade store experiment flags in the database?

ChaosBlade serializes the `ActionFlags` map to a raw string (e.g., `"--time=3000 --interface=eth0"`) and stores it in the `flag` column of the SQLite `experiment` table defined in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go). The target and action are stored separately in the `command` and `sub_command` columns.

### What happens when I run `blade destroy`?

The destroy command queries the SQLite database for the experiment UID, retrieves the raw target-action-flag data, and calls `spec.ConvertCommandsToExpModel` in the external `chaosblade-spec-go` module to reconstruct the `ExpModel`. This model is then passed to the appropriate executor (e.g., Docker or Kubernetes) to trigger the recovery logic.

### Where is the target-action-flag parsing logic implemented?

Core parsing logic resides in the external module `chaosblade-spec-go` within [`spec/convert.go`](https://github.com/chaosblade-io/chaosblade/blob/main/spec/convert.go). The function `ConvertCommandsToExpModel` handles splitting flag strings, respecting quoted values, and supporting both `--key=value` and `--key value` syntax. The ChaosBlade repository itself manages the CLI-to-model conversion in [`cli/cmd/exp.go`](https://github.com/chaosblade-io/chaosblade/blob/main/cli/cmd/exp.go) via `createExpModel`.

### Can I manually construct an experiment model programmatically?

Yes. You can construct an `ExpModel` directly by populating the `Target`, `ActionName`, and `ActionFlags` fields, then pass it to the data layer. For example, calling `spec.ConvertCommandsToExpModel("delay", "network", "--time=3000")` returns a fully populated model that can be inserted via `InsertExperimentModel` in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go) or executed directly through the appropriate executor.