Skip to content

Commit f402bf8

Browse files
chore(sdk): Adding support for helper based chaoslib (litmuschaos#546)
* chore(sdk): Adding support for helper based chaoslib Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io> * chore(sdk): Adding support for helper based chaoslib Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io> Signed-off-by: ispeakc0de <ashubham314@gmail.com> * Update contribute/developer-guide/README.md Co-authored-by: Udit Gaurav <35391335+uditgaurav@users.noreply.github.com> Co-authored-by: Udit Gaurav <35391335+uditgaurav@users.noreply.github.com>
1 parent 535c1e7 commit f402bf8

12 files changed

+251
-10
lines changed

contribute/developer-guide/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ The *generate_experiment.go* script is a simple way to bootstrap your experiment
110110

111111
**Note**: Replace the `<generate-type>` placeholder with the appropriate value based on the usecase:
112112
- `experiment`: Chaos experiment artifacts belonging to an existing OR new experiment.
113+
- Provide the type of chaoslib in the `-t` flag. It supports the following values:
114+
- `exec`: It creates the exec based chaoslib(default type)
115+
- `helper`: It creates the helper based chaoslib
113116
- `chart`: Just the chaos-chart metadata, i.e., chartserviceversion.yaml
114117
- Provide the type of chart in the `-t` flag. It supports the following values:
115118
- `category`: It creates the chart metadata for the category i.e chartserviceversion, package manifests

contribute/developer-guide/bin/main.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ func main() {
1313
var (
1414
filePath string
1515
chartType string
16+
libType string
1617
)
1718

1819
var generate = &cobra.Command{
@@ -30,7 +31,7 @@ func main() {
3031
Example: "./litmus-sdk generate experiment -f=attribute.yaml",
3132
DisableFlagsInUseLine: true,
3233
Run: func(cmd *cobra.Command, args []string) {
33-
if err := sdkCmd.GenerateExperiment(filePath, chartType, "experiment"); err != nil {
34+
if err := sdkCmd.GenerateExperiment(filePath, chartType, "experiment", libType); err != nil {
3435
log.Fatalf("error: %v", err)
3536
}
3637
fmt.Println("experiment created successfully")
@@ -45,14 +46,15 @@ func main() {
4546
Example: "./litmus-sdk generate chart -f=attribute.yaml",
4647
DisableFlagsInUseLine: true,
4748
Run: func(cmd *cobra.Command, args []string) {
48-
if err := sdkCmd.GenerateExperiment(filePath, chartType, "chart"); err != nil {
49+
if err := sdkCmd.GenerateExperiment(filePath, chartType, "chart", libType); err != nil {
4950
log.Fatalf("error: %v", err)
5051
}
5152
fmt.Println("chart created successfully")
5253
},
5354
}
5455

5556
experiment.Flags().StringVarP(&filePath, "file", "f", "", "path of the attribute.yaml manifest")
57+
experiment.Flags().StringVarP(&libType, "type", "t", "exec", "type of the experiment lib")
5658
chart.Flags().StringVarP(&filePath, "file", "f", "", "path of the attribute.yaml manifest")
5759
chart.Flags().StringVarP(&chartType, "type", "t", "all", "type of the chaos chart")
5860
chart.MarkFlagRequired("file")

contribute/developer-guide/generate_experiment.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"bytes"
5+
"fmt"
56
"io/ioutil"
67
"os"
78
"os/exec"
@@ -17,7 +18,7 @@ import (
1718
)
1819

1920
// GenerateExperiment generate the new/custom chaos experiment based on specified attribute file
20-
func GenerateExperiment(attributeFile, chartType string, generationType string) error {
21+
func GenerateExperiment(attributeFile, chartType, generationType, libType string) error {
2122

2223
// Fetch all the required attributes from the given file
2324
// Experiment contains all the required attributes
@@ -70,12 +71,12 @@ func GenerateExperiment(attributeFile, chartType string, generationType string)
7071
}
7172

7273
// creating chaoslib dir & files
73-
if err := createChaosLib(litmusRootDir, experimentDetails); err != nil {
74+
if err := createChaosLib(litmusRootDir, experimentDetails, libType); err != nil {
7475
return err
7576
}
7677

7778
// creating envs dir & files
78-
if err := createENV(litmusRootDir, experimentDetails); err != nil {
79+
if err := createENV(litmusRootDir, experimentDetails, libType); err != nil {
7980
return err
8081
}
8182

@@ -158,7 +159,7 @@ func createExperiment(experimentRootDIR string, experimentDetails types.Experime
158159
}
159160

160161
// createChaosLib creates the chaoslib for the experiment
161-
func createChaosLib(litmusRootDir string, experimentDetails types.Experiment) error {
162+
func createChaosLib(litmusRootDir string, experimentDetails types.Experiment, libType string) error {
162163
// create the chaoslib directory, if not present
163164
chaoslibRootDIR := litmusRootDir + "/chaoslib/litmus/" + experimentDetails.Name
164165
createDirectoryIfNotPresent(chaoslibRootDIR)
@@ -167,11 +168,11 @@ func createChaosLib(litmusRootDir string, experimentDetails types.Experiment) er
167168

168169
// generating the chaoslib file
169170
chaoslibFilePath := chaoslibDIR + "/" + experimentDetails.Name + ".go"
170-
return generateFile(experimentDetails, chaoslibFilePath, "./templates/chaoslib.tmpl")
171+
return generateFile(experimentDetails, chaoslibFilePath, fmt.Sprintf("./templates/chaoslib_%s.tmpl", libType))
171172
}
172173

173174
// createENV creates the env getter and setter files
174-
func createENV(litmusRootDir string, experimentDetails types.Experiment) error {
175+
func createENV(litmusRootDir string, experimentDetails types.Experiment, libType string) error {
175176
// creating the directory for the environment variables file, if not present
176177
experimentPKGDirectory := litmusRootDir + "/pkg/" + experimentDetails.Category
177178
createDirectoryIfNotPresent(experimentPKGDirectory)
@@ -188,13 +189,13 @@ func createENV(litmusRootDir string, experimentDetails types.Experiment) error {
188189

189190
// generating the environment var file
190191
environmentFilePath := environmentDIR + "/" + "environment.go"
191-
if err := generateFile(experimentDetails, environmentFilePath, "./templates/environment.tmpl"); err != nil {
192+
if err := generateFile(experimentDetails, environmentFilePath, fmt.Sprintf("./templates/environment_%s.tmpl", libType)); err != nil {
192193
return err
193194
}
194195

195196
// generating the types.go file
196197
typesFilePath := typesDIR + "/" + "types.go"
197-
if err := generateFile(experimentDetails, typesFilePath, "./templates/types.tmpl"); err != nil {
198+
if err := generateFile(experimentDetails, typesFilePath, fmt.Sprintf("./templates/types_%s.tmpl", libType)); err != nil {
198199
return err
199200
}
200201
return nil

contribute/developer-guide/litmus-sdk

-307 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.

contribute/developer-guide/templates/chaoslib.tmpl renamed to contribute/developer-guide/templates/chaoslib_exec.tmpl

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func runChaos(experimentsDetails *experimentTypes.ExperimentDetails, targetPodLi
105105
return nil
106106
}
107107

108+
//PrepareChaos contains the preparation steps before chaos injection
108109
func PrepareChaos(experimentsDetails *experimentTypes.ExperimentDetails, clients clients.ClientSets, resultDetails *types.ResultDetails, eventsDetails *types.EventDetails, chaosDetails *types.ChaosDetails) error {
109110

110111
//Waiting for the ramp time before chaos injection
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package lib
2+
3+
import (
4+
"context"
5+
clients "github.com/litmuschaos/litmus-go/pkg/clients"
6+
"github.com/litmuschaos/litmus-go/pkg/events"
7+
"github.com/litmuschaos/litmus-go/pkg/log"
8+
"github.com/litmuschaos/litmus-go/pkg/probe"
9+
experimentTypes "github.com/litmuschaos/litmus-go/pkg/{{ .Category }}/{{ .Name }}/types"
10+
"github.com/litmuschaos/litmus-go/pkg/status"
11+
"github.com/litmuschaos/litmus-go/pkg/types"
12+
"github.com/litmuschaos/litmus-go/pkg/utils/common"
13+
"github.com/pkg/errors"
14+
"github.com/sirupsen/logrus"
15+
corev1 "k8s.io/api/core/v1"
16+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17+
)
18+
19+
func experimentExecution(experimentsDetails *experimentTypes.ExperimentDetails, clients clients.ClientSets, resultDetails *types.ResultDetails, eventsDetails *types.EventDetails, chaosDetails *types.ChaosDetails) error {
20+
21+
// Get the target pod details for the chaos execution
22+
// if the target pod is not defined it will derive the random target pod list using pod affected percentage
23+
targetPodList, err := common.GetPodList(experimentsDetails.TargetPods, experimentsDetails.PodsAffectedPerc, clients, chaosDetails)
24+
if err != nil {
25+
return err
26+
}
27+
28+
podNames := []string{}
29+
for _, pod := range targetPodList.Items {
30+
podNames = append(podNames, pod.Name)
31+
}
32+
log.Infof("Target pods list for chaos, %v", podNames)
33+
34+
//Get the target container name of the application pod
35+
if experimentsDetails.TargetContainer == "" {
36+
experimentsDetails.TargetContainer, err = common.GetTargetContainer(experimentsDetails.AppNS, targetPodList.Items[0].Name, clients)
37+
if err != nil {
38+
return errors.Errorf("unable to get the target container name, err: %v", err)
39+
}
40+
}
41+
42+
if experimentsDetails.EngineName != "" {
43+
if err := common.SetHelperData(chaosDetails, experimentsDetails.SetHelperData, clients); err != nil {
44+
return err
45+
}
46+
}
47+
48+
return runChaos(experimentsDetails, targetPodList, clients, resultDetails, eventsDetails, chaosDetails)
49+
}
50+
51+
func runChaos(experimentsDetails *experimentTypes.ExperimentDetails, targetPodList corev1.PodList, clients clients.ClientSets, resultDetails *types.ResultDetails, eventsDetails *types.EventDetails, chaosDetails *types.ChaosDetails) error {
52+
if experimentsDetails.EngineName != "" {
53+
msg := "Injecting " + experimentsDetails.ExperimentName + " chaos on target pod"
54+
types.SetEngineEventAttributes(eventsDetails, types.ChaosInject, msg, "Normal", chaosDetails)
55+
events.GenerateEvents(eventsDetails, clients, chaosDetails, "ChaosEngine")
56+
}
57+
58+
labelSuffix := common.GetRunID()
59+
60+
// run the probes during chaos
61+
if len(resultDetails.ProbeDetails) != 0 {
62+
if err := probe.RunProbes(chaosDetails, clients, resultDetails, "DuringChaos", eventsDetails); err != nil {
63+
return err
64+
}
65+
}
66+
67+
// creating the helper pod to perform container kill chaos
68+
for _, pod := range targetPodList.Items {
69+
70+
runID := common.GetRunID()
71+
72+
log.InfoWithValues("[Info]: Details of application under chaos injection", logrus.Fields{
73+
"Target Pod": pod.Name,
74+
"NodeName": pod.Spec.NodeName,
75+
"Target Container": experimentsDetails.TargetContainer,
76+
})
77+
78+
if err := createHelperPod(experimentsDetails, clients, chaosDetails, pod.Name, pod.Spec.NodeName, runID, labelSuffix); err != nil {
79+
return errors.Errorf("unable to create the helper pod, err: %v", err)
80+
}
81+
82+
common.SetTargets(pod.Name, "targeted", "pod", chaosDetails)
83+
84+
appLabel := "name=" + experimentsDetails.ExperimentName + "-helper-" + runID
85+
86+
//checking the status of the helper pod, wait till the pod comes to running state else fail the experiment
87+
log.Info("[Status]: Checking the status of the helper pod")
88+
if err := status.CheckHelperStatus(experimentsDetails.ChaosNamespace, appLabel, experimentsDetails.Timeout, experimentsDetails.Delay, clients); err != nil {
89+
common.DeleteHelperPodBasedOnJobCleanupPolicy(experimentsDetails.ExperimentName+"-helper-"+runID, appLabel, chaosDetails, clients)
90+
return errors.Errorf("helper pod is not in running state, err: %v", err)
91+
}
92+
93+
log.Infof("[Wait]: Waiting for the %vs chaos duration", experimentsDetails.ChaosDuration)
94+
common.WaitForDuration(experimentsDetails.ChaosDuration)
95+
96+
//Deleting the helper pod
97+
log.Info("[Cleanup]: Deleting the helper pod")
98+
if err := common.DeletePod(experimentsDetails.ExperimentName+"-helper-"+runID, appLabel, experimentsDetails.ChaosNamespace, chaosDetails.Timeout, chaosDetails.Delay, clients); err != nil {
99+
return errors.Errorf("unable to delete the helper pod, err: %v", err)
100+
}
101+
}
102+
103+
return nil
104+
}
105+
106+
//PrepareChaos contains the preparation steps before chaos injection
107+
func PrepareChaos(experimentsDetails *experimentTypes.ExperimentDetails, clients clients.ClientSets, resultDetails *types.ResultDetails, eventsDetails *types.EventDetails, chaosDetails *types.ChaosDetails) error {
108+
109+
//Waiting for the ramp time before chaos injection
110+
if experimentsDetails.RampTime != 0 {
111+
log.Infof("[Ramp]: Waiting for the %vs ramp time before injecting chaos", experimentsDetails.RampTime)
112+
common.WaitForDuration(experimentsDetails.RampTime)
113+
}
114+
//Starting the CPU stress experiment
115+
if err := experimentExecution(experimentsDetails, clients, resultDetails, eventsDetails, chaosDetails);err != nil {
116+
return err
117+
}
118+
//Waiting for the ramp time after chaos injection
119+
if experimentsDetails.RampTime != 0 {
120+
log.Infof("[Ramp]: Waiting for the %vs ramp time after injecting chaos", experimentsDetails.RampTime)
121+
common.WaitForDuration(experimentsDetails.RampTime)
122+
}
123+
return nil
124+
}
125+
126+
// createHelperPod derive the attributes for helper pod and create the helper pod
127+
func createHelperPod(experimentsDetails *experimentTypes.ExperimentDetails, clients clients.ClientSets, chaosDetails *types.ChaosDetails, appName, appNodeName, runID, labelSuffix string) error {
128+
129+
helperPod := &corev1.Pod{
130+
ObjectMeta: v1.ObjectMeta{
131+
Name: experimentsDetails.ExperimentName + "-helper-" + runID,
132+
Namespace: experimentsDetails.ChaosNamespace,
133+
Labels: common.GetHelperLabels(chaosDetails.Labels, runID, labelSuffix, experimentsDetails.ExperimentName),
134+
Annotations: chaosDetails.Annotations,
135+
},
136+
Spec: corev1.PodSpec{
137+
RestartPolicy: corev1.RestartPolicyNever,
138+
ImagePullSecrets: chaosDetails.ImagePullSecrets,
139+
NodeName: appNodeName,
140+
Containers: []corev1.Container{
141+
{
142+
Name: experimentsDetails.ExperimentName,
143+
Image: experimentsDetails.LIBImage,
144+
ImagePullPolicy: corev1.PullPolicy(experimentsDetails.LIBImagePullPolicy),
145+
Command: []string{
146+
"/bin/bash",
147+
"-c",
148+
},
149+
Args: []string{
150+
"echo This is a sample pod",
151+
"sleep 10",
152+
},
153+
Resources: chaosDetails.Resources,
154+
},
155+
},
156+
},
157+
}
158+
159+
_, err := clients.KubeClient.CoreV1().Pods(experimentsDetails.ChaosNamespace).Create(context.Background(), helperPod, v1.CreateOptions{})
160+
return err
161+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package environment
2+
3+
import (
4+
"strconv"
5+
6+
clientTypes "k8s.io/apimachinery/pkg/types"
7+
8+
experimentTypes "github.com/litmuschaos/litmus-go/pkg/{{ .Category }}/{{ .Name }}/types"
9+
"github.com/litmuschaos/litmus-go/pkg/types"
10+
)
11+
12+
// STEPS TO GETENV OF YOUR CHOICE HERE
13+
// ADDED FOR FEW MANDATORY FIELD
14+
15+
//GetENV fetches all the env variables from the runner pod
16+
func GetENV(experimentDetails *experimentTypes.ExperimentDetails) {
17+
experimentDetails.ExperimentName = types.Getenv("EXPERIMENT_NAME", "")
18+
experimentDetails.ChaosNamespace = types.Getenv("CHAOS_NAMESPACE", "litmus")
19+
experimentDetails.EngineName = types.Getenv("CHAOSENGINE", "")
20+
experimentDetails.ChaosDuration, _ = strconv.Atoi(types.Getenv("TOTAL_CHAOS_DURATION", "30"))
21+
experimentDetails.ChaosInterval, _ = strconv.Atoi(types.Getenv("CHAOS_INTERVAL", "10"))
22+
experimentDetails.RampTime, _ = strconv.Atoi(types.Getenv("RAMP_TIME", "0"))
23+
experimentDetails.ChaosLib = types.Getenv("LIB", "litmus")
24+
experimentDetails.AppNS = types.Getenv("APP_NAMESPACE", "")
25+
experimentDetails.AppLabel = types.Getenv("APP_LABEL", "")
26+
experimentDetails.AppKind = types.Getenv("APP_KIND", "")
27+
experimentDetails.AuxiliaryAppInfo = types.Getenv("AUXILIARY_APPINFO", "")
28+
experimentDetails.ChaosUID = clientTypes.UID(types.Getenv("CHAOS_UID", ""))
29+
experimentDetails.InstanceID = types.Getenv("INSTANCE_ID", "")
30+
experimentDetails.ChaosPodName = types.Getenv("POD_NAME", "")
31+
experimentDetails.Delay, _ = strconv.Atoi(types.Getenv("STATUS_CHECK_DELAY", "2"))
32+
experimentDetails.Timeout, _ = strconv.Atoi(types.Getenv("STATUS_CHECK_TIMEOUT", "180"))
33+
experimentDetails.TargetContainer = types.Getenv("TARGET_CONTAINER", "")
34+
experimentDetails.TargetPods = types.Getenv("TARGET_PODS", "")
35+
experimentDetails.PodsAffectedPerc, _ = strconv.Atoi(types.Getenv("PODS_AFFECTED_PERC", "0"))
36+
experimentDetails.LIBImagePullPolicy = types.Getenv("LIB_IMAGE_PULL_POLICY", "Always")
37+
experimentDetails.LIBImage = types.Getenv("LIB_IMAGE", "litmuschaos/go-runner:latest")
38+
experimentDetails.SetHelperData = types.Getenv("SET_HELPER_DATA", "true")
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package types
2+
3+
import (
4+
clientTypes "k8s.io/apimachinery/pkg/types"
5+
)
6+
7+
// ADD THE ATTRIBUTES OF YOUR CHOICE HERE
8+
// FEW MENDATORY ATTRIBUTES ARE ADDED BY DEFAULT
9+
10+
// ExperimentDetails is for collecting all the experiment-related details
11+
type ExperimentDetails struct {
12+
ExperimentName string
13+
EngineName string
14+
ChaosDuration int
15+
ChaosInterval int
16+
RampTime int
17+
ChaosLib string
18+
AppNS string
19+
AppLabel string
20+
AppKind string
21+
AuxiliaryAppInfo string
22+
ChaosUID clientTypes.UID
23+
InstanceID string
24+
ChaosNamespace string
25+
ChaosPodName string
26+
Timeout int
27+
Delay int
28+
TargetContainer string
29+
PodsAffectedPerc int
30+
TargetPods string
31+
LIBImagePullPolicy string
32+
LIBImage string
33+
SetHelperData string
34+
}

0 commit comments

Comments
 (0)