Add a New Chaos Experiment Type

This document describes how to add a new chaos experiment type.

The following walks you through an example of HelloWorldChaos, a new chaos experiment type that prints Hello world! to the log. The steps include:

Step 1: Define the schema of HelloWorldChaos

  1. Add helloworldchaos_types.go to the api/v1alpha1 API directory with the following content:

    1. package v1alpha1
    2. import (
    3. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    4. )
    5. // +kubebuilder:object:root=true
    6. // +chaos-mesh:experiment
    7. // +chaos-mesh:oneshot=true
    8. // HelloWorldChaos is the Schema for the helloworldchaos API
    9. type HelloWorldChaos struct {
    10. metav1.TypeMeta `json:",inline"`
    11. metav1.ObjectMeta `json:"metadata,omitempty"`
    12. Spec HelloWorldChaosSpec `json:"spec"`
    13. Status HelloWorldChaosStatus `json:"status,omitempty"`
    14. }
    15. // HelloWorldChaosSpec defines the desired state of HelloWorldChaos
    16. type HelloWorldChaosSpec struct {
    17. // ContainerSelector specifies the target for injection
    18. ContainerSelector `json:",inline"`
    19. // Duration represents the duration of the chaos
    20. // +optional
    21. Duration *string `json:"duration,omitempty"`
    22. // RemoteCluster represents the remote cluster where the chaos will be deployed
    23. // +optional
    24. RemoteCluster string `json:"remoteCluster,omitempty"`
    25. }
    26. // HelloWorldChaosStatus defines the observed state of HelloWorldChaos
    27. type HelloWorldChaosStatus struct {
    28. ChaosStatus `json:",inline"`
    29. }
    30. // GetSelectorSpecs is a getter for selectors
    31. func (obj *HelloWorldChaos) GetSelectorSpecs() map[string]interface{} {
    32. return map[string]interface{}{
    33. ".": &obj.Spec.ContainerSelector,
    34. }
    35. }

    This file defines the schema type of HelloWorldChaos, which can be described in a YAML file:

    1. apiVersion: chaos-mesh.org/v1alpha1
    2. kind: HelloWorldChaos
    3. metadata:
    4. name: <resource name>
    5. namespace: <namespace>
    6. spec:
    7. duration: <duration>
    8. #...

Step 2: Register the CRD

You need to register the CRD (Custom Resource Definition) of HelloWorldChaos to interact it with Kubernetes API.

  1. To combine the CRD into manifests/crd.yaml, append config/crd/bases/chaos-mesh.org_helloworldchaos.yaml we generated in the previous step to config/crd/kustomization.yaml:

    1. resources:
    2. - bases/chaos-mesh.org_podchaos.yaml
    3. - bases/chaos-mesh.org_networkchaos.yaml
    4. - bases/chaos-mesh.org_iochaos.yaml
    5. - bases/chaos-mesh.org_helloworldchaos.yaml # This is the new line
  2. Run make generate in the root directory of Chaos Mesh, which generates a boilerplate of HelloWorldChaos for Chaos Mesh to compile:

    1. make generate

    Then you can see the definition of HelloWorldChaos in manifests/crd.yaml.

Step 3: Register the event handler for helloworldchaos objects

  1. Create a new file controllers/chaosimpl/helloworldchaos/types.go with the following content:

    1. package helloworldchaos
    2. import (
    3. "context"
    4. "github.com/go-logr/logr"
    5. "go.uber.org/fx"
    6. "sigs.k8s.io/controller-runtime/pkg/client"
    7. "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
    8. impltypes "github.com/chaos-mesh/chaos-mesh/controllers/chaosimpl/types"
    9. "github.com/chaos-mesh/chaos-mesh/controllers/chaosimpl/utils"
    10. )
    11. var _ impltypes.ChaosImpl = (*Impl)(nil)
    12. type Impl struct {
    13. client.Client
    14. Log logr.Logger
    15. decoder *utils.ContainerRecordDecoder
    16. }
    17. // This corresponds to the Apply phase of HelloWorldChaos. The execution of HelloWorldChaos will be triggered.
    18. func (impl *Impl) Apply(ctx context.Context, index int, records []*v1alpha1.Record, obj v1alpha1.InnerObject) (v1alpha1.Phase, error) {
    19. impl.Log.Info("Hello world!")
    20. return v1alpha1.Injected, nil
    21. }
    22. // This corresponds to the Recover phase of HelloWorldChaos. The reconciler will be triggered to recover the chaos action.
    23. func (impl *Impl) Recover(ctx context.Context, index int, records []*v1alpha1.Record, obj v1alpha1.InnerObject) (v1alpha1.Phase, error) {
    24. impl.Log.Info("Goodbye world!")
    25. return v1alpha1.NotInjected, nil
    26. }
    27. // NewImpl returns a new HelloWorldChaos implementation instance.
    28. func NewImpl(c client.Client, log logr.Logger, decoder *utils.ContainerRecordDecoder) *impltypes.ChaosImplPair {
    29. return &impltypes.ChaosImplPair{
    30. Name: "helloworldchaos",
    31. Object: &v1alpha1.HelloWorldChaos{},
    32. Impl: &Impl{
    33. Client: c,
    34. Log: log.WithName("helloworldchaos"),
    35. decoder: decoder,
    36. },
    37. ObjectList: &v1alpha1.HelloWorldChaosList{},
    38. }
    39. }
    40. var Module = fx.Provide(
    41. fx.Annotated{
    42. Group: "impl",
    43. Target: NewImpl,
    44. },
    45. )
  2. Chaos Mesh uses the fx library for dependency injection. To register HelloWorldChaos in the controller manager, add a line to controllers/chaosimpl/fx.go:

    1. var AllImpl = fx.Options(
    2. gcpchaos.Module,
    3. stresschaos.Module,
    4. jvmchaos.Module,
    5. timechaos.Module,
    6. helloworldchaos.Module // Add a new line. Make sure you have imported helloworldchaos first.
    7. //...
    8. )

    Then in controllers/types/types.go, append the following content into ChaosObjects:

    1. var ChaosObjects = fx.Supply(
    2. //...
    3. fx.Annotated{
    4. Group: "objs",
    5. Target: Object{
    6. Name: "helloworldchaos",
    7. Object: &v1alpha1.HelloWorldChaos{},
    8. },
    9. },
    10. )

Step 4: Build Docker images

  1. Build the production images:

    1. make image
  2. If you deploy the Kubernetes cluster using minikube, then you need to load images into the cluster:

    1. minikube image load ghcr.io/chaos-mesh/chaos-dashboard:latest
    2. minikube image load ghcr.io/chaos-mesh/chaos-mesh:latest
    3. minikube image load ghcr.io/chaos-mesh/chaos-daemon:latest

Step 5: Run HelloWorldChaos

In this step, you need to deploy Chaos Mesh with your latest changes to test HelloWorldChaos.

  1. Register the CRD in your cluster:

    1. kubectl create -f manifests/crd.yaml

    You can see HelloWorldChaos is created from the output:

    1. customresourcedefinition.apiextensions.k8s.io/helloworldchaos.chaos-mesh.org created

    Now you can get the CRD of HelloWorldChaos using the command below:

    1. kubectl get crd helloworldchaos.chaos-mesh.org
  2. Deploy Chaos Mesh:

    1. helm install chaos-mesh helm/chaos-mesh -n=chaos-mesh --set controllerManager.leaderElection.enabled=false,dashboard.securityMode=false

    To verify the deployment is successful, you can check all Pods in the chaos-mesh namespace:

    1. kubectl get pods --namespace chaos-mesh -l app.kubernetes.io/instance=chaos-mesh
  3. Deploy a deployment for testing, we can use an example echo server from minikube docs:

    1. kubectl create deployment hello-minikube --image=kicbase/echo-server:1.0

    Wait to see the pod is running:

    1. kubectl get pods

    Example output:

    1. NAME READY STATUS RESTARTS AGE
    2. hello-minikube-77b6f68484-dg4sw 1/1 Running 0 2m
  4. Create a hello.yaml file with the following content:

    1. apiVersion: chaos-mesh.org/v1alpha1
    2. kind: HelloWorldChaos
    3. metadata:
    4. name: hello-world
    5. namespace: chaos-mesh
    6. spec:
    7. selector:
    8. labelSelectors:
    9. app: hello-minikube
    10. mode: one
    11. duration: 1h
  5. Run:

    1. kubectl apply -f hello.yaml
    2. # helloworldchaos.chaos-mesh.org/hello-world created

    Now you can check if chaos-controller-manager has Hello world! in its logs:

    1. kubectl logs -n chaos-mesh chaos-controller-manager-xxx

    Example output:

    1. 2023-07-16T06:19:40.068Z INFO records records/controller.go:149 apply chaos {"id": "default/hello-minikube-77b6f68484-dg4sw/echo-server"}
    2. 2023-07-16T06:19:40.068Z INFO helloworldchaos helloworldchaos/types.go:26 Hello world!

What’s Next

If you encounter any problems during the process, create an issue in the Chaos Mesh repository.

In the next section, we’ll learn more about how to extend the behavior of HelloWorldChaos.