2015-02-20 18:01:33 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2015 The Kubernetes Authors .
2015-02-20 18:01:33 +00:00
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 .
* /
2017-03-19 19:25:40 +00:00
package storage
2015-02-20 18:01:33 +00:00
import (
"fmt"
2015-10-19 21:08:35 +00:00
mathrand "math/rand"
2015-02-20 18:01:33 +00:00
"strings"
"time"
2015-12-15 09:18:00 +00:00
"google.golang.org/api/googleapi"
2016-03-12 10:57:59 +00:00
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
2015-08-05 22:05:17 +00:00
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
2017-06-22 18:24:23 +00:00
"k8s.io/api/core/v1"
2017-10-19 07:11:58 +00:00
policy "k8s.io/api/policy/v1beta1"
2017-01-25 13:13:07 +00:00
"k8s.io/apimachinery/pkg/api/resource"
2017-01-11 14:09:48 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
2017-01-24 14:35:22 +00:00
"k8s.io/apimachinery/pkg/util/uuid"
2017-10-19 07:11:58 +00:00
"k8s.io/apimachinery/pkg/util/wait"
2017-10-17 18:56:09 +00:00
clientset "k8s.io/client-go/kubernetes"
2017-06-23 20:56:37 +00:00
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
2017-07-11 01:10:34 +00:00
"k8s.io/kubernetes/pkg/api/testapi"
2016-04-07 17:21:31 +00:00
"k8s.io/kubernetes/test/e2e/framework"
2017-08-29 08:32:08 +00:00
imageutils "k8s.io/kubernetes/test/utils/image"
2015-02-20 18:01:33 +00:00
)
2015-07-27 02:26:43 +00:00
const (
gcePDDetachTimeout = 10 * time . Minute
gcePDDetachPollTime = 10 * time . Second
2017-06-24 01:56:34 +00:00
nodeStatusTimeout = 10 * time . Minute
2016-05-30 02:22:22 +00:00
nodeStatusPollTime = 1 * time . Second
2017-10-19 07:11:58 +00:00
podEvictTimeout = 2 * time . Minute
2016-10-11 20:22:06 +00:00
maxReadRetry = 3
2017-10-13 06:36:51 +00:00
minNodes = 2
2015-07-27 02:26:43 +00:00
)
2017-07-11 21:55:45 +00:00
var _ = SIGDescribe ( "Pod Disks" , func ( ) {
2015-02-20 18:01:33 +00:00
var (
2017-10-19 07:11:58 +00:00
ns string
cs clientset . Interface
2016-11-18 20:55:17 +00:00
podClient v1core . PodInterface
nodeClient v1core . NodeInterface
2016-07-16 06:10:29 +00:00
host0Name types . NodeName
host1Name types . NodeName
2016-12-20 20:23:56 +00:00
nodes * v1 . NodeList
2015-02-20 18:01:33 +00:00
)
2016-04-07 17:21:31 +00:00
f := framework . NewDefaultFramework ( "pod-disks" )
2015-02-20 18:01:33 +00:00
BeforeEach ( func ( ) {
2017-10-13 06:36:51 +00:00
framework . SkipUnlessNodeCountIsAtLeast ( minNodes )
2017-10-19 07:11:58 +00:00
cs = f . ClientSet
ns = f . Namespace . Name
2015-06-22 21:14:54 +00:00
2017-10-19 07:11:58 +00:00
podClient = cs . Core ( ) . Pods ( ns )
nodeClient = cs . Core ( ) . Nodes ( )
nodes = framework . GetReadySchedulableNodesOrDie ( cs )
2017-10-13 06:36:51 +00:00
Expect ( len ( nodes . Items ) ) . To ( BeNumerically ( ">=" , minNodes ) , fmt . Sprintf ( "Requires at least %d nodes" , minNodes ) )
2016-07-16 06:10:29 +00:00
host0Name = types . NodeName ( nodes . Items [ 0 ] . ObjectMeta . Name )
host1Name = types . NodeName ( nodes . Items [ 1 ] . ObjectMeta . Name )
2015-07-20 02:00:10 +00:00
2015-10-19 21:08:35 +00:00
mathrand . Seed ( time . Now ( ) . UTC ( ) . UnixNano ( ) )
2015-02-20 18:01:33 +00:00
} )
2017-10-17 03:17:25 +00:00
Context ( "schedule pods each with a PD, delete pod and verify detach [Slow]" , func ( ) {
const (
podDefaultGrace = "default (30s)"
podImmediateGrace = "immediate (0s)"
)
var readOnlyMap = map [ bool ] string {
true : "read-only" ,
false : "RW" ,
}
2017-10-13 06:36:51 +00:00
type testT struct {
descr string // It description
2017-10-17 03:17:25 +00:00
readOnly bool // true means pd is read-only
2017-10-13 06:36:51 +00:00
deleteOpt * metav1 . DeleteOptions // pod delete option
}
tests := [ ] testT {
{
2017-10-17 03:17:25 +00:00
descr : podImmediateGrace ,
readOnly : false ,
deleteOpt : metav1 . NewDeleteOptions ( 0 ) ,
} ,
{
descr : podDefaultGrace ,
readOnly : false ,
deleteOpt : & metav1 . DeleteOptions { } ,
} ,
{
descr : podImmediateGrace ,
readOnly : true ,
2017-10-13 06:36:51 +00:00
deleteOpt : metav1 . NewDeleteOptions ( 0 ) ,
} ,
{
2017-10-17 03:17:25 +00:00
descr : podDefaultGrace ,
readOnly : true ,
deleteOpt : & metav1 . DeleteOptions { } ,
2017-10-13 06:36:51 +00:00
} ,
}
2016-07-12 00:58:25 +00:00
2017-10-13 06:36:51 +00:00
for _ , t := range tests {
2017-10-16 21:49:04 +00:00
podDelOpt := t . deleteOpt
2017-10-17 03:17:25 +00:00
readOnly := t . readOnly
readOnlyTxt := readOnlyMap [ readOnly ]
It ( fmt . Sprintf ( "for %s PD with pod delete grace period of %q" , readOnlyTxt , t . descr ) , func ( ) {
2017-10-13 06:36:51 +00:00
framework . SkipUnlessProviderIs ( "gce" , "gke" , "aws" )
2017-10-17 03:17:25 +00:00
if readOnly {
framework . SkipIfProviderIs ( "aws" )
}
2017-10-13 06:36:51 +00:00
By ( "creating PD" )
diskName , err := framework . CreatePDWithRetry ( )
framework . ExpectNoError ( err , "Error creating PD" )
2017-10-17 03:17:25 +00:00
var fmtPod * v1 . Pod
if readOnly {
// if all test pods are RO then need a RW pod to format pd
By ( "creating RW fmt Pod to ensure PD is formatted" )
fmtPod = testPDPod ( [ ] string { diskName } , host0Name , false , 1 )
_ , err = podClient . Create ( fmtPod )
framework . ExpectNoError ( err , "Failed to create fmtPod" )
framework . ExpectNoError ( f . WaitForPodRunningSlow ( fmtPod . Name ) )
By ( "deleting the fmtPod" )
framework . ExpectNoError ( podClient . Delete ( fmtPod . Name , metav1 . NewDeleteOptions ( 0 ) ) , "Failed to delete fmtPod" )
framework . Logf ( "deleted fmtPod %q" , fmtPod . Name )
By ( "waiting for PD to detach" )
framework . ExpectNoError ( waitForPDDetach ( diskName , host0Name ) )
}
// prepare to create two test pods on separate nodes
host0Pod := testPDPod ( [ ] string { diskName } , host0Name , readOnly , 1 )
host1Pod := testPDPod ( [ ] string { diskName } , host1Name , readOnly , 1 )
2017-10-13 06:36:51 +00:00
defer func ( ) {
// Teardown should do nothing unless test failed
By ( "defer: cleaning up PD-RW test environment" )
framework . Logf ( "defer cleanup errors can usually be ignored" )
2017-10-17 18:56:09 +00:00
if fmtPod != nil {
2017-10-17 03:17:25 +00:00
podClient . Delete ( fmtPod . Name , podDelOpt )
}
2017-10-13 06:36:51 +00:00
podClient . Delete ( host0Pod . Name , podDelOpt )
podClient . Delete ( host1Pod . Name , podDelOpt )
detachAndDeletePDs ( diskName , [ ] types . NodeName { host0Name , host1Name } )
} ( )
2017-10-16 21:49:04 +00:00
By ( "creating host0Pod on node0" )
2017-10-13 06:36:51 +00:00
_ , err = podClient . Create ( host0Pod )
framework . ExpectNoError ( err , fmt . Sprintf ( "Failed to create host0Pod: %v" , err ) )
framework . ExpectNoError ( f . WaitForPodRunningSlow ( host0Pod . Name ) )
2017-10-13 20:02:54 +00:00
framework . Logf ( "host0Pod: %q, node0: %q" , host0Pod . Name , host0Name )
2017-10-13 06:36:51 +00:00
2017-10-17 03:17:25 +00:00
var containerName , testFile , testFileContents string
if ! readOnly {
By ( "writing content to host0Pod on node0" )
containerName = "mycontainer"
testFile = "/testpd1/tracker"
testFileContents = fmt . Sprintf ( "%v" , mathrand . Int ( ) )
framework . ExpectNoError ( f . WriteFileViaContainer ( host0Pod . Name , containerName , testFile , testFileContents ) )
framework . Logf ( "wrote %q to file %q in pod %q on node %q" , testFileContents , testFile , host0Pod . Name , host0Name )
By ( "verifying PD is present in node0's VolumeInUse list" )
framework . ExpectNoError ( waitForPDInVolumesInUse ( nodeClient , diskName , host0Name , nodeStatusTimeout , true /* shouldExist */ ) )
By ( "deleting host0Pod" ) // delete this pod before creating next pod
framework . ExpectNoError ( podClient . Delete ( host0Pod . Name , podDelOpt ) , "Failed to delete host0Pod" )
framework . Logf ( "deleted host0Pod %q" , host0Pod . Name )
}
2017-10-13 06:36:51 +00:00
By ( "creating host1Pod on node1" )
_ , err = podClient . Create ( host1Pod )
framework . ExpectNoError ( err , "Failed to create host1Pod" )
framework . ExpectNoError ( f . WaitForPodRunningSlow ( host1Pod . Name ) )
2017-10-13 20:02:54 +00:00
framework . Logf ( "host1Pod: %q, node1: %q" , host1Pod . Name , host1Name )
2017-10-13 06:36:51 +00:00
2017-10-17 03:17:25 +00:00
if readOnly {
By ( "deleting host0Pod" )
framework . ExpectNoError ( podClient . Delete ( host0Pod . Name , podDelOpt ) , "Failed to delete host0Pod" )
framework . Logf ( "deleted host0Pod %q" , host0Pod . Name )
} else {
By ( "verifying PD contents in host1Pod" )
verifyPDContentsViaContainer ( f , host1Pod . Name , containerName , map [ string ] string { testFile : testFileContents } )
framework . Logf ( "verified PD contents in pod %q" , host1Pod . Name )
By ( "verifying PD is removed from node0" )
framework . ExpectNoError ( waitForPDInVolumesInUse ( nodeClient , diskName , host0Name , nodeStatusTimeout , false /* shouldExist */ ) )
framework . Logf ( "PD %q removed from node %q's VolumeInUse list" , diskName , host1Pod . Name )
}
2017-10-13 06:36:51 +00:00
By ( "deleting host1Pod" )
framework . ExpectNoError ( podClient . Delete ( host1Pod . Name , podDelOpt ) , "Failed to delete host1Pod" )
2017-10-13 20:02:54 +00:00
framework . Logf ( "deleted host1Pod %q" , host1Pod . Name )
2017-10-13 06:36:51 +00:00
By ( "Test completed successfully, waiting for PD to detach from both nodes" )
waitForPDDetach ( diskName , host0Name )
waitForPDDetach ( diskName , host1Name )
} )
}
2016-06-28 20:32:57 +00:00
} )
2017-10-13 06:36:51 +00:00
Context ( "schedule a pod w/ RW PD(s) mounted to 1 or more containers, write to PD, verify content, delete pod, and repeat in rapid succession [Slow]" , func ( ) {
var diskNames [ ] string
type testT struct {
numContainers int
numPDs int
2017-10-17 18:56:09 +00:00
repeatCnt int
2017-10-13 06:36:51 +00:00
}
tests := [ ] testT {
{
numContainers : 4 ,
numPDs : 1 ,
2017-10-17 18:56:09 +00:00
repeatCnt : 3 ,
2017-10-13 06:36:51 +00:00
} ,
{
numContainers : 1 ,
numPDs : 2 ,
2017-10-17 18:56:09 +00:00
repeatCnt : 3 ,
2017-10-13 06:36:51 +00:00
} ,
2015-09-23 22:08:11 +00:00
}
2016-07-12 00:58:25 +00:00
2017-10-13 06:36:51 +00:00
for _ , t := range tests {
numPDs := t . numPDs
numContainers := t . numContainers
It ( fmt . Sprintf ( "using %d containers and %d PDs" , numContainers , numPDs ) , func ( ) {
framework . SkipUnlessProviderIs ( "gce" , "gke" , "aws" )
var host0Pod * v1 . Pod
var err error
fileAndContentToVerify := make ( map [ string ] string )
By ( fmt . Sprintf ( "creating %d PD(s)" , numPDs ) )
for i := 0 ; i < numPDs ; i ++ {
name , err := framework . CreatePDWithRetry ( )
framework . ExpectNoError ( err , fmt . Sprintf ( "Error creating PD %d" , i ) )
diskNames = append ( diskNames , name )
}
defer func ( ) {
// Teardown should do nothing unless test failed.
By ( "defer: cleaning up PD-RW test environment" )
framework . Logf ( "defer cleanup errors can usually be ignored" )
if host0Pod != nil {
podClient . Delete ( host0Pod . Name , metav1 . NewDeleteOptions ( 0 ) )
}
for _ , diskName := range diskNames {
detachAndDeletePDs ( diskName , [ ] types . NodeName { host0Name } )
}
} ( )
2017-10-17 18:56:09 +00:00
for i := 0 ; i < t . repeatCnt ; i ++ { // "rapid" repeat loop
2017-10-13 06:36:51 +00:00
framework . Logf ( "PD Read/Writer Iteration #%v" , i )
By ( fmt . Sprintf ( "creating host0Pod with %d containers on node0" , numContainers ) )
host0Pod = testPDPod ( diskNames , host0Name , false /* readOnly */ , numContainers )
_ , err = podClient . Create ( host0Pod )
framework . ExpectNoError ( err , fmt . Sprintf ( "Failed to create host0Pod: %v" , err ) )
framework . ExpectNoError ( f . WaitForPodRunningSlow ( host0Pod . Name ) )
By ( fmt . Sprintf ( "writing %d file(s) via a container" , numPDs ) )
containerName := "mycontainer"
if numContainers > 1 {
containerName = fmt . Sprintf ( "mycontainer%v" , mathrand . Intn ( numContainers ) + 1 )
}
for x := 1 ; x <= numPDs ; x ++ {
testFile := fmt . Sprintf ( "/testpd%d/tracker%d" , x , i )
testFileContents := fmt . Sprintf ( "%v" , mathrand . Int ( ) )
fileAndContentToVerify [ testFile ] = testFileContents
framework . ExpectNoError ( f . WriteFileViaContainer ( host0Pod . Name , containerName , testFile , testFileContents ) )
2017-10-13 20:02:54 +00:00
framework . Logf ( "wrote %q to file %q in pod %q (container %q) on node %q" , testFileContents , testFile , host0Pod . Name , containerName , host0Name )
2017-10-13 06:36:51 +00:00
}
By ( "verifying PD contents via a container" )
if numContainers > 1 {
containerName = fmt . Sprintf ( "mycontainer%v" , mathrand . Intn ( numContainers ) + 1 )
}
verifyPDContentsViaContainer ( f , host0Pod . Name , containerName , fileAndContentToVerify )
By ( "deleting host0Pod" )
framework . ExpectNoError ( podClient . Delete ( host0Pod . Name , metav1 . NewDeleteOptions ( 0 ) ) , "Failed to delete host0Pod" )
}
By ( fmt . Sprintf ( "Test completed successfully, waiting for %d PD(s) to detach from node0" , numPDs ) )
for _ , diskName := range diskNames {
waitForPDDetach ( diskName , host0Name )
}
} )
}
2015-09-23 22:08:11 +00:00
} )
2016-11-01 00:57:13 +00:00
2017-10-19 07:11:58 +00:00
Context ( "detach in a disrupted environment [Slow] [Disruptive]" , func ( ) {
2017-10-16 21:49:04 +00:00
const (
deleteNode = 1 // delete physical node
deleteNodeObj = 2 // delete node's api object only
2017-10-19 07:11:58 +00:00
evictPod = 3 // evict host0Pod on node0
2017-10-16 21:49:04 +00:00
)
type testT struct {
2017-10-19 07:11:58 +00:00
descr string // It description
disruptOp int // disruptive operation performed on target node
2017-10-16 21:49:04 +00:00
}
tests := [ ] testT {
{
2017-10-19 07:11:58 +00:00
descr : "node is deleted" ,
disruptOp : deleteNode ,
2017-10-16 21:49:04 +00:00
} ,
{
2017-10-19 07:11:58 +00:00
descr : "node's API object is deleted" ,
disruptOp : deleteNodeObj ,
} ,
{
descr : "pod is evicted" ,
disruptOp : evictPod ,
2017-10-16 21:49:04 +00:00
} ,
}
2016-11-01 00:57:13 +00:00
2017-10-16 21:49:04 +00:00
for _ , t := range tests {
2017-10-19 07:11:58 +00:00
disruptOp := t . disruptOp
2017-10-16 21:49:04 +00:00
It ( fmt . Sprintf ( "when %s" , t . descr ) , func ( ) {
framework . SkipUnlessProviderIs ( "gce" )
2017-10-17 18:56:09 +00:00
origNodeCnt := len ( nodes . Items ) // healhy nodes running kublet
2017-10-16 21:49:04 +00:00
By ( "creating a pd" )
diskName , err := framework . CreatePDWithRetry ( )
framework . ExpectNoError ( err , "Error creating a pd" )
2017-10-13 06:36:51 +00:00
2017-10-19 07:11:58 +00:00
targetNode := & nodes . Items [ 0 ] // for node delete ops
2017-10-16 21:49:04 +00:00
host0Pod := testPDPod ( [ ] string { diskName } , host0Name , false , 1 )
containerName := "mycontainer"
defer func ( ) {
By ( "defer: cleaning up PD-RW test env" )
framework . Logf ( "defer cleanup errors can usually be ignored" )
2017-10-19 07:11:58 +00:00
By ( "defer: delete host0Pod" )
podClient . Delete ( host0Pod . Name , metav1 . NewDeleteOptions ( 0 ) )
By ( "defer: detach and delete PDs" )
2017-10-16 21:49:04 +00:00
detachAndDeletePDs ( diskName , [ ] types . NodeName { host0Name } )
2017-10-19 07:11:58 +00:00
if disruptOp == deleteNode || disruptOp == deleteNodeObj {
if disruptOp == deleteNodeObj {
targetNode . ObjectMeta . SetResourceVersion ( "0" )
// need to set the resource version or else the Create() fails
By ( "defer: re-create host0 node object" )
_ , err := nodeClient . Create ( targetNode )
framework . ExpectNoError ( err , fmt . Sprintf ( "defer: Unable to re-create the deleted node object %q" , targetNode . Name ) )
}
By ( "defer: verify the number of ready nodes" )
numNodes := countReadyNodes ( cs , host0Name )
Expect ( numNodes ) . To ( Equal ( origNodeCnt ) , fmt . Sprintf ( "defer: Requires current node count (%d) to return to original node count (%d)" , numNodes , origNodeCnt ) )
2017-10-16 21:49:04 +00:00
}
} ( )
By ( "creating host0Pod on node0" )
_ , err = podClient . Create ( host0Pod )
framework . ExpectNoError ( err , fmt . Sprintf ( "Failed to create host0Pod: %v" , err ) )
2017-10-19 07:11:58 +00:00
By ( "waiting for host0Pod to be running" )
2017-10-16 21:49:04 +00:00
framework . ExpectNoError ( f . WaitForPodRunningSlow ( host0Pod . Name ) )
By ( "writing content to host0Pod" )
testFile := "/testpd1/tracker"
testFileContents := fmt . Sprintf ( "%v" , mathrand . Int ( ) )
framework . ExpectNoError ( f . WriteFileViaContainer ( host0Pod . Name , containerName , testFile , testFileContents ) )
framework . Logf ( "wrote %q to file %q in pod %q on node %q" , testFileContents , testFile , host0Pod . Name , host0Name )
By ( "verifying PD is present in node0's VolumeInUse list" )
framework . ExpectNoError ( waitForPDInVolumesInUse ( nodeClient , diskName , host0Name , nodeStatusTimeout , true /* should exist*/ ) )
2017-10-19 07:11:58 +00:00
if disruptOp == deleteNode {
2017-10-16 21:49:04 +00:00
By ( "getting gce instances" )
gceCloud , err := framework . GetGCECloud ( )
framework . ExpectNoError ( err , fmt . Sprintf ( "Unable to create gcloud client err=%v" , err ) )
output , err := gceCloud . ListInstanceNames ( framework . TestContext . CloudConfig . ProjectID , framework . TestContext . CloudConfig . Zone )
framework . ExpectNoError ( err , fmt . Sprintf ( "Unable to get list of node instances err=%v output=%s" , err , output ) )
Expect ( true , strings . Contains ( string ( output ) , string ( host0Name ) ) )
By ( "deleting host0" )
resp , err := gceCloud . DeleteInstance ( framework . TestContext . CloudConfig . ProjectID , framework . TestContext . CloudConfig . Zone , string ( host0Name ) )
framework . ExpectNoError ( err , fmt . Sprintf ( "Failed to delete host0Pod: err=%v response=%#v" , err , resp ) )
2017-10-19 07:11:58 +00:00
By ( "expecting host0 node to be re-created" )
numNodes := countReadyNodes ( cs , host0Name )
2017-10-17 18:56:09 +00:00
Expect ( numNodes ) . To ( Equal ( origNodeCnt ) , fmt . Sprintf ( "Requires current node count (%d) to return to original node count (%d)" , numNodes , origNodeCnt ) )
2017-10-16 21:49:04 +00:00
output , err = gceCloud . ListInstanceNames ( framework . TestContext . CloudConfig . ProjectID , framework . TestContext . CloudConfig . Zone )
framework . ExpectNoError ( err , fmt . Sprintf ( "Unable to get list of node instances err=%v output=%s" , err , output ) )
Expect ( false , strings . Contains ( string ( output ) , string ( host0Name ) ) )
2017-10-17 18:56:09 +00:00
2017-10-19 07:11:58 +00:00
} else if disruptOp == deleteNodeObj {
2017-10-16 21:49:04 +00:00
By ( "deleting host0's node api object" )
framework . ExpectNoError ( nodeClient . Delete ( string ( host0Name ) , metav1 . NewDeleteOptions ( 0 ) ) , "Unable to delete host0's node object" )
By ( "deleting host0Pod" )
framework . ExpectNoError ( podClient . Delete ( host0Pod . Name , metav1 . NewDeleteOptions ( 0 ) ) , "Unable to delete host0Pod" )
2017-10-19 07:11:58 +00:00
} else if disruptOp == evictPod {
evictTarget := & policy . Eviction {
ObjectMeta : metav1 . ObjectMeta {
Name : host0Pod . Name ,
Namespace : ns ,
} ,
}
By ( "evicting host0Pod" )
err = wait . PollImmediate ( framework . Poll , podEvictTimeout , func ( ) ( bool , error ) {
err = cs . CoreV1 ( ) . Pods ( ns ) . Evict ( evictTarget )
if err != nil {
return false , nil
} else {
return true , nil
}
} )
Expect ( err ) . NotTo ( HaveOccurred ( ) , fmt . Sprintf ( "failed to evict host0Pod after %v" , podEvictTimeout ) )
2017-10-16 21:49:04 +00:00
}
By ( "waiting for pd to detach from host0" )
waitForPDDetach ( diskName , host0Name )
} )
}
2016-12-20 20:23:56 +00:00
} )
2017-03-02 22:48:44 +00:00
It ( "should be able to delete a non-existent PD without error" , func ( ) {
framework . SkipUnlessProviderIs ( "gce" )
By ( "delete a PD" )
2017-04-01 04:42:52 +00:00
framework . ExpectNoError ( framework . DeletePDWithRetry ( "non-exist" ) )
2017-03-02 22:48:44 +00:00
} )
2015-07-20 02:00:10 +00:00
} )
2017-10-17 18:56:09 +00:00
func countReadyNodes ( c clientset . Interface , hostName types . NodeName ) int {
framework . WaitForNodeToBeReady ( c , string ( hostName ) , nodeStatusTimeout )
framework . WaitForAllNodesSchedulable ( c , nodeStatusTimeout )
nodes := framework . GetReadySchedulableNodesOrDie ( c )
return len ( nodes . Items )
}
2016-04-07 17:21:31 +00:00
func verifyPDContentsViaContainer ( f * framework . Framework , podName , containerName string , fileAndContentToVerify map [ string ] string ) {
2015-07-20 02:00:10 +00:00
for filePath , expectedContents := range fileAndContentToVerify {
2016-10-11 20:22:06 +00:00
var value string
// Add a retry to avoid temporal failure in reading the content
for i := 0 ; i < maxReadRetry ; i ++ {
v , err := f . ReadFileViaContainer ( podName , containerName , filePath )
value = v
2016-10-03 22:44:40 +00:00
if err != nil {
framework . Logf ( "Error reading file: %v" , err )
}
2016-10-11 20:22:06 +00:00
framework . ExpectNoError ( err )
framework . Logf ( "Read file %q with content: %v (iteration %d)" , filePath , v , i )
if strings . TrimSpace ( v ) != strings . TrimSpace ( expectedContents ) {
framework . Logf ( "Warning: read content <%q> does not match execpted content <%q>." , v , expectedContents )
size , err := f . CheckFileSizeViaContainer ( podName , containerName , filePath )
if err != nil {
framework . Logf ( "Error checking file size: %v" , err )
}
framework . Logf ( "Check file %q size: %q" , filePath , size )
} else {
break
}
2016-10-03 22:44:40 +00:00
}
2016-10-11 20:22:06 +00:00
Expect ( strings . TrimSpace ( value ) ) . To ( Equal ( strings . TrimSpace ( expectedContents ) ) )
2015-07-10 11:13:42 +00:00
}
}
2016-07-16 06:10:29 +00:00
func detachPD ( nodeName types . NodeName , pdName string ) error {
2016-04-07 17:21:31 +00:00
if framework . TestContext . Provider == "gce" || framework . TestContext . Provider == "gke" {
2017-02-17 04:33:41 +00:00
gceCloud , err := framework . GetGCECloud ( )
2015-11-24 22:48:41 +00:00
if err != nil {
return err
}
2016-07-16 06:10:29 +00:00
err = gceCloud . DetachDisk ( pdName , nodeName )
2015-11-24 22:48:41 +00:00
if err != nil {
if gerr , ok := err . ( * googleapi . Error ) ; ok && strings . Contains ( gerr . Message , "Invalid value for field 'disk'" ) {
// PD already detached, ignore error.
return nil
}
2016-04-07 17:21:31 +00:00
framework . Logf ( "Error detaching PD %q: %v" , pdName , err )
2015-11-24 22:48:41 +00:00
}
return err
2017-10-13 20:02:54 +00:00
2016-04-07 17:21:31 +00:00
} else if framework . TestContext . Provider == "aws" {
2016-03-12 10:57:59 +00:00
client := ec2 . New ( session . New ( ) )
tokens := strings . Split ( pdName , "/" )
awsVolumeID := tokens [ len ( tokens ) - 1 ]
request := ec2 . DetachVolumeInput {
VolumeId : aws . String ( awsVolumeID ) ,
2015-03-06 14:26:39 +00:00
}
2016-03-12 10:57:59 +00:00
_ , err := client . DetachVolume ( & request )
if err != nil {
return fmt . Errorf ( "error detaching EBS volume: %v" , err )
}
return nil
2017-10-13 20:02:54 +00:00
2016-03-12 10:57:59 +00:00
} else {
return fmt . Errorf ( "Provider does not support volume detaching" )
2015-03-06 14:26:39 +00:00
}
2015-02-20 18:01:33 +00:00
}
2017-10-13 20:02:54 +00:00
// Returns pod spec suitable for api Create call. Handles gce, gke and aws providers only and
// escapes if a different provider is supplied.
// The first container name is hard-coded to "mycontainer". Subsequent containers are named:
// "mycontainer<number> where <number> is 1..numContainers. Note if there is only one container it's
// name has no number.
// Container's volumeMounts are hard-coded to "/testpd<number>" where <number> is 1..len(diskNames).
2016-11-18 20:55:17 +00:00
func testPDPod ( diskNames [ ] string , targetNode types . NodeName , readOnly bool , numContainers int ) * v1 . Pod {
2017-10-13 20:02:54 +00:00
// escape if not a supported provider
if ! ( framework . TestContext . Provider == "gce" || framework . TestContext . Provider == "gke" ||
framework . TestContext . Provider == "aws" ) {
framework . Failf ( fmt . Sprintf ( "func `testPDPod` only supports gce, gke, and aws providers, not %v" , framework . TestContext . Provider ) )
}
2016-11-18 20:55:17 +00:00
containers := make ( [ ] v1 . Container , numContainers )
2015-07-20 02:00:10 +00:00
for i := range containers {
2015-09-23 22:08:11 +00:00
containers [ i ] . Name = "mycontainer"
2015-07-20 02:00:10 +00:00
if numContainers > 1 {
2015-09-23 22:08:11 +00:00
containers [ i ] . Name = fmt . Sprintf ( "mycontainer%v" , i + 1 )
2015-07-20 02:00:10 +00:00
}
2017-08-29 08:32:08 +00:00
containers [ i ] . Image = imageutils . GetBusyBoxImage ( )
2015-07-20 02:00:10 +00:00
containers [ i ] . Command = [ ] string { "sleep" , "6000" }
2016-11-18 20:55:17 +00:00
containers [ i ] . VolumeMounts = make ( [ ] v1 . VolumeMount , len ( diskNames ) )
2015-09-23 22:08:11 +00:00
for k := range diskNames {
containers [ i ] . VolumeMounts [ k ] . Name = fmt . Sprintf ( "testpd%v" , k + 1 )
containers [ i ] . VolumeMounts [ k ] . MountPath = fmt . Sprintf ( "/testpd%v" , k + 1 )
2015-07-20 02:00:10 +00:00
}
2016-11-18 20:55:17 +00:00
containers [ i ] . Resources . Limits = v1 . ResourceList { }
containers [ i ] . Resources . Limits [ v1 . ResourceCPU ] = * resource . NewQuantity ( int64 ( 0 ) , resource . DecimalSI )
2015-07-20 02:00:10 +00:00
}
2016-11-18 20:55:17 +00:00
pod := & v1 . Pod {
2016-12-03 18:57:26 +00:00
TypeMeta : metav1 . TypeMeta {
2015-02-20 18:01:33 +00:00
Kind : "Pod" ,
2017-07-11 01:10:34 +00:00
APIVersion : testapi . Groups [ v1 . GroupName ] . GroupVersion ( ) . String ( ) ,
2015-02-20 18:01:33 +00:00
} ,
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2016-07-26 15:13:18 +00:00
Name : "pd-test-" + string ( uuid . NewUUID ( ) ) ,
2015-02-20 18:01:33 +00:00
} ,
2016-11-18 20:55:17 +00:00
Spec : v1 . PodSpec {
2015-07-20 02:00:10 +00:00
Containers : containers ,
2016-07-16 06:10:29 +00:00
NodeName : string ( targetNode ) ,
2015-02-20 18:01:33 +00:00
} ,
}
2015-03-06 14:26:39 +00:00
2017-10-13 20:02:54 +00:00
pod . Spec . Volumes = make ( [ ] v1 . Volume , len ( diskNames ) )
for k , diskName := range diskNames {
pod . Spec . Volumes [ k ] . Name = fmt . Sprintf ( "testpd%v" , k + 1 )
if framework . TestContext . Provider == "aws" {
2016-11-18 20:55:17 +00:00
pod . Spec . Volumes [ k ] . VolumeSource = v1 . VolumeSource {
2017-10-13 20:02:54 +00:00
AWSElasticBlockStore : & v1 . AWSElasticBlockStoreVolumeSource {
VolumeID : diskName ,
2015-09-23 22:08:11 +00:00
FSType : "ext4" ,
ReadOnly : readOnly ,
2015-03-06 14:26:39 +00:00
} ,
2015-09-23 22:08:11 +00:00
}
2017-10-13 20:02:54 +00:00
} else { // "gce" or "gke"
2016-11-18 20:55:17 +00:00
pod . Spec . Volumes [ k ] . VolumeSource = v1 . VolumeSource {
2017-10-13 20:02:54 +00:00
GCEPersistentDisk : & v1 . GCEPersistentDiskVolumeSource {
PDName : diskName ,
2015-09-23 22:08:11 +00:00
FSType : "ext4" ,
ReadOnly : readOnly ,
2015-03-06 14:26:39 +00:00
} ,
2015-09-23 22:08:11 +00:00
}
2015-03-06 14:26:39 +00:00
}
}
return pod
2015-02-20 18:01:33 +00:00
}
2015-07-27 02:26:43 +00:00
// Waits for specified PD to to detach from specified hostName
2016-07-16 06:10:29 +00:00
func waitForPDDetach ( diskName string , nodeName types . NodeName ) error {
2016-04-07 17:21:31 +00:00
if framework . TestContext . Provider == "gce" || framework . TestContext . Provider == "gke" {
2016-07-16 06:10:29 +00:00
framework . Logf ( "Waiting for GCE PD %q to detach from node %q." , diskName , nodeName )
2017-02-17 04:33:41 +00:00
gceCloud , err := framework . GetGCECloud ( )
2015-11-24 22:48:41 +00:00
if err != nil {
return err
}
for start := time . Now ( ) ; time . Since ( start ) < gcePDDetachTimeout ; time . Sleep ( gcePDDetachPollTime ) {
2016-07-16 06:10:29 +00:00
diskAttached , err := gceCloud . DiskIsAttached ( diskName , nodeName )
2015-07-27 02:26:43 +00:00
if err != nil {
2016-07-16 06:10:29 +00:00
framework . Logf ( "Error waiting for PD %q to detach from node %q. 'DiskIsAttached(...)' failed with %v" , diskName , nodeName , err )
2015-07-27 02:26:43 +00:00
return err
}
2015-11-24 22:48:41 +00:00
if ! diskAttached {
2015-07-27 02:26:43 +00:00
// Specified disk does not appear to be attached to specified node
2016-07-16 06:10:29 +00:00
framework . Logf ( "GCE PD %q appears to have successfully detached from %q." , diskName , nodeName )
2015-07-27 02:26:43 +00:00
return nil
}
2016-07-16 06:10:29 +00:00
framework . Logf ( "Waiting for GCE PD %q to detach from %q." , diskName , nodeName )
2015-07-27 02:26:43 +00:00
}
2016-07-16 06:10:29 +00:00
return fmt . Errorf ( "Gave up waiting for GCE PD %q to detach from %q after %v" , diskName , nodeName , gcePDDetachTimeout )
2015-07-27 02:26:43 +00:00
}
return nil
}
2015-11-24 22:48:41 +00:00
2016-07-16 06:10:29 +00:00
func detachAndDeletePDs ( diskName string , hosts [ ] types . NodeName ) {
2015-11-24 22:48:41 +00:00
for _ , host := range hosts {
2016-07-12 00:58:25 +00:00
framework . Logf ( "Detaching GCE PD %q from node %q." , diskName , host )
2015-11-24 22:48:41 +00:00
detachPD ( host , diskName )
By ( fmt . Sprintf ( "Waiting for PD %q to detach from %q" , diskName , host ) )
waitForPDDetach ( diskName , host )
}
By ( fmt . Sprintf ( "Deleting PD %q" , diskName ) )
2017-04-01 04:42:52 +00:00
framework . ExpectNoError ( framework . DeletePDWithRetry ( diskName ) )
2015-11-24 22:48:41 +00:00
}
2016-05-30 02:22:22 +00:00
func waitForPDInVolumesInUse (
2016-11-18 20:55:17 +00:00
nodeClient v1core . NodeInterface ,
2016-07-16 06:10:29 +00:00
diskName string ,
nodeName types . NodeName ,
2016-05-30 02:22:22 +00:00
timeout time . Duration ,
shouldExist bool ) error {
logStr := "to contain"
if ! shouldExist {
logStr = "to NOT contain"
}
2017-10-13 20:02:54 +00:00
framework . Logf ( "Waiting for node %s's VolumesInUse Status %s PD %q" , nodeName , logStr , diskName )
2016-05-30 02:22:22 +00:00
for start := time . Now ( ) ; time . Since ( start ) < timeout ; time . Sleep ( nodeStatusPollTime ) {
2016-12-07 14:40:26 +00:00
nodeObj , err := nodeClient . Get ( string ( nodeName ) , metav1 . GetOptions { } )
2016-05-30 02:22:22 +00:00
if err != nil || nodeObj == nil {
2017-10-13 20:02:54 +00:00
framework . Logf ( "Failed to fetch node object %q from API server. err=%v" , nodeName , err )
2016-05-30 02:22:22 +00:00
continue
}
exists := false
for _ , volumeInUse := range nodeObj . Status . VolumesInUse {
volumeInUseStr := string ( volumeInUse )
if strings . Contains ( volumeInUseStr , diskName ) {
if shouldExist {
2017-10-13 20:02:54 +00:00
framework . Logf ( "Found PD %q in node %q's VolumesInUse Status: %q" , diskName , nodeName , volumeInUseStr )
2016-05-30 02:22:22 +00:00
return nil
}
exists = true
}
}
if ! shouldExist && ! exists {
2017-10-13 20:02:54 +00:00
framework . Logf ( "Verified PD %q does not exist in node %q's VolumesInUse Status." , diskName , nodeName )
2016-05-30 02:22:22 +00:00
return nil
}
}
2017-10-13 20:02:54 +00:00
return fmt . Errorf ( "Timed out waiting for node %s VolumesInUse Status %s diskName %q" , nodeName , logStr , diskName )
2016-05-30 02:22:22 +00:00
}