Add 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 type
  • Step 2: Register the CRD
  • Step 3: Register the event handler for this chaos object
  • Step 4: Build the Docker image
  • Step 5: Run the chaos experiment

Step 1: Define the schema type

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

    ``` package v1alpha1

  1. import (
  2. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  3. )
  4. // +kubebuilder:object:root=true
  5. // +chaos-mesh:base
  6. // +chaos-mesh:oneshot=true
  7. // HelloWorldChaos is the Schema for the helloworldchaos API
  8. type HelloWorldChaos struct {
  9. metav1.TypeMeta `json:",inline"`
  10. metav1.ObjectMeta `json:"metadata,omitempty"`
  11. Spec HelloWorldChaosSpec `json:"spec"`
  12. Status HelloWorldChaosStatus `json:"status,omitempty"`
  13. }
  14. // HelloWorldChaosSpec is the content of the specification for a HelloWorldChaos
  15. type HelloWorldChaosSpec struct {
  16. // ContainerSelector specifies target
  17. ContainerSelector `json:",inline"`
  18. // Duration represents the duration of the chaos action
  19. // +optional
  20. Duration *string `json:"duration,omitempty"`
  21. }
  22. // HelloWorldChaosStatus represents the status of a HelloWorldChaos
  23. type HelloWorldChaosStatus struct {
  24. ChaosStatus `json:",inline"`
  25. }
  26. // GetSelectorSpecs is a getter for selectors
  27. func (obj *HelloWorldChaos) GetSelectorSpecs() map[string]interface{} {
  28. return map[string]interface{}{
  29. ".": &obj.Spec.ContainerSelector,
  30. }
  31. }
  32. ```
  33. This file defines the schema type of HelloWorldChaos, which can be described in a YAML file:
  34. ```
  35. apiVersion: chaos-mesh. rg/v1alpha1
  36. kind: HelloWorldChaos
  37. metadata:
  38. name: <resource name>
  39. name: <namespace>
  40. spec:
  41. duration: <duration>
  42. status:
  43. experiment: <experimental status>
  44. ...
  45. ```
  1. Run make generate in the root directory of Chaos Mesh, which generates a boilerplate for Chaos Mesh to compile.

Step 2: Register the CRD

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

  1. Run make yaml in the root directory. The generated YAML file is at config/crd/bases/chaos-mesh.org_helloworldchaos.yaml.

  2. To combine this YAML file into manifests/crd.yaml, add a new line in 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
  3. Run make yaml again. You can see the definition of HelloWorldChaos in manifests/crd.yaml. To confirm, you can use the git diff command.

Step 3: Register the event handler for this chaos object

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

    ``` package helloworldchaos

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

    1. ...
    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 to this file first.

    In controllers/types/types.go, add the following content:

    ```

    1. ...
    2. fx.Annotated{
    3. Group: "objs",
    4. Target: Object{
    5. Name: "timechaos",
    6. Object: &v1alpha1.TimeChaos{},
    7. },
    8. },
  1. fx.Annotated{
  2. Group: "objs",
  3. Target: Object{
  4. Name: "gcpchaos",
  5. Object: &v1alpha1.GCPChaos{},
  6. },
  7. },
  8. fx.Annotated{
  9. Group: "objs",
  10. Target: Object{
  11. Name: "helloworldchaos",
  12. Object: &v1alpha1.HelloWorldChaos{},
  13. },
  14. },
  15. ```

Step 4: Build the Docker image

  1. Build the Docker image:

    1. make
  2. Push the image to your local Docker Registry:

    1. make docker-push
  3. If you deploy Kubernetes clusters using kind, then you need to load images into kind:

    1. kind load docker-image localhost:5000/pingcap/chaos-mesh:latest
    2. kind load docker-image localhost:5000/pingcap/chaos-daemon:latest
    3. kind load docker-image localhost:5000/pingcap/chaos-dashboard:latest

Step 5: Run the chaos experiment

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

Before you pull any image for Chaos Mesh (using helm install or helm upgrade), modify the helm template helm/chaos-mesh/values.yaml to replace the default image with what you just pushed to your local Docker registry.

The templates in Chaos Mesh use pingcap/chaos-mesh:latest as the default Registry. You need to set the path with the environment variable of DOCKER_REGISTRY (The default value is localhost:5000), as shown below:

  1. controllerManager:
  2. image: localhost:5000/pingcap/chaos-mesh:latest
  3. ...
  4. chaosDaemon:
  5. image: localhost:5000/pingcap/chaos-daemon:latest
  6. ...
  7. dashboard:
  8. image: localhost:5000/pingcap/chaos-dashboard:latest
  9. ...

After you update the template, try running 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:

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

    1. kubectl get pods --namespace chaos-testing -l app.kubernetes.io/instance=chaos-mesh
    note" class="reference-link">Add New Chaos Experiment Type - 图3note

    Arguments --set chaosDaemon.runtime=containerd --set chaosDaemon.socketPath=/run/containerd/containerd.sock are used to run NeteworkChaos on kind.

  3. Deploy the Pod for testing:

    1. kubectl apply -f https://raw.githubusercontent.com/chaos-mesh/apps/master/ping/busybox-statefulset.yaml

    Make sure the Pod for testing works properly.

  4. Create a chaos.yaml file in any location with the following content:

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

    1. kubectl apply -f /path/to/chaos.yaml
    1. kubectl get HelloWorldChaos -n chaos-testing

    Now you can see Hello World! in the logs of chaos-controller-manager:

    1. kubectl logs chaos-controller-manager-{pod-post-fix} -n chaos-testing

    Example output:

    1. 2021-06-24T06:42:26.858Z INFO records apply chaos {"id": "chaos-testing/chaos-daemon-vsmc5"}
    2. 2021-06-24T06:42:26.858Z INFO helloworldchaos Hello World!
    note" class="reference-link">Add New Chaos Experiment Type - 图4note

    {pod-post-fix} is a random string generated by Kubernetes. You can check it by executing kubectl get pod -n chaos-testing.

What’s Next

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

If you want to dive deep into developing new chaos experiment types, see Extend Chaos Daemon interface.