How to Create Custom Chaos Experiment Scenarios for New Targets in ChaosBlade
To create custom chaos experiment scenarios for new targets in ChaosBlade, implement the spec.Executor interface with Create, Destroy, and optional Query methods, define a command model spec in exec/<target>/spec.go, and register the executor in cli/cmd/exp.go using the createExecutorKey function.
ChaosBlade models every chaos experiment as a target → action → flags triple. Extending the framework to support new infrastructure or services requires implementing the executor interface and registering your components in the CLI. This guide walks through the exact source code locations and implementation patterns used in the chaosblade-io/chaosblade repository to add a custom target like myservice.
Step 1: Define the Command Model Spec
Create exec/<target>/spec.go to declare the target's metadata, default flags, and examples. This file must implement the spec.ExpModelCommandSpec interface, which provides the CLI with the target's name and usage information.
package myservice
import "github.com/chaosblade-io/chaosblade-spec-go/spec"
type CommandModelSpec struct {
spec.BaseExpModelCommandSpec
}
func NewCommandModelSpec() spec.ExpModelCommandSpec {
return &CommandModelSpec{
spec.BaseExpModelCommandSpec{
ExpActions: []spec.ExpActionCommandSpec{},
ExpFlags: []spec.ExpFlagSpec{
&spec.ExpFlag{
Name: "endpoint",
Desc: "myservice endpoint (host:port)",
},
},
},
}
}
func (*CommandModelSpec) Name() string { return "myservice" }
func (*CommandModelSpec) ShortDesc() string { return "MyService experiment" }
func (*CommandModelSpec) LongDesc() string { return "Chaos experiments for MyService" }
func (*CommandModelSpec) Example() string {
return "blade create myservice latency --delay 1000 --endpoint 127.0.0.1:8080"
}
The Example() method returns the command string users will run, while ExpFlags defines common flags available to all actions under this target.
Step 2: Implement the Executor Interface
Create exec/<target>/executor.go to handle the actual chaos injection and cleanup. The executor must satisfy the spec.Executor interface defined in the ChaosBlade specification, which requires Create, Destroy, and optional Query methods.
package myservice
import (
"context"
"github.com/chaosblade-io/chaosblade-spec-go/spec"
"github.com/chaosblade-io/chaosblade-spec-go/log"
)
type Executor struct{}
// NewExecutor returns an executor that implements spec.Executor.
func NewExecutor() spec.Executor { return &Executor{} }
func (e *Executor) Name() string { return "myservice" }
func (e *Executor) SetChannel(c spec.Channel) {}
func (e *Executor) Create(ctx context.Context, model *spec.ExpModel) *spec.Response {
// Parse flags from the experiment model
delay := model.ActionFlags["delay"]
endpoint := model.ActionFlags["endpoint"]
// Insert real injection logic here (e.g., call myservice API)
log.Infof(ctx, "Injecting latency %s ms to %s", delay, endpoint)
return spec.ReturnSuccess("")
}
func (e *Executor) Destroy(ctx context.Context, model *spec.ExpModel) *spec.Response {
// Cleanup logic to remove the chaos injection
log.Infof(ctx, "Removing latency injection from %s", model.ActionFlags["endpoint"])
return spec.ReturnSuccess("")
}
func (e *Executor) Query(ctx context.Context, model *spec.ExpModel) *spec.Response {
// Optional: return current status of the experiment
return spec.ReturnSuccess("")
}
The Create method receives an ExpModel containing parsed flags from the CLI and returns a spec.Response indicating success or failure.
Step 3: Register the Target in the CLI
In cli/cmd/exp.go, register the executor so the blade create command can resolve it. The registration uses createExecutorKey(target, actionTarget, action) to build a unique lookup key for the executors map.
Add the following initialization code where target specs are loaded:
myserviceSpec := myservice.NewCommandModelSpec()
myserviceExecutor := myservice.NewExecutor()
// Register the target command
ec.registerExpCommand(myserviceSpec, "")
// Map the specific action to the executor
ec.executors[createExecutorKey("myservice", "", "latency")] = myserviceExecutor
If your target implements dynamic spec loading, this registration happens automatically when the action command is created (see lines 65‑70 in cli/cmd/exp.go). The createExecutorKey function concatenates the target name, action target, and action to generate the map key used by GetExecutor.
Step 4: Create YAML Scenario Specifications
Write a YAML file describing your target's actions, flags, and matchers. The blade CLI reads these definitions from chaosblade.spec.yaml, which is generated by merging all scenario files.
Create scenario/myservice.yaml:
name: myservice
shortDesc: MyService experiment
longDesc: |
Chaos experiments for MyService, such as injecting latency,
CPU overload, or killing processes.
actions:
- name: latency
shortDesc: Add network latency
longDesc: |
Injects a fixed latency (ms) to the communication channel of the target
MyService instance.
flags:
- name: delay
desc: "latency in milliseconds"
required: true
- name: endpoint
desc: "myservice endpoint (host:port)"
required: true
matchers: [] # optional, for complex targeting logic
Place this file in a scenario directory that will be processed by the spec builder tool.
Step 5: Build and Test the Custom Target
Rebuild the aggregated specification to include your new target's YAML definitions. Run the spec merger tool from the repository root:
go run ./build/spec <scenario-dir> <target-dir>
For example, if your YAML is in ./scenario and you want the output in the current directory:
go run ./build/spec scenario ./
This executes the merging logic in build/spec/spec.go, which writes the combined chaosblade.spec.yaml file containing all registered targets. The blade CLI reads this file at startup to populate available commands.
Test your implementation by creating an experiment:
blade create myservice latency --delay 2000 --endpoint 127.0.0.1:8080 --duration 30s
The CLI parses the flags, builds an ExpModel via createExpModel in exp.go, looks up your executor using the generated key, and invokes the Create method with the experiment context.
Summary
- Define the spec: Implement
spec.ExpModelCommandSpecinexec/<target>/spec.goto declare target metadata and default flags. - Implement the executor: Create
exec/<target>/executor.gosatisfying thespec.Executorinterface withCreate,Destroy, andQuerymethods. - Register with CLI: Modify
cli/cmd/exp.goto register the spec and add the executor to theexecutorsmap usingcreateExecutorKey. - Provide YAML specs: Define actions and flags in YAML files processed by
build/spec/spec.goto generatechaosblade.spec.yaml. - Rebuild specs: Run
go run ./build/specto regenerate the master specification when adding or modifying YAML scenarios.
Frequently Asked Questions
What interface must a custom target executor implement?
Your executor must implement the spec.Executor interface from chaosblade-spec-go. This requires three methods: Create(ctx context.Context, model *spec.ExpModel) *spec.Response for injection logic, Destroy for cleanup and rollback, and Query for status checking. Each method receives the experiment model containing parsed CLI flags and must return a response indicating success or failure.
How does ChaosBlade map CLI commands to executors?
The GetExecutor function in cli/cmd/exp.go builds a lookup key using createExecutorKey(target, actionTarget, action), which concatenates the target name, action target, and action into a unique string. This key retrieves the appropriate executor from the ec.executors map. When you run blade create myservice latency, the CLI generates the key for the myservice target and latency action, then invokes the matching executor's Create method.
Where should YAML scenario files be placed for custom targets?
Place YAML files in any directory of your choice, then pass this directory as the first argument to the spec builder: go run ./build/spec <scenario-dir> <output-dir>. The tool merges all YAML files in the scenario directory into a single chaosblade.spec.yaml file that the blade CLI reads at runtime to discover available actions and their flags.
Is rebuilding the spec required for every code change?
No. Rebuilding the specification via build/spec/spec.go is only necessary when adding new YAML scenario files or modifying action definitions. Changes to Go source code in your executor or spec files require recompiling the blade binary itself, as the CLI loads executor implementations directly from compiled code, while the YAML specs provide metadata for CLI argument parsing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →