mirror of
https://github.com/kastenhq/kubestr.git
synced 2024-12-14 11:57:56 +00:00
Allow FIO test to use an input file (#32)
* Allow FIO test to use an input file * lint ci errors
This commit is contained in:
parent
50b02f177a
commit
bc9dbcb6b4
7 changed files with 306 additions and 611 deletions
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kastenhq/kubestr/pkg/fio"
|
||||
"github.com/kastenhq/kubestr/pkg/kubestr"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -40,7 +41,9 @@ var (
|
|||
}
|
||||
|
||||
fioCheckerStorageClass string
|
||||
fioCheckerConfigMap string
|
||||
fioCheckerSize string
|
||||
fioCheckerNamespace string
|
||||
fioCheckerFilePath string
|
||||
fioCheckerTestName string
|
||||
fioCmd = &cobra.Command{
|
||||
Use: "fio",
|
||||
|
@ -49,7 +52,7 @@ var (
|
|||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
Fio(ctx, output, fioCheckerStorageClass, fioCheckerConfigMap, fioCheckerTestName)
|
||||
Fio(ctx, output, fioCheckerStorageClass, fioCheckerSize, fioCheckerNamespace, fioCheckerTestName, fioCheckerFilePath)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -58,9 +61,12 @@ func init() {
|
|||
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "Options(json)")
|
||||
|
||||
rootCmd.AddCommand(fioCmd)
|
||||
fioCmd.Flags().StringVarP(&fioCheckerStorageClass, "storageclass", "s", "", "The Name of a storageclass")
|
||||
fioCmd.Flags().StringVarP(&fioCheckerConfigMap, "configmap", "c", "", "The Name of a configmap in the current namespace")
|
||||
fioCmd.Flags().StringVarP(&fioCheckerTestName, "testname", "t", "", "The Name of a predefined kubestr fio test")
|
||||
fioCmd.Flags().StringVarP(&fioCheckerStorageClass, "storageclass", "c", "", "The name of a storageclass. (Required)")
|
||||
_ = fioCmd.MarkFlagRequired("storageclass")
|
||||
fioCmd.Flags().StringVarP(&fioCheckerSize, "size", "s", fio.DefaultPVCSize, "The size of the volume used to run FIO.")
|
||||
fioCmd.Flags().StringVarP(&fioCheckerNamespace, "namespace", "n", fio.DefaultNS, "The namespace used to run FIO.")
|
||||
fioCmd.Flags().StringVarP(&fioCheckerFilePath, "fiofile", "f", "", "The path to a an fio config file.")
|
||||
fioCmd.Flags().StringVarP(&fioCheckerTestName, "testname", "t", "", "The Name of a predefined kubestr fio test. Options(default-fio)")
|
||||
// //rootCmd.AddCommand(provCmd)
|
||||
}
|
||||
|
||||
|
@ -110,13 +116,26 @@ func Baseline(ctx context.Context, output string) {
|
|||
}
|
||||
|
||||
// Fio executes the FIO test.
|
||||
func Fio(ctx context.Context, output, storageclass, configMapName, testName string) {
|
||||
func Fio(ctx context.Context, output, storageclass, size, namespace, jobName, fioFilePath string) {
|
||||
p, err := kubestr.NewKubestr()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
result := p.FIO(ctx, storageclass, configMapName, testName)
|
||||
testName := "FIO test results"
|
||||
var result *kubestr.TestOutput
|
||||
if fioResult, err := p.Fio.RunFio(ctx, &fio.RunFIOArgs{
|
||||
StorageClass: storageclass,
|
||||
Size: size,
|
||||
Namespace: namespace,
|
||||
FIOJobName: jobName,
|
||||
FIOJobFilepath: fioFilePath,
|
||||
}); err != nil {
|
||||
result = kubestr.MakeTestOutput(testName, kubestr.StatusError, err.Error(), fioResult)
|
||||
} else {
|
||||
result = kubestr.MakeTestOutput(testName, kubestr.StatusOK, fmt.Sprintf("\n%s\n", fioResult.Result), fioResult)
|
||||
}
|
||||
|
||||
if output == "json" {
|
||||
jsonRes, _ := json.MarshalIndent(result, "", " ")
|
||||
fmt.Println(string(jsonRes))
|
||||
|
|
290
pkg/fio/fio.go
290
pkg/fio/fio.go
|
@ -3,11 +3,13 @@ package fio
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
kankube "github.com/kanisterio/kanister/pkg/kube"
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
sv1 "k8s.io/api/storage/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
@ -22,10 +24,6 @@ const (
|
|||
DefaultFIOJob = "default-fio"
|
||||
// KubestrFIOJob describes the default FIO job
|
||||
KubestrFIOJobGenName = "kubestr-fio"
|
||||
// ConfigMapSCKey describes the storage class key in a config map
|
||||
ConfigMapSCKey = "storageclass"
|
||||
// ConfigMapSizeKey describes the size key in a config map
|
||||
ConfigMapSizeKey = "pvcsize"
|
||||
// ConfigMapJobKey is the default fio job key
|
||||
ConfigMapJobKey = "fiojob"
|
||||
// DefaultPVCSize is the default PVC size
|
||||
|
@ -48,7 +46,7 @@ const (
|
|||
|
||||
// FIO is an interface that represents FIO related commands
|
||||
type FIO interface {
|
||||
RunFio(ctx context.Context, args *RunFIOArgs) (string, error) // , test config
|
||||
RunFio(ctx context.Context, args *RunFIOArgs) (*RunFIOResult, error) // , test config
|
||||
}
|
||||
|
||||
// FIOrunner implments FIO
|
||||
|
@ -58,154 +56,155 @@ type FIOrunner struct {
|
|||
}
|
||||
|
||||
type RunFIOArgs struct {
|
||||
StorageClass string
|
||||
ConfigMapName string
|
||||
JobName string
|
||||
StorageClass string
|
||||
Size string
|
||||
Namespace string
|
||||
FIOJobFilepath string
|
||||
FIOJobName string
|
||||
}
|
||||
|
||||
func (f *FIOrunner) RunFio(ctx context.Context, args *RunFIOArgs) (string, error) {
|
||||
func (a *RunFIOArgs) Validate() error {
|
||||
if a.StorageClass == "" || a.Size == "" || a.Namespace == "" {
|
||||
return fmt.Errorf("Require fields are missing. (StorageClass, Size, Namespace)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RunFIOResult struct {
|
||||
Size string `json:"size,omitempty"`
|
||||
StorageClass *sv1.StorageClass `json:"storageClass,omitempty"`
|
||||
FioConfig string `json:"fioConfig,omitempty"`
|
||||
Result string `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
func (f *FIOrunner) RunFio(ctx context.Context, args *RunFIOArgs) (*RunFIOResult, error) {
|
||||
f.fioSteps = &fioStepper{
|
||||
cli: f.Cli,
|
||||
podReady: &podReadyChecker{cli: f.Cli},
|
||||
podSpecMerger: &podSpecMerger{cli: f.Cli},
|
||||
kubeExecutor: &kubeExecutor{cli: f.Cli},
|
||||
cli: f.Cli,
|
||||
podReady: &podReadyChecker{cli: f.Cli},
|
||||
kubeExecutor: &kubeExecutor{cli: f.Cli},
|
||||
}
|
||||
return f.RunFioHelper(ctx, args)
|
||||
|
||||
}
|
||||
|
||||
func (f *FIOrunner) RunFioHelper(ctx context.Context, args *RunFIOArgs) (string, error) {
|
||||
func (f *FIOrunner) RunFioHelper(ctx context.Context, args *RunFIOArgs) (*RunFIOResult, error) {
|
||||
// create a configmap with test parameters
|
||||
if f.Cli == nil { // for UT purposes
|
||||
return "", fmt.Errorf("cli uninitialized")
|
||||
return nil, fmt.Errorf("cli uninitialized")
|
||||
}
|
||||
if args == nil {
|
||||
args = &RunFIOArgs{}
|
||||
|
||||
if err := args.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := f.fioSteps.validateNamespace(ctx, args.Namespace); err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to find namespace (%s)", args.Namespace)
|
||||
}
|
||||
|
||||
sc, err := f.fioSteps.storageClassExists(ctx, args.StorageClass)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Cannot find StorageClass")
|
||||
}
|
||||
|
||||
configMap, err := f.fioSteps.loadConfigMap(ctx, args)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "Unable to create a ConfigMap")
|
||||
return nil, errors.Wrap(err, "Unable to create a ConfigMap")
|
||||
}
|
||||
defer func() {
|
||||
_ = f.fioSteps.deleteConfigMap(context.TODO(), configMap)
|
||||
_ = f.fioSteps.deleteConfigMap(context.TODO(), configMap, args.Namespace)
|
||||
}()
|
||||
|
||||
testFileName, err := fioTestFilename(configMap.Data)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "Failed to get test file name.")
|
||||
return nil, errors.Wrap(err, "Failed to get test file name.")
|
||||
}
|
||||
|
||||
size := configMap.Data[ConfigMapSizeKey]
|
||||
if size == "" {
|
||||
size = DefaultPVCSize
|
||||
}
|
||||
|
||||
storageClass := configMap.Data[ConfigMapSCKey]
|
||||
if storageClass == "" {
|
||||
return "", fmt.Errorf("StorageClass must be provided")
|
||||
}
|
||||
|
||||
if err := f.fioSteps.storageClassExists(ctx, storageClass); err != nil {
|
||||
return "", errors.Wrap(err, "Cannot find StorageClass")
|
||||
}
|
||||
|
||||
pvc, err := f.fioSteps.createPVC(ctx, storageClass, size)
|
||||
pvc, err := f.fioSteps.createPVC(ctx, args.StorageClass, args.Size, args.Namespace)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "Failed to create PVC")
|
||||
return nil, errors.Wrap(err, "Failed to create PVC")
|
||||
}
|
||||
defer func() {
|
||||
_ = f.fioSteps.deletePVC(context.TODO(), pvc.Name)
|
||||
_ = f.fioSteps.deletePVC(context.TODO(), pvc.Name, args.Namespace)
|
||||
}()
|
||||
fmt.Println("PVC created", pvc.Name)
|
||||
|
||||
pod, err := f.fioSteps.createPod(ctx, pvc.Name, configMap.Name, testFileName)
|
||||
pod, err := f.fioSteps.createPod(ctx, pvc.Name, configMap.Name, testFileName, args.Namespace)
|
||||
defer func() {
|
||||
_ = f.fioSteps.deletePod(context.TODO(), pod.Name)
|
||||
_ = f.fioSteps.deletePod(context.TODO(), pod.Name, args.Namespace)
|
||||
}()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "Failed to create POD")
|
||||
return nil, errors.Wrap(err, "Failed to create POD")
|
||||
}
|
||||
fmt.Println("Pod created", pod.Name)
|
||||
|
||||
fmt.Printf("Running FIO test (%s) on StorageClass (%s) with a PVC of Size (%s)\n", testFileName, storageClass, size)
|
||||
return f.fioSteps.runFIOCommand(ctx, pod.Name, ContainerName, testFileName)
|
||||
fmt.Printf("Running FIO test (%s) on StorageClass (%s) with a PVC of Size (%s)\n", testFileName, args.StorageClass, args.Size)
|
||||
fioOutput, err := f.fioSteps.runFIOCommand(ctx, pod.Name, ContainerName, testFileName, args.Namespace)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed while running FIO test.")
|
||||
}
|
||||
return &RunFIOResult{
|
||||
Size: args.Size,
|
||||
StorageClass: sc,
|
||||
FioConfig: "",
|
||||
Result: fioOutput,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type fioSteps interface {
|
||||
storageClassExists(ctx context.Context, storageClass string) error
|
||||
validateNamespace(ctx context.Context, namespace string) error
|
||||
storageClassExists(ctx context.Context, storageClass string) (*sv1.StorageClass, error)
|
||||
loadConfigMap(ctx context.Context, args *RunFIOArgs) (*v1.ConfigMap, error)
|
||||
createPVC(ctx context.Context, storageclass, size string) (*v1.PersistentVolumeClaim, error)
|
||||
deletePVC(ctx context.Context, pvcName string) error
|
||||
createPod(ctx context.Context, pvcName, configMapName, testFileName string) (*v1.Pod, error)
|
||||
deletePod(ctx context.Context, podName string) error
|
||||
runFIOCommand(ctx context.Context, podName, containerName, testFileName string) (string, error)
|
||||
deleteConfigMap(ctx context.Context, configMap *v1.ConfigMap) error
|
||||
createPVC(ctx context.Context, storageclass, size, namespace string) (*v1.PersistentVolumeClaim, error)
|
||||
deletePVC(ctx context.Context, pvcName, namespace string) error
|
||||
createPod(ctx context.Context, pvcName, configMapName, testFileName, namespace string) (*v1.Pod, error)
|
||||
deletePod(ctx context.Context, podName, namespace string) error
|
||||
runFIOCommand(ctx context.Context, podName, containerName, testFileName, namespace string) (string, error)
|
||||
deleteConfigMap(ctx context.Context, configMap *v1.ConfigMap, namespace string) error
|
||||
}
|
||||
|
||||
type fioStepper struct {
|
||||
cli kubernetes.Interface
|
||||
podReady waitForPodReadyInterface
|
||||
podSpecMerger podSpecMergeInterface
|
||||
kubeExecutor kubeExecInterface
|
||||
cli kubernetes.Interface
|
||||
podReady waitForPodReadyInterface
|
||||
kubeExecutor kubeExecInterface
|
||||
}
|
||||
|
||||
func (s *fioStepper) storageClassExists(ctx context.Context, storageClass string) error {
|
||||
if _, err := s.cli.StorageV1().StorageClasses().Get(ctx, storageClass, metav1.GetOptions{}); err != nil {
|
||||
func (s *fioStepper) validateNamespace(ctx context.Context, namespace string) error {
|
||||
if _, err := s.cli.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *fioStepper) loadConfigMap(ctx context.Context, args *RunFIOArgs) (*v1.ConfigMap, error) {
|
||||
configMap := &v1.ConfigMap{}
|
||||
var err error
|
||||
if args.ConfigMapName != "" {
|
||||
configMap, err = s.cli.CoreV1().ConfigMaps(GetPodNamespace()).Get(ctx, args.ConfigMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Failed to load configMap (%s) in namespace (%s)", args.ConfigMapName, GetPodNamespace())
|
||||
}
|
||||
}
|
||||
|
||||
if configMap.Data == nil {
|
||||
configMap.Data = map[string]string{}
|
||||
}
|
||||
|
||||
if args.StorageClass != "" {
|
||||
configMap.Data[ConfigMapSCKey] = args.StorageClass
|
||||
}
|
||||
|
||||
if val, ok := configMap.Data[ConfigMapSizeKey]; !ok || val == "" {
|
||||
configMap.Data[ConfigMapSizeKey] = DefaultPVCSize
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(configMap.Data) > 3:
|
||||
return nil, fmt.Errorf("Invalid configmap data- %v", configMap.Data)
|
||||
case args.JobName != "": // replace existing job with provided one
|
||||
fioJob, ok := fioJobs[args.JobName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Unable to find fio job (%s)", args.JobName)
|
||||
}
|
||||
configMapJobKey := ConfigMapJobKey
|
||||
for key := range configMap.Data {
|
||||
if key != ConfigMapSizeKey && key != ConfigMapSCKey {
|
||||
configMapJobKey = key
|
||||
}
|
||||
}
|
||||
configMap.Data[configMapJobKey] = fioJob
|
||||
case len(configMap.Data) == 2: // if none provided use default
|
||||
configMap.Data[ConfigMapJobKey] = fioJobs[DefaultFIOJob]
|
||||
default:
|
||||
}
|
||||
// create
|
||||
configMap.Name = ""
|
||||
configMap.GenerateName = KubestrFIOJobGenName
|
||||
configMap.Labels = map[string]string{CreatedByFIOLabel: "true"}
|
||||
return s.cli.CoreV1().ConfigMaps(GetPodNamespace()).Create(ctx, configMap, metav1.CreateOptions{})
|
||||
func (s *fioStepper) storageClassExists(ctx context.Context, storageClass string) (*sv1.StorageClass, error) {
|
||||
return s.cli.StorageV1().StorageClasses().Get(ctx, storageClass, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (s *fioStepper) createPVC(ctx context.Context, storageclass, size string) (*v1.PersistentVolumeClaim, error) {
|
||||
func (s *fioStepper) loadConfigMap(ctx context.Context, args *RunFIOArgs) (*v1.ConfigMap, error) {
|
||||
configMap := &v1.ConfigMap{
|
||||
Data: make(map[string]string),
|
||||
}
|
||||
switch {
|
||||
case args.FIOJobFilepath != "":
|
||||
data, err := ioutil.ReadFile(args.FIOJobFilepath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "File reading error")
|
||||
}
|
||||
configMap.Data[filepath.Base(args.FIOJobFilepath)] = string(data)
|
||||
case args.FIOJobName != "":
|
||||
if _, ok := fioJobs[args.FIOJobName]; !ok {
|
||||
return nil, fmt.Errorf("FIO job not found")
|
||||
}
|
||||
configMap.Data[args.FIOJobName] = fioJobs[args.FIOJobName]
|
||||
default:
|
||||
configMap.Data[DefaultFIOJob] = fioJobs[DefaultFIOJob]
|
||||
}
|
||||
// create
|
||||
configMap.GenerateName = KubestrFIOJobGenName
|
||||
configMap.Labels = map[string]string{CreatedByFIOLabel: "true"}
|
||||
return s.cli.CoreV1().ConfigMaps(args.Namespace).Create(ctx, configMap, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
func (s *fioStepper) createPVC(ctx context.Context, storageclass, size, namespace string) (*v1.PersistentVolumeClaim, error) {
|
||||
sizeResource, err := resource.ParseQuantity(size)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to parse PVC size (%s)", size)
|
||||
|
@ -224,21 +223,21 @@ func (s *fioStepper) createPVC(ctx context.Context, storageclass, size string) (
|
|||
},
|
||||
},
|
||||
}
|
||||
return s.cli.CoreV1().PersistentVolumeClaims(GetPodNamespace()).Create(ctx, pvc, metav1.CreateOptions{})
|
||||
return s.cli.CoreV1().PersistentVolumeClaims(namespace).Create(ctx, pvc, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
func (s *fioStepper) deletePVC(ctx context.Context, pvcName string) error {
|
||||
return s.cli.CoreV1().PersistentVolumeClaims(GetPodNamespace()).Delete(ctx, pvcName, metav1.DeleteOptions{})
|
||||
func (s *fioStepper) deletePVC(ctx context.Context, pvcName, namespace string) error {
|
||||
return s.cli.CoreV1().PersistentVolumeClaims(namespace).Delete(ctx, pvcName, metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
func (s *fioStepper) createPod(ctx context.Context, pvcName, configMapName, testFileName string) (*v1.Pod, error) {
|
||||
func (s *fioStepper) createPod(ctx context.Context, pvcName, configMapName, testFileName, namespace string) (*v1.Pod, error) {
|
||||
if pvcName == "" || configMapName == "" || testFileName == "" {
|
||||
return nil, fmt.Errorf("Create pod missing required arguments.")
|
||||
}
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: PodGenerateName,
|
||||
Namespace: GetPodNamespace(),
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{
|
||||
|
@ -249,6 +248,7 @@ func (s *fioStepper) createPod(ctx context.Context, pvcName, configMapName, test
|
|||
{Name: "persistent-storage", MountPath: VolumeMountPath},
|
||||
{Name: "config-map", MountPath: ConfigMapMountPath},
|
||||
},
|
||||
Image: "ghcr.io/kastenhq/kubestr:latest",
|
||||
}},
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
|
@ -270,24 +270,17 @@ func (s *fioStepper) createPod(ctx context.Context, pvcName, configMapName, test
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
mergedPodSpec, err := s.podSpecMerger.mergePodSpec(ctx, GetPodNamespace(), pod.Spec)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to merge Pod Spec with parent pod.")
|
||||
}
|
||||
|
||||
pod.Spec = mergedPodSpec
|
||||
podRes, err := s.cli.CoreV1().Pods(GetPodNamespace()).Create(ctx, pod, metav1.CreateOptions{})
|
||||
podRes, err := s.cli.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return podRes, err
|
||||
}
|
||||
|
||||
err = s.podReady.waitForPodReady(ctx, GetPodNamespace(), podRes.Name)
|
||||
err = s.podReady.waitForPodReady(ctx, namespace, podRes.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podRes, err = s.cli.CoreV1().Pods(GetPodNamespace()).Get(ctx, podRes.Name, metav1.GetOptions{})
|
||||
podRes, err = s.cli.CoreV1().Pods(namespace).Get(ctx, podRes.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return podRes, err
|
||||
}
|
||||
|
@ -295,14 +288,14 @@ func (s *fioStepper) createPod(ctx context.Context, pvcName, configMapName, test
|
|||
return podRes, nil
|
||||
}
|
||||
|
||||
func (s *fioStepper) deletePod(ctx context.Context, podName string) error {
|
||||
return s.cli.CoreV1().Pods(GetPodNamespace()).Delete(ctx, podName, metav1.DeleteOptions{})
|
||||
func (s *fioStepper) deletePod(ctx context.Context, podName, namespace string) error {
|
||||
return s.cli.CoreV1().Pods(namespace).Delete(ctx, podName, metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
func (s *fioStepper) runFIOCommand(ctx context.Context, podName, containerName, testFileName string) (string, error) {
|
||||
func (s *fioStepper) runFIOCommand(ctx context.Context, podName, containerName, testFileName, namespace string) (string, error) {
|
||||
jobFilePath := fmt.Sprintf("%s/%s", ConfigMapMountPath, testFileName)
|
||||
command := []string{"fio", "--directory", VolumeMountPath, jobFilePath}
|
||||
stdout, stderr, err := s.kubeExecutor.exec(GetPodNamespace(), podName, containerName, command)
|
||||
stdout, stderr, err := s.kubeExecutor.exec(namespace, podName, containerName, command)
|
||||
if err != nil || stderr != "" {
|
||||
return stdout, errors.Wrapf(err, "Error running command:(%v), stderr:(%s)", command, stderr)
|
||||
}
|
||||
|
@ -310,32 +303,22 @@ func (s *fioStepper) runFIOCommand(ctx context.Context, podName, containerName,
|
|||
}
|
||||
|
||||
// deleteConfigMap only deletes a config map if it has the label
|
||||
func (s *fioStepper) deleteConfigMap(ctx context.Context, configMap *v1.ConfigMap) error {
|
||||
func (s *fioStepper) deleteConfigMap(ctx context.Context, configMap *v1.ConfigMap, namespace string) error {
|
||||
if val, ok := configMap.Labels[CreatedByFIOLabel]; ok && val == "true" {
|
||||
return s.cli.CoreV1().ConfigMaps(GetPodNamespace()).Delete(ctx, configMap.Name, metav1.DeleteOptions{})
|
||||
return s.cli.CoreV1().ConfigMaps(namespace).Delete(ctx, configMap.Name, metav1.DeleteOptions{})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPodNamespace gets the pods namespace or returns default
|
||||
func GetPodNamespace() string {
|
||||
if val, ok := os.LookupEnv(PodNamespaceEnvKey); ok {
|
||||
return val
|
||||
}
|
||||
return DefaultNS
|
||||
}
|
||||
|
||||
func fioTestFilename(configMap map[string]string) (string, error) {
|
||||
potentialFilenames := []string{}
|
||||
for key := range configMap {
|
||||
if key != ConfigMapSCKey && key != ConfigMapSizeKey {
|
||||
potentialFilenames = append(potentialFilenames, key)
|
||||
}
|
||||
}
|
||||
if len(potentialFilenames) != 1 {
|
||||
if len(configMap) != 1 {
|
||||
return "", fmt.Errorf("Unable to find fio file in configmap/more than one found %v", configMap)
|
||||
}
|
||||
return potentialFilenames[0], nil
|
||||
var fileName string
|
||||
for key := range configMap {
|
||||
fileName = key
|
||||
}
|
||||
return fileName, nil
|
||||
}
|
||||
|
||||
type waitForPodReadyInterface interface {
|
||||
|
@ -361,30 +344,3 @@ type kubeExecutor struct {
|
|||
func (k *kubeExecutor) exec(namespace, podName, containerName string, command []string) (string, string, error) {
|
||||
return kankube.Exec(k.cli, namespace, podName, containerName, command, nil)
|
||||
}
|
||||
|
||||
type podSpecMergeInterface interface {
|
||||
mergePodSpec(ctx context.Context, namespace string, podSpec v1.PodSpec) (v1.PodSpec, error)
|
||||
}
|
||||
|
||||
type podSpecMerger struct {
|
||||
cli kubernetes.Interface
|
||||
}
|
||||
|
||||
func (m *podSpecMerger) mergePodSpec(ctx context.Context, namespace string, podSpec v1.PodSpec) (v1.PodSpec, error) {
|
||||
currentPodName := os.Getenv(PodNameEnvKey)
|
||||
if currentPodName == "" {
|
||||
return podSpec, fmt.Errorf("Unable to retrieve Pod name from environment variable (%s)", PodNameEnvKey)
|
||||
}
|
||||
currentPod, err := m.cli.CoreV1().Pods(namespace).Get(ctx, currentPodName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return podSpec, fmt.Errorf("Failed to discover pod configuration for Pod (%s): (%s)\n", currentPodName, err.Error())
|
||||
}
|
||||
if len(podSpec.Containers) != 1 {
|
||||
return podSpec, fmt.Errorf("FIO pod doesn't have exactly 1 container.")
|
||||
}
|
||||
podSpec.NodeSelector = currentPod.Spec.NodeSelector
|
||||
podSpec.Tolerations = currentPod.Spec.Tolerations
|
||||
podSpec.Containers[0].Image = currentPod.Spec.Containers[0].Image
|
||||
podSpec.SecurityContext = currentPod.Spec.SecurityContext
|
||||
return podSpec, nil
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package fio
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
|
@ -10,6 +11,7 @@ import (
|
|||
. "gopkg.in/check.v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
scv1 "k8s.io/api/storage/v1"
|
||||
sv1 "k8s.io/api/storage/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
@ -46,42 +48,56 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
expectedTFN string
|
||||
expectedPVC string
|
||||
}{
|
||||
{ // invalid args (storageclass)
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{},
|
||||
args: &RunFIOArgs{},
|
||||
checker: NotNil,
|
||||
},
|
||||
{ // invalid args (size)
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{},
|
||||
args: &RunFIOArgs{
|
||||
StorageClass: "sc",
|
||||
},
|
||||
checker: NotNil,
|
||||
},
|
||||
{ // invalid args (namespace)
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{},
|
||||
args: &RunFIOArgs{
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
},
|
||||
checker: NotNil,
|
||||
},
|
||||
{ // namespace doesn't exist
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{
|
||||
vnErr: fmt.Errorf("namespace Err"),
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
Namespace: "foo",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"VN"},
|
||||
},
|
||||
{ // storageclass not found
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{
|
||||
lcmConfigMap: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
},
|
||||
},
|
||||
cPVC: &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "PVC",
|
||||
},
|
||||
},
|
||||
cPod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "Pod",
|
||||
},
|
||||
},
|
||||
sceErr: fmt.Errorf("storageclass Err"),
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
StorageClass: "sc",
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
Namespace: "foo",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"LCM", "DCM"},
|
||||
expectedSC: "sc",
|
||||
expectedSize: DefaultPVCSize,
|
||||
expectedTFN: "testfile.fio",
|
||||
expectedCM: "CM1",
|
||||
expectedPVC: "PVC",
|
||||
expectedSteps: []string{"VN", "SCE"},
|
||||
},
|
||||
{ // storage class provided by config map
|
||||
{ // success
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{
|
||||
lcmConfigMap: &v1.ConfigMap{
|
||||
|
@ -90,7 +106,6 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
ConfigMapSCKey: "sc",
|
||||
},
|
||||
},
|
||||
cPVC: &v1.PersistentVolumeClaim{
|
||||
|
@ -105,53 +120,18 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
},
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
Namespace: "foo",
|
||||
},
|
||||
checker: IsNil,
|
||||
expectedSteps: []string{"LCM", "SCE", "CPVC", "CPOD", "RFIOC", "DPOD", "DPVC", "DCM"},
|
||||
expectedSteps: []string{"VN", "SCE", "LCM", "CPVC", "CPOD", "RFIOC", "DPOD", "DPVC", "DCM"},
|
||||
expectedSC: "sc",
|
||||
expectedSize: DefaultPVCSize,
|
||||
expectedTFN: "testfile.fio",
|
||||
expectedCM: "CM1",
|
||||
expectedPVC: "PVC",
|
||||
},
|
||||
{ // use size provided by Configmap
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{
|
||||
lcmConfigMap: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
ConfigMapSCKey: "SC2",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
},
|
||||
},
|
||||
cPVC: &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "PVC",
|
||||
},
|
||||
},
|
||||
cPod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "Pod",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
},
|
||||
checker: IsNil,
|
||||
expectedSteps: []string{"LCM", "SCE", "CPVC", "CPOD", "RFIOC", "DPOD", "DPVC", "DCM"},
|
||||
expectedSC: "SC2",
|
||||
expectedSize: "10Gi",
|
||||
expectedTFN: "testfile.fio",
|
||||
expectedCM: "CM1",
|
||||
expectedPVC: "PVC",
|
||||
},
|
||||
{ // fio test error
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{
|
||||
|
@ -160,9 +140,7 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
Name: "CM1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
ConfigMapSCKey: "SC2",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
"testfile.fio": "testfiledata",
|
||||
},
|
||||
},
|
||||
cPVC: &v1.PersistentVolumeClaim{
|
||||
|
@ -178,16 +156,12 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
rFIOErr: fmt.Errorf("run fio error"),
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
Namespace: "foo",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"LCM", "SCE", "CPVC", "CPOD", "RFIOC", "DPOD", "DPVC", "DCM"},
|
||||
expectedSC: "SC2",
|
||||
expectedSize: "10Gi",
|
||||
expectedTFN: "testfile.fio",
|
||||
expectedCM: "CM1",
|
||||
expectedPVC: "PVC",
|
||||
expectedSteps: []string{"VN", "SCE", "LCM", "CPVC", "CPOD", "RFIOC", "DPOD", "DPVC", "DCM"},
|
||||
},
|
||||
{ // create pod error
|
||||
cli: fake.NewSimpleClientset(),
|
||||
|
@ -197,9 +171,7 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
Name: "CM1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
ConfigMapSCKey: "SC2",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
"testfile.fio": "testfiledata",
|
||||
},
|
||||
},
|
||||
cPVC: &v1.PersistentVolumeClaim{
|
||||
|
@ -215,11 +187,12 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
cPodErr: fmt.Errorf("pod create error"),
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
Namespace: "foo",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"LCM", "SCE", "CPVC", "CPOD", "DPOD", "DPVC", "DCM"},
|
||||
expectedSteps: []string{"VN", "SCE", "LCM", "CPVC", "CPOD", "DPOD", "DPVC", "DCM"},
|
||||
},
|
||||
{ // create PVC error
|
||||
cli: fake.NewSimpleClientset(),
|
||||
|
@ -229,41 +202,18 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
Name: "CM1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
ConfigMapSCKey: "SC2",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
"testfile.fio": "testfiledata",
|
||||
},
|
||||
},
|
||||
cPVCErr: fmt.Errorf("pvc create error"),
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
Namespace: "foo",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"LCM", "SCE", "CPVC", "DCM"},
|
||||
},
|
||||
{ // storageclass not found
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{
|
||||
lcmConfigMap: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
ConfigMapSCKey: "SC2",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
},
|
||||
},
|
||||
sceErr: fmt.Errorf("storageclass not found error"),
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"LCM", "SCE", "DCM"},
|
||||
expectedSteps: []string{"VN", "SCE", "LCM", "CPVC", "DCM"},
|
||||
},
|
||||
{ // testfilename retrieval error, more than one provided
|
||||
cli: fake.NewSimpleClientset(),
|
||||
|
@ -273,53 +223,31 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
Name: "CM1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
"testfile.fio2": "testfiledata",
|
||||
ConfigMapSCKey: "SC2",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
"testfile.fio": "testfiledata",
|
||||
"testfile.fio2": "testfiledata",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
Namespace: "foo",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"LCM", "DCM"},
|
||||
},
|
||||
{ // storageclass not provided in args or configmap
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{
|
||||
lcmConfigMap: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"testfile.fio": "testfiledata",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
},
|
||||
},
|
||||
cPVC: &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "PVC",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
JobName: "job",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"LCM", "DCM"},
|
||||
expectedSteps: []string{"VN", "SCE", "LCM", "DCM"},
|
||||
},
|
||||
{ // load configmap error
|
||||
cli: fake.NewSimpleClientset(),
|
||||
stepper: &fakeFioStepper{
|
||||
lcmErr: fmt.Errorf("failed to load configmap"),
|
||||
},
|
||||
args: nil,
|
||||
args: &RunFIOArgs{
|
||||
StorageClass: "sc",
|
||||
Size: "100Gi",
|
||||
Namespace: "foo",
|
||||
},
|
||||
checker: NotNil,
|
||||
expectedSteps: []string{"LCM"},
|
||||
expectedSteps: []string{"VN", "SCE", "LCM"},
|
||||
},
|
||||
} {
|
||||
c.Log(i)
|
||||
|
@ -331,8 +259,6 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
c.Check(err, tc.checker)
|
||||
c.Assert(tc.stepper.steps, DeepEquals, tc.expectedSteps)
|
||||
if err == nil {
|
||||
c.Assert(tc.expectedSC, Equals, tc.stepper.sceExpSC)
|
||||
c.Assert(tc.expectedCM, Equals, tc.stepper.lcmExpCM)
|
||||
c.Assert(tc.expectedSC, Equals, tc.stepper.cPVCExpSC)
|
||||
c.Assert(tc.expectedSize, Equals, tc.stepper.cPVCExpSize)
|
||||
c.Assert(tc.expectedTFN, Equals, tc.stepper.cPodExpFN)
|
||||
|
@ -345,10 +271,11 @@ func (s *FIOTestSuite) TestRunFioHelper(c *C) {
|
|||
type fakeFioStepper struct {
|
||||
steps []string
|
||||
|
||||
sceExpSC string
|
||||
sceErr error
|
||||
vnErr error
|
||||
|
||||
sceSC *sv1.StorageClass
|
||||
sceErr error
|
||||
|
||||
lcmExpCM string
|
||||
lcmConfigMap *v1.ConfigMap
|
||||
lcmErr error
|
||||
|
||||
|
@ -371,42 +298,44 @@ type fakeFioStepper struct {
|
|||
rFIOErr error
|
||||
}
|
||||
|
||||
func (f *fakeFioStepper) storageClassExists(ctx context.Context, storageClass string) error {
|
||||
func (f *fakeFioStepper) validateNamespace(ctx context.Context, namespace string) error {
|
||||
f.steps = append(f.steps, "VN")
|
||||
return f.vnErr
|
||||
}
|
||||
func (f *fakeFioStepper) storageClassExists(ctx context.Context, storageClass string) (*sv1.StorageClass, error) {
|
||||
f.steps = append(f.steps, "SCE")
|
||||
f.sceExpSC = storageClass
|
||||
return f.sceErr
|
||||
return f.sceSC, f.sceErr
|
||||
}
|
||||
func (f *fakeFioStepper) loadConfigMap(ctx context.Context, args *RunFIOArgs) (*v1.ConfigMap, error) {
|
||||
f.steps = append(f.steps, "LCM")
|
||||
f.lcmExpCM = args.ConfigMapName
|
||||
return f.lcmConfigMap, f.lcmErr
|
||||
}
|
||||
func (f *fakeFioStepper) createPVC(ctx context.Context, storageclass, size string) (*v1.PersistentVolumeClaim, error) {
|
||||
func (f *fakeFioStepper) createPVC(ctx context.Context, storageclass, size, namespace string) (*v1.PersistentVolumeClaim, error) {
|
||||
f.steps = append(f.steps, "CPVC")
|
||||
f.cPVCExpSC = storageclass
|
||||
f.cPVCExpSize = size
|
||||
return f.cPVC, f.cPVCErr
|
||||
}
|
||||
func (f *fakeFioStepper) deletePVC(ctx context.Context, pvcName string) error {
|
||||
func (f *fakeFioStepper) deletePVC(ctx context.Context, pvcName, namespace string) error {
|
||||
f.steps = append(f.steps, "DPVC")
|
||||
return f.dPVCErr
|
||||
}
|
||||
func (f *fakeFioStepper) createPod(ctx context.Context, pvcName, configMapName, testFileName string) (*v1.Pod, error) {
|
||||
func (f *fakeFioStepper) createPod(ctx context.Context, pvcName, configMapName, testFileName, namespace string) (*v1.Pod, error) {
|
||||
f.steps = append(f.steps, "CPOD")
|
||||
f.cPodExpCM = configMapName
|
||||
f.cPodExpFN = testFileName
|
||||
f.cPodExpPVC = pvcName
|
||||
return f.cPod, f.cPodErr
|
||||
}
|
||||
func (f *fakeFioStepper) deletePod(ctx context.Context, podName string) error {
|
||||
func (f *fakeFioStepper) deletePod(ctx context.Context, podName, namespace string) error {
|
||||
f.steps = append(f.steps, "DPOD")
|
||||
return f.dPodErr
|
||||
}
|
||||
func (f *fakeFioStepper) runFIOCommand(ctx context.Context, podName, containerName, testFileName string) (string, error) {
|
||||
func (f *fakeFioStepper) runFIOCommand(ctx context.Context, podName, containerName, testFileName, namespace string) (string, error) {
|
||||
f.steps = append(f.steps, "RFIOC")
|
||||
return f.rFIOout, f.rFIOErr
|
||||
}
|
||||
func (f *fakeFioStepper) deleteConfigMap(ctx context.Context, configMap *v1.ConfigMap) error {
|
||||
func (f *fakeFioStepper) deleteConfigMap(ctx context.Context, configMap *v1.ConfigMap, namespace string) error {
|
||||
f.steps = append(f.steps, "DCM")
|
||||
return nil
|
||||
}
|
||||
|
@ -430,13 +359,30 @@ func (s *FIOTestSuite) TestStorageClassExists(c *C) {
|
|||
},
|
||||
} {
|
||||
stepper := &fioStepper{cli: tc.cli}
|
||||
err := stepper.storageClassExists(ctx, tc.storageClass)
|
||||
_, err := stepper.storageClassExists(ctx, tc.storageClass)
|
||||
c.Check(err, tc.checker)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FIOTestSuite) TestValidateNamespace(c *C) {
|
||||
ctx := context.Background()
|
||||
stepper := &fioStepper{cli: fake.NewSimpleClientset()}
|
||||
err := stepper.validateNamespace(ctx, "ns")
|
||||
c.Assert(err, NotNil)
|
||||
stepper = &fioStepper{cli: fake.NewSimpleClientset(&v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns",
|
||||
},
|
||||
})}
|
||||
err = stepper.validateNamespace(ctx, "ns")
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *FIOTestSuite) TestLoadConfigMap(c *C) {
|
||||
ctx := context.Background()
|
||||
file, err := ioutil.TempFile("", "tempTLCfile")
|
||||
c.Check(err, IsNil)
|
||||
defer os.Remove(file.Name())
|
||||
for i, tc := range []struct {
|
||||
cli kubernetes.Interface
|
||||
configMapName string
|
||||
|
@ -447,126 +393,46 @@ func (s *FIOTestSuite) TestLoadConfigMap(c *C) {
|
|||
failCreates bool
|
||||
hasLabel bool
|
||||
}{
|
||||
{ // provided cm name not found
|
||||
{ // provided file name not found
|
||||
cli: fake.NewSimpleClientset(),
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "nonexistantcm",
|
||||
FIOJobFilepath: "nonexistantfile",
|
||||
},
|
||||
cmChecker: IsNil,
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // specified config map found
|
||||
cli: fake.NewSimpleClientset(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
Namespace: "default",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
}),
|
||||
cli: fake.NewSimpleClientset(),
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
StorageClass: "sc",
|
||||
FIOJobFilepath: file.Name(),
|
||||
FIOJobName: "random", // won't use this case
|
||||
},
|
||||
cmChecker: NotNil,
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{ // specified config map found, replace storage class
|
||||
cli: fake.NewSimpleClientset(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
Namespace: "default",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
}),
|
||||
{ // specified job name, not found
|
||||
cli: fake.NewSimpleClientset(),
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
StorageClass: "sc",
|
||||
},
|
||||
cmChecker: NotNil,
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{ // specified config map found, invalid config map
|
||||
cli: fake.NewSimpleClientset(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
Namespace: "default",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"fiojob": "data",
|
||||
"baddata1": "baddata",
|
||||
},
|
||||
}),
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
StorageClass: "sc",
|
||||
FIOJobName: "random",
|
||||
},
|
||||
cmChecker: IsNil,
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // specified config map found, replace with job
|
||||
cli: fake.NewSimpleClientset(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
Namespace: "default",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"job1": "jobdetails",
|
||||
},
|
||||
}),
|
||||
{ // specified job name, found
|
||||
cli: fake.NewSimpleClientset(),
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
StorageClass: "sc",
|
||||
JobName: DefaultFIOJob,
|
||||
FIOJobName: DefaultFIOJob,
|
||||
},
|
||||
cmChecker: NotNil,
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{ // specified config map found, replace with job
|
||||
cli: fake.NewSimpleClientset(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
Namespace: "default",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
}),
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
StorageClass: "sc",
|
||||
JobName: DefaultFIOJob,
|
||||
},
|
||||
cmChecker: NotNil,
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{ // specified config map not found in namespace
|
||||
cli: fake.NewSimpleClientset(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "CM1",
|
||||
Namespace: "badns",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
}),
|
||||
args: &RunFIOArgs{
|
||||
ConfigMapName: "CM1",
|
||||
},
|
||||
cmChecker: IsNil,
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // creates the default job ConfigMap
|
||||
{ // use default job
|
||||
cli: fake.NewSimpleClientset(),
|
||||
cmChecker: NotNil,
|
||||
errChecker: IsNil,
|
||||
args: &RunFIOArgs{},
|
||||
cmChecker: NotNil,
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{ // job doesn't exist.
|
||||
cli: fake.NewSimpleClientset(),
|
||||
cmChecker: IsNil,
|
||||
errChecker: NotNil,
|
||||
args: &RunFIOArgs{
|
||||
JobName: "nonExistentJob",
|
||||
},
|
||||
jobName: "nonExistentJob",
|
||||
},
|
||||
{ // Fails to create default job
|
||||
{ // Fails to create configMap
|
||||
cli: fake.NewSimpleClientset(),
|
||||
cmChecker: IsNil,
|
||||
errChecker: NotNil,
|
||||
|
@ -630,7 +496,7 @@ func (s *FIOTestSuite) TestCreatePVC(c *C) {
|
|||
return true, nil, errors.New("Error creating object")
|
||||
})
|
||||
}
|
||||
pvc, err := stepper.createPVC(ctx, tc.storageclass, tc.size)
|
||||
pvc, err := stepper.createPVC(ctx, tc.storageclass, tc.size, DefaultNS)
|
||||
c.Check(err, tc.errChecker)
|
||||
c.Check(pvc, tc.pvcChecker)
|
||||
if pvc != nil {
|
||||
|
@ -648,24 +514,23 @@ func (s *FIOTestSuite) TestDeletePVC(c *C) {
|
|||
stepper := &fioStepper{cli: fake.NewSimpleClientset(&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
Namespace: GetPodNamespace(),
|
||||
Namespace: DefaultNS,
|
||||
}})}
|
||||
err := stepper.deletePVC(ctx, "pvc")
|
||||
err := stepper.deletePVC(ctx, "pvc", DefaultNS)
|
||||
c.Assert(err, IsNil)
|
||||
err = stepper.deletePVC(ctx, "pvc")
|
||||
err = stepper.deletePVC(ctx, "pvc", DefaultNS)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (s *FIOTestSuite) TestCreatPod(c *C) {
|
||||
ctx := context.Background()
|
||||
for _, tc := range []struct {
|
||||
pvcName string
|
||||
configMapName string
|
||||
testFileName string
|
||||
reactor []k8stesting.Reactor
|
||||
podReadyErr error
|
||||
podSpecMergerErr error
|
||||
errChecker Checker
|
||||
for i, tc := range []struct {
|
||||
pvcName string
|
||||
configMapName string
|
||||
testFileName string
|
||||
reactor []k8stesting.Reactor
|
||||
podReadyErr error
|
||||
errChecker Checker
|
||||
}{
|
||||
{
|
||||
pvcName: "pvc",
|
||||
|
@ -726,13 +591,6 @@ func (s *FIOTestSuite) TestCreatPod(c *C) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pvcName: "pvc",
|
||||
configMapName: "cm",
|
||||
testFileName: "sdf",
|
||||
errChecker: NotNil,
|
||||
podSpecMergerErr: fmt.Errorf("podspecmerger error"),
|
||||
},
|
||||
{
|
||||
pvcName: "pvc",
|
||||
configMapName: "cm",
|
||||
|
@ -752,15 +610,15 @@ func (s *FIOTestSuite) TestCreatPod(c *C) {
|
|||
errChecker: NotNil,
|
||||
},
|
||||
} {
|
||||
fmt.Println(i)
|
||||
stepper := &fioStepper{
|
||||
cli: fake.NewSimpleClientset(),
|
||||
podReady: &fakePodReadyChecker{prcErr: tc.podReadyErr},
|
||||
podSpecMerger: &fakePodSpecMerger{psmErr: tc.podSpecMergerErr},
|
||||
cli: fake.NewSimpleClientset(),
|
||||
podReady: &fakePodReadyChecker{prcErr: tc.podReadyErr},
|
||||
}
|
||||
if tc.reactor != nil {
|
||||
stepper.cli.(*fake.Clientset).Fake.ReactionChain = tc.reactor
|
||||
}
|
||||
pod, err := stepper.createPod(ctx, tc.pvcName, tc.configMapName, tc.testFileName)
|
||||
pod, err := stepper.createPod(ctx, tc.pvcName, tc.configMapName, tc.testFileName, DefaultNS)
|
||||
c.Check(err, tc.errChecker)
|
||||
if err == nil {
|
||||
c.Assert(pod.GenerateName, Equals, PodGenerateName)
|
||||
|
@ -790,11 +648,11 @@ func (s *FIOTestSuite) TestDeletePod(c *C) {
|
|||
stepper := &fioStepper{cli: fake.NewSimpleClientset(&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod",
|
||||
Namespace: GetPodNamespace(),
|
||||
Namespace: DefaultNS,
|
||||
}})}
|
||||
err := stepper.deletePod(ctx, "pod")
|
||||
err := stepper.deletePod(ctx, "pod", DefaultNS)
|
||||
c.Assert(err, IsNil)
|
||||
err = stepper.deletePod(ctx, "pod")
|
||||
err = stepper.deletePod(ctx, "pod", DefaultNS)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
|
@ -806,19 +664,16 @@ func (s *FIOTestSuite) TestFioTestFileName(c *C) {
|
|||
}{
|
||||
{
|
||||
configMap: map[string]string{
|
||||
ConfigMapSCKey: "storageclass",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
"testfile.fio": "some test data",
|
||||
"testfile.fio": "some test data",
|
||||
},
|
||||
retVal: "testfile.fio",
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{
|
||||
configMap: map[string]string{
|
||||
ConfigMapSCKey: "storageclass",
|
||||
ConfigMapSizeKey: "10Gi",
|
||||
"testfile.fio": "some test data",
|
||||
"testfile2.fio": "some test data2", // only support one file
|
||||
"ConfigMapSCKey": "storageclass",
|
||||
"ConfigMapSizeKey": "10Gi",
|
||||
"testfile.fio": "some test data",
|
||||
},
|
||||
retVal: "",
|
||||
errChecker: NotNil,
|
||||
|
@ -865,7 +720,7 @@ func (s *FIOTestSuite) TestRunFioCommand(c *C) {
|
|||
stepper := &fioStepper{
|
||||
kubeExecutor: tc.executor,
|
||||
}
|
||||
out, err := stepper.runFIOCommand(ctx, tc.podName, tc.containerName, tc.testFileName)
|
||||
out, err := stepper.runFIOCommand(ctx, tc.podName, tc.containerName, tc.testFileName, DefaultNS)
|
||||
c.Check(err, tc.errChecker)
|
||||
c.Assert(out, Equals, tc.executor.keStdOut)
|
||||
c.Assert(tc.executor.keInPodName, Equals, tc.podName)
|
||||
|
@ -939,7 +794,7 @@ func (s *FIOTestSuite) TestDeleteConfigMap(c *C) {
|
|||
},
|
||||
} {
|
||||
stepper := &fioStepper{cli: tc.cli}
|
||||
err := stepper.deleteConfigMap(ctx, tc.cm)
|
||||
err := stepper.deleteConfigMap(ctx, tc.cm, DefaultNS)
|
||||
c.Check(err, tc.errChecker)
|
||||
if err == nil {
|
||||
list, err := stepper.cli.CoreV1().ConfigMaps(defaultNS).List(ctx, metav1.ListOptions{})
|
||||
|
@ -968,112 +823,6 @@ func (s *FIOTestSuite) TestWaitForPodReady(c *C) {
|
|||
})
|
||||
}
|
||||
|
||||
func (s *FIOTestSuite) TestMergePodSpec(c *C) {
|
||||
ctx := context.Background()
|
||||
runAsUserInt64 := int64(1)
|
||||
for _, tc := range []struct {
|
||||
namespace string
|
||||
inPodSpec v1.PodSpec
|
||||
podName string
|
||||
parentPod *v1.Pod
|
||||
errChecker Checker
|
||||
}{
|
||||
{
|
||||
parentPod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "podName",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeSelector: map[string]string{
|
||||
"node": "selector",
|
||||
},
|
||||
Tolerations: []v1.Toleration{
|
||||
{Value: "toleration"},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{Image: "Image"},
|
||||
},
|
||||
SecurityContext: &v1.PodSecurityContext{
|
||||
RunAsUser: &runAsUserInt64,
|
||||
},
|
||||
},
|
||||
},
|
||||
namespace: "ns",
|
||||
podName: "podName",
|
||||
inPodSpec: v1.PodSpec{Containers: []v1.Container{{Name: "container1"}}},
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{
|
||||
parentPod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "podName",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeSelector: map[string]string{
|
||||
"node": "selector",
|
||||
},
|
||||
Tolerations: []v1.Toleration{
|
||||
{Value: "toleration"},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{Image: "Image"},
|
||||
},
|
||||
SecurityContext: &v1.PodSecurityContext{
|
||||
RunAsUser: &runAsUserInt64,
|
||||
},
|
||||
},
|
||||
},
|
||||
namespace: "ns",
|
||||
podName: "podName",
|
||||
inPodSpec: v1.PodSpec{Containers: []v1.Container{{Name: "container1"}, {Name: "container2"}}},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{
|
||||
namespace: "ns",
|
||||
podName: "podName",
|
||||
inPodSpec: v1.PodSpec{Containers: []v1.Container{{Name: "container1"}}},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{
|
||||
namespace: "ns",
|
||||
podName: "",
|
||||
inPodSpec: v1.PodSpec{Containers: []v1.Container{{Name: "container1"}}},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
} {
|
||||
tempHostname := os.Getenv(PodNameEnvKey)
|
||||
defer func() {
|
||||
os.Setenv(PodNameEnvKey, tempHostname)
|
||||
}()
|
||||
os.Setenv(PodNameEnvKey, tc.podName)
|
||||
|
||||
cli := fake.NewSimpleClientset()
|
||||
if tc.parentPod != nil {
|
||||
cli = fake.NewSimpleClientset(tc.parentPod)
|
||||
}
|
||||
psm := podSpecMerger{cli}
|
||||
outPodSpec, err := psm.mergePodSpec(ctx, tc.namespace, tc.inPodSpec)
|
||||
c.Check(err, tc.errChecker)
|
||||
if err == nil {
|
||||
c.Assert(outPodSpec, Not(DeepEquals), tc.inPodSpec)
|
||||
c.Assert(outPodSpec.NodeSelector, DeepEquals, tc.parentPod.Spec.NodeSelector)
|
||||
c.Assert(outPodSpec.Tolerations, DeepEquals, tc.parentPod.Spec.Tolerations)
|
||||
c.Assert(outPodSpec.Containers[0].Image, Equals, tc.parentPod.Spec.Containers[0].Image)
|
||||
c.Assert(outPodSpec.SecurityContext, DeepEquals, tc.parentPod.Spec.SecurityContext)
|
||||
}
|
||||
os.Setenv(PodNameEnvKey, tempHostname)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FIOTestSuite) TestGetPodNamespace(c *C) {
|
||||
os.Setenv(PodNamespaceEnvKey, "ns")
|
||||
ns := GetPodNamespace()
|
||||
c.Assert(ns, Equals, "ns")
|
||||
os.Unsetenv(PodNamespaceEnvKey)
|
||||
}
|
||||
|
||||
type fakePodReadyChecker struct {
|
||||
prcErr error
|
||||
}
|
||||
|
@ -1082,14 +831,6 @@ func (f *fakePodReadyChecker) waitForPodReady(ctx context.Context, namespace, na
|
|||
return f.prcErr
|
||||
}
|
||||
|
||||
type fakePodSpecMerger struct {
|
||||
psmErr error
|
||||
}
|
||||
|
||||
func (fm *fakePodSpecMerger) mergePodSpec(ctx context.Context, namespace string, podSpec v1.PodSpec) (v1.PodSpec, error) {
|
||||
return podSpec, fm.psmErr
|
||||
}
|
||||
|
||||
type fakeKubeExecutor struct {
|
||||
keErr error
|
||||
keStdOut string
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
package kubestr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/kastenhq/kubestr/pkg/fio"
|
||||
)
|
||||
|
||||
func (p *Kubestr) FIO(ctx context.Context, storageClass, configMap, jobName string) *TestOutput {
|
||||
testName := "FIO test results"
|
||||
fioResult, err := p.fio.RunFio(ctx, &fio.RunFIOArgs{
|
||||
StorageClass: storageClass,
|
||||
ConfigMapName: configMap,
|
||||
JobName: jobName,
|
||||
})
|
||||
if err != nil {
|
||||
return makeTestOutput(testName, StatusError, err.Error(), nil)
|
||||
}
|
||||
return makeTestOutput(testName, StatusOK, fmt.Sprintf("\n%s\n", fioResult), fioResult)
|
||||
}
|
|
@ -34,9 +34,9 @@ func (p *Kubestr) validateK8sVersion() *TestOutput {
|
|||
testName := "Kubernetes Version Check"
|
||||
version, err := p.validateK8sVersionHelper()
|
||||
if err != nil {
|
||||
return makeTestOutput(testName, StatusError, err.Error(), nil)
|
||||
return MakeTestOutput(testName, StatusError, err.Error(), nil)
|
||||
}
|
||||
return makeTestOutput(testName, StatusOK, fmt.Sprintf("Valid kubernetes version (%s)", version.String()), version)
|
||||
return MakeTestOutput(testName, StatusOK, fmt.Sprintf("Valid kubernetes version (%s)", version.String()), version)
|
||||
}
|
||||
|
||||
// getK8sVersion fetches the k8s vesion
|
||||
|
@ -75,9 +75,9 @@ func (p *Kubestr) validateRBAC() *TestOutput {
|
|||
//fmt.Println(" Checking if Kubernetes RBAC is enabled:")
|
||||
group, err := p.validateRBACHelper()
|
||||
if err != nil {
|
||||
return makeTestOutput(testName, StatusError, err.Error(), nil)
|
||||
return MakeTestOutput(testName, StatusError, err.Error(), nil)
|
||||
}
|
||||
return makeTestOutput(testName, StatusOK, "Kubernetes RBAC is enabled", *group)
|
||||
return MakeTestOutput(testName, StatusOK, "Kubernetes RBAC is enabled", *group)
|
||||
}
|
||||
|
||||
// getRBAC runs the Rbac test
|
||||
|
@ -98,9 +98,9 @@ func (p *Kubestr) validateAggregatedLayer() *TestOutput {
|
|||
testName := "Aggregated Layer Check"
|
||||
resourceList, err := p.validateAggregatedLayerHelper()
|
||||
if err != nil {
|
||||
makeTestOutput(testName, StatusError, err.Error(), nil)
|
||||
MakeTestOutput(testName, StatusError, err.Error(), nil)
|
||||
}
|
||||
return makeTestOutput(testName, StatusOK, "The Kubernetes Aggregated Layer is enabled", resourceList)
|
||||
return MakeTestOutput(testName, StatusOK, "The Kubernetes Aggregated Layer is enabled", resourceList)
|
||||
}
|
||||
|
||||
// getAggregatedLayer checks the aggregated API layer
|
||||
|
|
|
@ -18,7 +18,7 @@ type Kubestr struct {
|
|||
sdsfgValidator snapshotDataSourceFG
|
||||
storageClassList *sv1.StorageClassList
|
||||
volumeSnapshotClassList *unstructured.UnstructuredList
|
||||
fio fio.FIO
|
||||
Fio fio.FIO
|
||||
}
|
||||
|
||||
const Logo = `
|
||||
|
@ -48,7 +48,7 @@ func NewKubestr() (*Kubestr, error) {
|
|||
cli: cli,
|
||||
dynCli: dynCli,
|
||||
},
|
||||
fio: &fio.FIOrunner{
|
||||
Fio: &fio.FIOrunner{
|
||||
Cli: cli,
|
||||
},
|
||||
}, nil
|
||||
|
|
|
@ -93,7 +93,7 @@ func (t *TestOutput) Print() {
|
|||
}
|
||||
}
|
||||
|
||||
func makeTestOutput(testname string, code StatusCode, mesg string, raw interface{}) *TestOutput {
|
||||
func MakeTestOutput(testname string, code StatusCode, mesg string, raw interface{}) *TestOutput {
|
||||
return &TestOutput{
|
||||
TestName: testname,
|
||||
Status: []Status{makeStatus(code, mesg, nil)},
|
||||
|
|
Loading…
Reference in a new issue