Skip to content

Commit

Permalink
Delete pod E2e test
Browse files Browse the repository at this point in the history
  • Loading branch information
dkoshkin committed Jan 23, 2019
1 parent 9c7cc41 commit 5cd720f
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 12 deletions.
4 changes: 2 additions & 2 deletions tests/e2e/driver/ebs_csi_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ func (d *ebsCSIDriver) GetDynamicProvisionStorageClass(parameters map[string]str

func (d *ebsCSIDriver) GetPersistentVolume(volumeID string, fsType string, size string, reclaimPolicy *v1.PersistentVolumeReclaimPolicy, namespace string) *v1.PersistentVolume {
provisioner := d.driverName
generatedName := fmt.Sprintf("%s-%s-preprovsioned-pv-", namespace, provisioner)
generateName := fmt.Sprintf("%s-%s-preprovsioned-pv-", namespace, provisioner)
// Default to Retain ReclaimPolicy for pre-provisioned volumes
pvReclaimPolicy := v1.PersistentVolumeReclaimRetain
if reclaimPolicy != nil {
pvReclaimPolicy = *reclaimPolicy
}
return &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
GenerateName: generatedName,
GenerateName: generateName,
Namespace: namespace,
// TODO remove if https://github.com/kubernetes-csi/external-provisioner/issues/202 is fixed
Annotations: map[string]string{
Expand Down
26 changes: 26 additions & 0 deletions tests/e2e/dynamic_provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,32 @@ var _ = Describe("[ebs-csi-e2e] [single-az] Dynamic Provisioning", func() {
}
test.Run(cs, ns)
})

It("should create a deployment object, write and read to it, delete the pod and write and read to it again", func() {
pod := testsuites.PodDetails{
Cmd: "echo 'hello world' >> /mnt/test-1/data && while true; do sleep 1; done",
Volumes: []testsuites.VolumeDetails{
{
VolumeType: awscloud.VolumeTypeGP2,
FSType: ebscsidriver.FSTypeExt3,
ClaimSize: driver.MinimumSizeForVolumeType(awscloud.VolumeTypeGP2),
VolumeMount: testsuites.VolumeMountDetails{
NameGenerate: "test-volume-",
MountPathGenerate: "/mnt/test-",
},
},
},
}
test := testsuites.DynamicallyProvisionedDeletePodTest{
CSIDriver: ebsDriver,
Pod: pod,
PodCheck: &testsuites.PodExecCheck{
Cmd: []string{"cat", "/mnt/test-1/data"},
ExpectedString: "hello world\nhello world\n", // pod will be restarted so expect to see 2 instances of string
},
}
test.Run(cs, ns)
})
})

var _ = Describe("[ebs-csi-e2e] [multi-az] Dynamic Provisioning", func() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (t *DynamicallyProvisionedCollocatedPodTest) Run(client clientset.Interface
By("deploying the pod")
tpod.Create()
podCleanup = append(podCleanup, tpod.Cleanup)
By("checking that the pods command exits with no error")
By("checking that the pod is running")
tpod.WaitForRunning()
nodeName = tpod.pod.Spec.NodeName
}
Expand Down
61 changes: 61 additions & 0 deletions tests/e2e/testsuites/dynamically_provisioned_delete_pod_tester.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package testsuites

import (
"github.com/kubernetes-sigs/aws-ebs-csi-driver/tests/e2e/driver"
. "github.com/onsi/ginkgo"
"k8s.io/api/core/v1"
clientset "k8s.io/client-go/kubernetes"
)

// DynamicallyProvisionedDeletePodTest will provision required StorageClass and Deployment
// Testing if the Pod can write and read to mounted volumes
// Deleting a pod, and again testing if the Pod can write and read to mounted volumes
type DynamicallyProvisionedDeletePodTest struct {
CSIDriver driver.DynamicPVTestDriver
Pod PodDetails
PodCheck *PodExecCheck
}

type PodExecCheck struct {
Cmd []string
ExpectedString string
}

func (t *DynamicallyProvisionedDeletePodTest) Run(client clientset.Interface, namespace *v1.Namespace) {
tDeployment, cleanup := t.Pod.SetupDeployment(client, namespace, t.CSIDriver)
// defer must be called here for resources not get removed before using them
for i := range cleanup {
defer cleanup[i]()
}

By("deploying the deployment")
tDeployment.Create()

By("checking that the pod is running")
tDeployment.WaitForPodReady()

By("deleting the pod for deployment")
tDeployment.DeletePodAndWait()

By("checking again that the pod is running")
tDeployment.WaitForPodReady()

if t.PodCheck != nil {
By("checking pod exec")
tDeployment.Exec(t.PodCheck.Cmd, t.PodCheck.ExpectedString)
}
}
21 changes: 21 additions & 0 deletions tests/e2e/testsuites/specs.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ func (pod *PodDetails) SetupWithPreProvisionedVolumes(client clientset.Interface
return tpod, cleanupFuncs
}

func (pod *PodDetails) SetupDeployment(client clientset.Interface, namespace *v1.Namespace, csiDriver driver.DynamicPVTestDriver) (*TestDeployment, []func()) {
cleanupFuncs := make([]func(), 0)
volume := pod.Volumes[0]
By("setting up the StorageClass")
storageClass := csiDriver.GetDynamicProvisionStorageClass(driver.GetParameters(volume.VolumeType, volume.FSType, volume.Encrypted), volume.MountOptions, volume.ReclaimPolicy, volume.VolumeBindingMode, volume.AllowedTopologyValues, namespace.Name)
tsc := NewTestStorageClass(client, namespace, storageClass)
createdStorageClass := tsc.Create()
cleanupFuncs = append(cleanupFuncs, tsc.Cleanup)
By("setting up the PVC")
tpvc := NewTestPersistentVolumeClaim(client, namespace, volume.ClaimSize, &createdStorageClass)
tpvc.Create()
tpvc.WaitForBound()
tpvc.ValidateProvisionedPersistentVolume()
cleanupFuncs = append(cleanupFuncs, tpvc.Cleanup)
By("setting up the Deployment")
tDeployment := NewTestDeployment(client, namespace, pod.Cmd, tpvc.persistentVolumeClaim, fmt.Sprintf("%s%d", volume.VolumeMount.NameGenerate, 1), fmt.Sprintf("%s%d", volume.VolumeMount.MountPathGenerate, 1), volume.VolumeMount.ReadOnly)

cleanupFuncs = append(cleanupFuncs, tDeployment.Cleanup)
return tDeployment, cleanupFuncs
}

func (volume *VolumeDetails) SetupDynamicPersistentVolumeClaim(client clientset.Interface, namespace *v1.Namespace, csiDriver driver.DynamicPVTestDriver) (*TestPersistentVolumeClaim, []func()) {
cleanupFuncs := make([]func(), 0)
By("setting up the StorageClass")
Expand Down
154 changes: 145 additions & 9 deletions tests/e2e/testsuites/testsuites.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ package testsuites
import (
"context"
"fmt"
"math/rand"
"time"

awscloud "github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apps "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
Expand All @@ -32,8 +35,13 @@ import (
)

const (
execTimeout = 10 * time.Second
// How often to poll pods.
slowPodPoll = 2 * time.Second
// Some pods can take much longer to get ready due to volume attach/detach latency.
slowPodStartTimeout = 15 * time.Minute
// How long to wait for the pod to be deleted
deletePodMaxWait = 5 * time.Minute
// Description that will printed during tests
failedConditionDescription = "Error status code"
)
Expand Down Expand Up @@ -243,6 +251,126 @@ func (t *TestPersistentVolumeClaim) DeleteBackingVolume(cloud awscloud.Cloud) {
}
}

type TestDeployment struct {
client clientset.Interface
deployment *apps.Deployment
namespace *v1.Namespace
podName string
}

func NewTestDeployment(c clientset.Interface, ns *v1.Namespace, command string, pvc *v1.PersistentVolumeClaim, volumeName, mountPath string, readOnly bool) *TestDeployment {
generateName := "ebs-volume-tester-"
selectorValue := fmt.Sprintf("%s%d", generateName, rand.Int())
replicas := int32(1)
return &TestDeployment{
client: c,
namespace: ns,
deployment: &apps.Deployment{
ObjectMeta: metav1.ObjectMeta{
GenerateName: generateName,
},
Spec: apps.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": selectorValue},
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": selectorValue},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "volume-tester",
Image: imageutils.GetE2EImage(imageutils.BusyBox),
Command: []string{"/bin/sh"},
Args: []string{"-c", command},
VolumeMounts: []v1.VolumeMount{
{
Name: volumeName,
MountPath: mountPath,
ReadOnly: readOnly,
},
},
},
},
RestartPolicy: v1.RestartPolicyAlways,
Volumes: []v1.Volume{
{
Name: volumeName,
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: pvc.Name,
},
},
},
},
},
},
},
},
}
}

func (t *TestDeployment) Create() {
var err error
t.deployment, err = t.client.AppsV1().Deployments(t.namespace.Name).Create(t.deployment)
framework.ExpectNoError(err)
err = framework.WaitForDeploymentComplete(t.client, t.deployment)
framework.ExpectNoError(err)
pods, err := framework.GetPodsForDeployment(t.client, t.deployment)
framework.ExpectNoError(err)
// always get first pod as there should only be one
t.podName = pods.Items[0].Name
}

func (t *TestDeployment) WaitForPodReady() {
pods, err := framework.GetPodsForDeployment(t.client, t.deployment)
framework.ExpectNoError(err)
// always get first pod as there should only be one
pod := pods.Items[0]
t.podName = pod.Name
err = framework.WaitForPodRunningInNamespace(t.client, &pod)
framework.ExpectNoError(err)
}

func (t *TestDeployment) Exec(command []string, expectedString string) {
_, err := framework.LookForStringInPodExec(t.namespace.Name, t.podName, command, expectedString, execTimeout)
framework.ExpectNoError(err)
}

func (t *TestDeployment) DeletePodAndWait() {
framework.Logf("Deleting pod %q in namespace %q", t.podName, t.namespace.Name)
err := t.client.CoreV1().Pods(t.namespace.Name).Delete(t.podName, nil)
if err != nil {
if !apierrs.IsNotFound(err) {
framework.ExpectNoError(fmt.Errorf("pod %q Delete API error: %v", t.podName, err))
}
return
}
//framework.Logf("Wait up to %v for pod %q to be fully deleted", deletePodMaxWait, t.podName)
//err = framework.WaitForPodNoLongerRunningInNamespace(t.client, t.podName, t.namespace.Name)
//if err != nil {
// framework.ExpectNoError(fmt.Errorf("pod %q was not deleted: %v", t.podName, err))
//}
}

func (t *TestDeployment) Cleanup() {
framework.Logf("deleting Deployment %q/%q", t.namespace.Name, t.deployment.Name)
body, err := t.Logs()
if err != nil {
framework.Logf("Error getting logs for pod %s: %v", t.podName, err)
} else {
framework.Logf("Pod %s has the following logs: %s", t.podName, body)
}
err = t.client.AppsV1().Deployments(t.namespace.Name).Delete(t.deployment.Name, nil)
framework.ExpectNoError(err)
}

func (t *TestDeployment) Logs() ([]byte, error) {
return podLogs(t.client, t.podName, t.namespace.Name)
}

type TestPod struct {
client clientset.Interface
pod *v1.Pod
Expand All @@ -255,7 +383,7 @@ func NewTestPod(c clientset.Interface, ns *v1.Namespace, command string) *TestPo
namespace: ns,
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "pvc-volume-tester-",
GenerateName: "ebs-volume-tester-",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
Expand Down Expand Up @@ -333,17 +461,25 @@ func (t *TestPod) SetNodeSelector(nodeSelector map[string]string) {
t.pod.Spec.NodeSelector = nodeSelector
}

func (p *TestPod) Cleanup() {
framework.Logf("deleting Pod %q/%q", p.namespace.Name, p.pod.Name)
body, err := p.Logs()
func (t *TestPod) Cleanup() {
cleanupPodOrFail(t.client, t.pod.Name, t.namespace.Name)
}

func (t *TestPod) Logs() ([]byte, error) {
return podLogs(t.client, t.pod.Name, t.namespace.Name)
}

func cleanupPodOrFail(client clientset.Interface, name, namespace string) {
framework.Logf("deleting Pod %q/%q", namespace, name)
body, err := podLogs(client, name, namespace)
if err != nil {
framework.Logf("Error getting logs for pod %s: %v", p.pod.Name, err)
framework.Logf("Error getting logs for pod %s: %v", name, err)
} else {
framework.Logf("Pod %s has the following logs: %s", p.pod.Name, body)
framework.Logf("Pod %s has the following logs: %s", name, body)
}
framework.DeletePodOrFail(p.client, p.namespace.Name, p.pod.Name)
framework.DeletePodOrFail(client, namespace, name)
}

func (p *TestPod) Logs() ([]byte, error) {
return p.client.CoreV1().Pods(p.namespace.Name).GetLogs(p.pod.Name, &v1.PodLogOptions{}).Do().Raw()
func podLogs(client clientset.Interface, name, namespace string) ([]byte, error) {
return client.CoreV1().Pods(namespace).GetLogs(name, &v1.PodLogOptions{}).Do().Raw()
}

0 comments on commit 5cd720f

Please sign in to comment.