237 lines
8.8 KiB
Go
237 lines
8.8 KiB
Go
package pkg
|
|
|
|
import (
|
|
yamlChe "github.com/ghodss/yaml"
|
|
"github.com/litmuschaos/chaos-ci-lib/pkg/environment"
|
|
"github.com/litmuschaos/chaos-ci-lib/pkg/log"
|
|
"github.com/litmuschaos/chaos-ci-lib/pkg/types"
|
|
"github.com/litmuschaos/chaos-operator/pkg/apis/litmuschaos/v1alpha1"
|
|
"github.com/pkg/errors"
|
|
corev1 "k8s.io/api/core/v1"
|
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
"bytes"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
|
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
|
|
"k8s.io/client-go/dynamic"
|
|
"k8s.io/client-go/restmapper"
|
|
)
|
|
|
|
var err error
|
|
|
|
//CreateChaosResource creates litmus components with given inputs
|
|
func CreateChaosResource(fileData []byte, namespace string, clients environment.ClientSets) error {
|
|
|
|
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader(fileData), 100)
|
|
|
|
// for loop to install all the resouces
|
|
for {
|
|
//runtime defines conversions between generic types and structs to map query strings to struct objects.
|
|
var rawObj runtime.RawExtension
|
|
if err = decoder.Decode(&rawObj); err != nil {
|
|
// if the object is null, successfully installed all manifest
|
|
if rawObj.Raw == nil {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// NewDecodingSerializer adds YAML decoding support to a serializer that supports JSON.
|
|
obj, gvk, _ := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)
|
|
unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
unstructuredObj := &unstructured.Unstructured{Object: unstructuredMap}
|
|
|
|
// GetAPIGroupResources uses the provided discovery client to gather
|
|
// discovery information and populate a slice of APIGroupResources.
|
|
gr, err := restmapper.GetAPIGroupResources(clients.KubeClient.DiscoveryClient)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
mapper := restmapper.NewDiscoveryRESTMapper(gr)
|
|
|
|
// RESTMapping returns a struct representing the resource path and conversion interfaces a
|
|
// RESTClient should use to operate on the provided group/kind in order of versions.
|
|
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//ResourceInterface is an API interface to a specific resource under a dynamic client
|
|
var dri dynamic.ResourceInterface
|
|
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
|
|
unstructuredObj.SetNamespace(namespace)
|
|
dri = clients.DynamicClient.Resource(mapping.Resource).Namespace(unstructuredObj.GetNamespace())
|
|
} else {
|
|
dri = clients.DynamicClient.Resource(mapping.Resource)
|
|
}
|
|
|
|
// Create Chaos Resource using dynamic resource interface
|
|
if _, err := dri.Create(unstructuredObj, v1.CreateOptions{}); err != nil {
|
|
if !k8serrors.IsAlreadyExists(err) {
|
|
return err
|
|
} else {
|
|
// Updating present resource
|
|
log.Infof("[Status]: Updating %v", unstructuredObj.GetKind())
|
|
_, _ = dri.Update(unstructuredObj, v1.UpdateOptions{})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//InstallGoRbac installs and configure rbac for running go based chaos
|
|
func InstallRbac(experimentsDetails *types.ExperimentDetails, rbacNamespace string) error {
|
|
|
|
//Fetch RBAC file
|
|
err = DownloadFile("/tmp/"+experimentsDetails.ExperimentName+"-sa.yaml", experimentsDetails.RbacPath)
|
|
if err != nil {
|
|
return errors.Errorf("Fail to fetch the rbac file, due to %v", err)
|
|
}
|
|
//Modify Namespace field of the RBAC
|
|
if rbacNamespace != "" {
|
|
err = EditFile("/tmp/"+experimentsDetails.ExperimentName+"-sa.yaml", "namespace: default", "namespace: "+rbacNamespace)
|
|
if err != nil {
|
|
return errors.Errorf("Fail to Modify rbac file, due to %v", err)
|
|
}
|
|
}
|
|
log.Info("[RBAC]: Installing RABC...")
|
|
//Creating rbac
|
|
command := []string{"apply", "-f", "/tmp/" + experimentsDetails.ExperimentName + "-sa.yaml", "-n", rbacNamespace}
|
|
err := Kubectl(command...)
|
|
if err != nil {
|
|
return errors.Errorf("fail to apply rbac file, err: %v", err)
|
|
}
|
|
log.Info("[RBAC]: Rbac installed successfully !!!")
|
|
|
|
return nil
|
|
}
|
|
|
|
//InstallChaosEngine installs the given go based chaos engine
|
|
func InstallChaosEngine(experimentsDetails *types.ExperimentDetails, chaosEngine *v1alpha1.ChaosEngine, experimentENVs *ENVDetails, clients environment.ClientSets) error {
|
|
|
|
// contains all the envs
|
|
envDetails := ENVDetails{
|
|
ENV: map[string]string{},
|
|
}
|
|
|
|
//Fetch Engine file
|
|
res, err := http.Get(experimentsDetails.EnginePath)
|
|
if err != nil {
|
|
return errors.Errorf("Fail to fetch the engine file, due to %v", err)
|
|
}
|
|
|
|
// ReadAll reads from response until an error or EOF and returns the data it read.
|
|
fileInput, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
log.Errorf("Fail to read data from response: %v", err)
|
|
}
|
|
|
|
// Unmarshal decodes the fileInput into chaosEngine
|
|
err = yamlChe.Unmarshal([]byte(fileInput), &chaosEngine)
|
|
if err != nil {
|
|
log.Errorf("error when unmarshalling: %v", err)
|
|
}
|
|
|
|
// Add JobCleanUpPolicy of chaos-runner to retain
|
|
chaosEngine.Spec.JobCleanUpPolicy = v1alpha1.CleanUpPolicy(experimentsDetails.JobCleanUpPolicy)
|
|
|
|
// Add ImagePullPolicy of chaos-runner to Always
|
|
chaosEngine.Spec.Components.Runner.ImagePullPolicy = corev1.PullPolicy(experimentsDetails.ImagePullPolicy)
|
|
|
|
// Modify the spec of engine file
|
|
chaosEngine.ObjectMeta.Name = experimentsDetails.EngineName
|
|
chaosEngine.ObjectMeta.Namespace = experimentsDetails.ChaosNamespace
|
|
|
|
// If ChaosEngine contain App Info then update it
|
|
if chaosEngine.Spec.Appinfo.Appns != "" && chaosEngine.Spec.Appinfo.Applabel != "" {
|
|
chaosEngine.Spec.Appinfo.Appns = experimentsDetails.AppNS
|
|
chaosEngine.Spec.Appinfo.Applabel = experimentsDetails.AppLabel
|
|
}
|
|
chaosEngine.Spec.Appinfo.AppKind = experimentsDetails.AppKind
|
|
chaosEngine.Spec.ChaosServiceAccount = experimentsDetails.ChaosServiceAccount
|
|
chaosEngine.Spec.Experiments[0].Name = experimentsDetails.ExperimentName
|
|
chaosEngine.Spec.AnnotationCheck = experimentsDetails.AnnotationCheck
|
|
|
|
// Add common ENV's
|
|
envDetails.SetEnv("TOTAL_CHAOS_DURATION", strconv.Itoa(experimentsDetails.ChaosDuration)).
|
|
SetEnv("CHAOS_INTERVAL", strconv.Itoa(experimentsDetails.ChaosInterval))
|
|
|
|
// Add experiment specific ENV's
|
|
for key, value := range experimentENVs.ENV {
|
|
envDetails.SetEnv(key, value)
|
|
}
|
|
|
|
// update App Node Details
|
|
if experimentsDetails.ApplicationNodeName != "" {
|
|
envDetails.SetEnv("TARGET_NODE", experimentsDetails.ApplicationNodeName)
|
|
if chaosEngine.Spec.Experiments[0].Spec.Components.NodeSelector == nil {
|
|
chaosEngine.Spec.Experiments[0].Spec.Components.NodeSelector = map[string]string{}
|
|
}
|
|
chaosEngine.Spec.Experiments[0].Spec.Components.NodeSelector["kubernetes.io/hostname"] = experimentsDetails.NodeSelectorName
|
|
}
|
|
|
|
// update all the value corresponding to keys from the ENV's in Engine
|
|
for key, value := range chaosEngine.Spec.Experiments[0].Spec.Components.ENV {
|
|
_, ok := envDetails.ENV[value.Name]
|
|
if ok {
|
|
chaosEngine.Spec.Experiments[0].Spec.Components.ENV[key].Value = envDetails.ENV[value.Name]
|
|
}
|
|
}
|
|
|
|
// Marshal serializes the values provided into a YAML document.
|
|
fileData, err := json.Marshal(chaosEngine)
|
|
if err != nil {
|
|
return errors.Errorf("Fail to marshal ChaosEngine %v", err)
|
|
}
|
|
|
|
//Creating chaos engine
|
|
log.Info("[Engine]: Installing ChaosEngine...")
|
|
if err = CreateChaosResource(fileData, experimentsDetails.ChaosNamespace, clients); err != nil {
|
|
return errors.Errorf("fail to apply engine file, err: %v", err)
|
|
}
|
|
log.Info("[Engine]: ChaosEngine Installed Successfully !!!")
|
|
return nil
|
|
}
|
|
|
|
//InstallLitmus installs the latest version of litmus
|
|
func InstallLitmus(testsDetails *types.ExperimentDetails) error {
|
|
|
|
log.Info("Installing Litmus ...")
|
|
if err := DownloadFile("/tmp/install-litmus.yaml", testsDetails.InstallLitmus); err != nil {
|
|
return errors.Errorf("Fail to fetch litmus operator file, due to %v", err)
|
|
}
|
|
log.Info("Updating ChaosOperator Image ...")
|
|
if err := EditFile("/tmp/install-litmus.yaml", "image: litmuschaos/chaos-operator:latest", "image: "+testsDetails.OperatorImage); err != nil {
|
|
return errors.Errorf("Unable to update operator image, due to %v", err)
|
|
|
|
}
|
|
if err = EditKeyValue("/tmp/install-litmus.yaml", " - chaos-operator", "imagePullPolicy: Always", "imagePullPolicy: "+testsDetails.ImagePullPolicy); err != nil {
|
|
return errors.Errorf("Unable to update image pull policy, due to %v", err)
|
|
}
|
|
log.Info("Updating Chaos Runner Image ...")
|
|
if err := EditKeyValue("/tmp/install-litmus.yaml", "CHAOS_RUNNER_IMAGE", "value: \"litmuschaos/chaos-runner:latest\"", "value: '"+testsDetails.RunnerImage+"'"); err != nil {
|
|
return errors.Errorf("Unable to update runner image, due to %v", err)
|
|
}
|
|
//Creating engine
|
|
command := []string{"apply", "-f", "/tmp/install-litmus.yaml"}
|
|
err := Kubectl(command...)
|
|
if err != nil {
|
|
return errors.Errorf("fail to apply litmus installation file, err: %v", err)
|
|
}
|
|
log.Info("[Info]: Litmus installed successfully !!!")
|
|
|
|
return nil
|
|
}
|