2016-06-29 00:20:08 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2016 The Kubernetes Authors .
2016-06-29 00:20:08 +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 .
* /
package framework
import (
2016-07-06 02:05:53 +00:00
"fmt"
2016-11-18 18:29:37 +00:00
"regexp"
2016-06-29 00:20:08 +00:00
"sync"
2016-07-06 02:05:53 +00:00
"time"
2016-06-29 00:20:08 +00:00
2019-04-10 21:31:19 +00:00
v1 "k8s.io/api/core/v1"
2017-01-13 17:48:50 +00:00
"k8s.io/apimachinery/pkg/api/errors"
2017-01-11 14:09:48 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
2019-04-02 14:08:55 +00:00
"k8s.io/client-go/kubernetes/scheme"
2017-06-23 20:56:37 +00:00
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
2018-08-14 22:36:07 +00:00
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
2017-02-23 09:46:50 +00:00
"k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/pkg/kubelet/sysctl"
2016-06-29 00:20:08 +00:00
2019-03-29 17:55:19 +00:00
"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
2016-06-29 00:20:08 +00:00
)
2019-04-01 08:04:29 +00:00
// DefaultPodDeletionTimeout is the default timeout for deleting pod
2017-03-08 22:39:51 +00:00
const DefaultPodDeletionTimeout = 3 * time . Minute
2016-09-15 21:49:14 +00:00
// ImageWhiteList is the images used in the current test suite. It should be initialized in test suite and
// the images in the white list should be pre-pulled in the test suite. Currently, this is only used by
// node e2e test.
var ImageWhiteList sets . String
2019-03-29 17:55:19 +00:00
// PodClient is a convenience method for getting a pod client interface in the framework's namespace,
2016-08-22 11:07:16 +00:00
// possibly applying test-suite specific transformations to the pod spec, e.g. for
// node e2e pod scheduling.
2016-07-08 05:56:46 +00:00
func ( f * Framework ) PodClient ( ) * PodClient {
return & PodClient {
f : f ,
2017-10-25 15:54:32 +00:00
PodInterface : f . ClientSet . CoreV1 ( ) . Pods ( f . Namespace . Name ) ,
2016-07-08 05:56:46 +00:00
}
2016-06-29 00:20:08 +00:00
}
2019-03-29 17:55:19 +00:00
// PodClientNS is a convenience method for getting a pod client interface in an alternative namespace,
2017-01-26 19:07:50 +00:00
// possibly applying test-suite specific transformations to the pod spec, e.g. for
// node e2e pod scheduling.
func ( f * Framework ) PodClientNS ( namespace string ) * PodClient {
return & PodClient {
f : f ,
2017-10-25 15:54:32 +00:00
PodInterface : f . ClientSet . CoreV1 ( ) . Pods ( namespace ) ,
2017-01-26 19:07:50 +00:00
}
}
2019-03-29 17:55:19 +00:00
// PodClient is a struct for pod client.
2016-07-08 05:56:46 +00:00
type PodClient struct {
f * Framework
2016-11-18 20:55:17 +00:00
v1core . PodInterface
2016-06-29 00:20:08 +00:00
}
2016-07-08 05:56:46 +00:00
// Create creates a new pod according to the framework specifications (don't wait for it to start).
2016-11-18 20:55:17 +00:00
func ( c * PodClient ) Create ( pod * v1 . Pod ) * v1 . Pod {
2016-07-20 18:03:05 +00:00
c . mungeSpec ( pod )
2016-07-08 05:56:46 +00:00
p , err := c . PodInterface . Create ( pod )
2016-06-29 00:20:08 +00:00
ExpectNoError ( err , "Error creating Pod" )
2016-07-10 23:57:18 +00:00
return p
2016-06-29 00:20:08 +00:00
}
2018-09-19 10:04:32 +00:00
// CreateEventually retries pod creation for a while before failing
// the test with the most recent error. This mimicks the behavior
// of a controller (like the one for DaemonSet) and is necessary
// because pod creation can fail while its service account is still
// getting provisioned
// (https://github.com/kubernetes/kubernetes/issues/68776).
//
// Both the timeout and polling interval are configurable as optional
// arguments:
// - The first optional argument is the timeout.
// - The second optional argument is the polling interval.
//
// Both intervals can either be specified as time.Duration, parsable
// duration strings or as floats/integers. In the last case they are
// interpreted as seconds.
func ( c * PodClient ) CreateEventually ( pod * v1 . Pod , opts ... interface { } ) * v1 . Pod {
c . mungeSpec ( pod )
var ret * v1 . Pod
2019-03-29 17:55:19 +00:00
gomega . Eventually ( func ( ) error {
2018-09-19 10:04:32 +00:00
p , err := c . PodInterface . Create ( pod )
ret = p
return err
2019-03-29 17:55:19 +00:00
} , opts ... ) . ShouldNot ( gomega . HaveOccurred ( ) , "Failed to create %q pod" , pod . GetName ( ) )
2018-09-19 10:04:32 +00:00
return ret
}
2019-03-29 17:55:19 +00:00
// CreateSyncInNamespace creates a new pod according to the framework specifications in the given namespace, and waits for it to start.
2017-02-23 18:31:20 +00:00
func ( c * PodClient ) CreateSyncInNamespace ( pod * v1 . Pod , namespace string ) * v1 . Pod {
2016-07-08 05:56:46 +00:00
p := c . Create ( pod )
2017-02-23 18:31:20 +00:00
ExpectNoError ( WaitForPodNameRunningInNamespace ( c . f . ClientSet , p . Name , namespace ) )
2016-07-15 18:06:10 +00:00
// Get the newest pod after it becomes running, some status may change after pod created, such as pod ip.
2016-12-07 14:40:26 +00:00
p , err := c . Get ( p . Name , metav1 . GetOptions { } )
2016-07-15 18:06:10 +00:00
ExpectNoError ( err )
2016-07-08 05:56:46 +00:00
return p
}
2017-02-23 18:31:20 +00:00
// CreateSync creates a new pod according to the framework specifications, and wait for it to start.
func ( c * PodClient ) CreateSync ( pod * v1 . Pod ) * v1 . Pod {
return c . CreateSyncInNamespace ( pod , c . f . Namespace . Name )
}
2016-07-08 05:56:46 +00:00
// CreateBatch create a batch of pods. All pods are created before waiting.
2016-11-18 20:55:17 +00:00
func ( c * PodClient ) CreateBatch ( pods [ ] * v1 . Pod ) [ ] * v1 . Pod {
ps := make ( [ ] * v1 . Pod , len ( pods ) )
2016-06-29 00:20:08 +00:00
var wg sync . WaitGroup
2016-07-15 18:06:10 +00:00
for i , pod := range pods {
2016-06-29 00:20:08 +00:00
wg . Add ( 1 )
2016-11-18 20:55:17 +00:00
go func ( i int , pod * v1 . Pod ) {
2016-07-20 17:26:56 +00:00
defer wg . Done ( )
2019-03-29 17:55:19 +00:00
defer ginkgo . GinkgoRecover ( )
2016-07-15 18:06:10 +00:00
ps [ i ] = c . CreateSync ( pod )
} ( i , pod )
2016-06-29 00:20:08 +00:00
}
wg . Wait ( )
2016-07-10 23:57:18 +00:00
return ps
2016-06-29 00:20:08 +00:00
}
2016-07-08 05:56:46 +00:00
// Update updates the pod object. It retries if there is a conflict, throw out error if
2016-07-06 02:05:53 +00:00
// there is any other errors. name is the pod name, updateFn is the function updating the
// pod object.
2016-11-18 20:55:17 +00:00
func ( c * PodClient ) Update ( name string , updateFn func ( pod * v1 . Pod ) ) {
2016-07-06 02:05:53 +00:00
ExpectNoError ( wait . Poll ( time . Millisecond * 500 , time . Second * 30 , func ( ) ( bool , error ) {
2016-12-07 14:40:26 +00:00
pod , err := c . PodInterface . Get ( name , metav1 . GetOptions { } )
2016-07-06 02:05:53 +00:00
if err != nil {
return false , fmt . Errorf ( "failed to get pod %q: %v" , name , err )
}
updateFn ( pod )
2016-07-08 05:56:46 +00:00
_ , err = c . PodInterface . Update ( pod )
2016-07-06 02:05:53 +00:00
if err == nil {
Logf ( "Successfully updated pod %q" , name )
return true , nil
}
if errors . IsConflict ( err ) {
Logf ( "Conflicting update to pod %q, re-get and re-update: %v" , name , err )
return false , nil
}
return false , fmt . Errorf ( "failed to update pod %q: %v" , name , err )
} ) )
}
2016-07-08 05:56:46 +00:00
2016-09-21 19:58:25 +00:00
// DeleteSync deletes the pod and wait for the pod to disappear for `timeout`. If the pod doesn't
// disappear before the timeout, it will fail the test.
2017-01-24 15:38:21 +00:00
func ( c * PodClient ) DeleteSync ( name string , options * metav1 . DeleteOptions , timeout time . Duration ) {
2017-02-23 18:31:20 +00:00
c . DeleteSyncInNamespace ( name , c . f . Namespace . Name , options , timeout )
}
// DeleteSyncInNamespace deletes the pod from the namespace and wait for the pod to disappear for `timeout`. If the pod doesn't
// disappear before the timeout, it will fail the test.
func ( c * PodClient ) DeleteSyncInNamespace ( name string , namespace string , options * metav1 . DeleteOptions , timeout time . Duration ) {
2016-09-21 19:58:25 +00:00
err := c . Delete ( name , options )
if err != nil && ! errors . IsNotFound ( err ) {
Failf ( "Failed to delete pod %q: %v" , name , err )
}
2019-03-29 17:55:19 +00:00
gomega . Expect ( WaitForPodToDisappear ( c . f . ClientSet , namespace , name , labels . Everything ( ) ,
2 * time . Second , timeout ) ) . To ( gomega . Succeed ( ) , "wait for pod %q to disappear" , name )
2016-09-21 19:58:25 +00:00
}
2016-07-20 18:03:05 +00:00
// mungeSpec apply test-suite specific transformations to the pod spec.
2016-11-18 20:55:17 +00:00
func ( c * PodClient ) mungeSpec ( pod * v1 . Pod ) {
2016-11-09 04:30:44 +00:00
if ! TestContext . NodeE2E {
return
}
2019-03-29 17:55:19 +00:00
gomega . Expect ( pod . Spec . NodeName ) . To ( gomega . Or ( gomega . BeZero ( ) , gomega . Equal ( TestContext . NodeName ) ) , "Test misconfigured" )
2016-11-09 04:30:44 +00:00
pod . Spec . NodeName = TestContext . NodeName
// Node e2e does not support the default DNSClusterFirst policy. Set
// the policy to DNSDefault, which is configured per node.
2016-11-18 20:55:17 +00:00
pod . Spec . DNSPolicy = v1 . DNSDefault
2016-11-09 04:30:44 +00:00
// PrepullImages only works for node e2e now. For cluster e2e, image prepull is not enforced,
// we should not munge ImagePullPolicy for cluster e2e pods.
if ! TestContext . PrepullImages {
return
}
// If prepull is enabled, munge the container spec to make sure the images are not pulled
// during the test.
for i := range pod . Spec . Containers {
c := & pod . Spec . Containers [ i ]
2016-11-18 20:55:17 +00:00
if c . ImagePullPolicy == v1 . PullAlways {
2016-11-09 04:30:44 +00:00
// If the image pull policy is PullAlways, the image doesn't need to be in
// the white list or pre-pulled, because the image is expected to be pulled
// in the test anyway.
continue
2016-09-15 21:49:14 +00:00
}
2016-11-09 04:30:44 +00:00
// If the image policy is not PullAlways, the image must be in the white list and
// pre-pulled.
2019-03-29 17:55:19 +00:00
gomega . Expect ( ImageWhiteList . Has ( c . Image ) ) . To ( gomega . BeTrue ( ) , "Image %q is not in the white list, consider adding it to CommonImageWhiteList in test/e2e/common/util.go or NodeImageWhiteList in test/e2e_node/image_list.go" , c . Image )
2016-11-09 04:30:44 +00:00
// Do not pull images during the tests because the images in white list should have
// been prepulled.
2016-11-18 20:55:17 +00:00
c . ImagePullPolicy = v1 . PullNever
2016-07-20 18:03:05 +00:00
}
}
2017-02-23 09:46:50 +00:00
// WaitForSuccess waits for pod to succeed.
2019-03-29 17:55:19 +00:00
// TODO(random-liu): Move pod wait function into this file
2016-09-23 20:36:37 +00:00
func ( c * PodClient ) WaitForSuccess ( name string , timeout time . Duration ) {
f := c . f
2019-03-29 17:55:19 +00:00
gomega . Expect ( WaitForPodCondition ( f . ClientSet , f . Namespace . Name , name , "success or failure" , timeout ,
2016-11-18 20:55:17 +00:00
func ( pod * v1 . Pod ) ( bool , error ) {
2016-09-23 20:36:37 +00:00
switch pod . Status . Phase {
2016-11-18 20:55:17 +00:00
case v1 . PodFailed :
2016-09-23 20:36:37 +00:00
return true , fmt . Errorf ( "pod %q failed with reason: %q, message: %q" , name , pod . Status . Reason , pod . Status . Message )
2016-11-18 20:55:17 +00:00
case v1 . PodSucceeded :
2016-09-23 20:36:37 +00:00
return true , nil
default :
return false , nil
}
} ,
2019-03-29 17:55:19 +00:00
) ) . To ( gomega . Succeed ( ) , "wait for pod %q to success" , name )
2016-09-23 20:36:37 +00:00
}
2016-11-18 18:29:37 +00:00
2017-07-28 09:15:43 +00:00
// WaitForFailure waits for pod to fail.
func ( c * PodClient ) WaitForFailure ( name string , timeout time . Duration ) {
f := c . f
2019-03-29 17:55:19 +00:00
gomega . Expect ( WaitForPodCondition ( f . ClientSet , f . Namespace . Name , name , "success or failure" , timeout ,
2017-07-28 09:15:43 +00:00
func ( pod * v1 . Pod ) ( bool , error ) {
switch pod . Status . Phase {
case v1 . PodFailed :
return true , nil
case v1 . PodSucceeded :
return true , fmt . Errorf ( "pod %q successed with reason: %q, message: %q" , name , pod . Status . Reason , pod . Status . Message )
default :
return false , nil
}
} ,
2019-03-29 17:55:19 +00:00
) ) . To ( gomega . Succeed ( ) , "wait for pod %q to fail" , name )
2017-07-28 09:15:43 +00:00
}
2019-04-10 21:31:19 +00:00
// WaitForFinish waits for pod to finish running, regardless of success or failure.
func ( c * PodClient ) WaitForFinish ( name string , timeout time . Duration ) {
f := c . f
gomega . Expect ( WaitForPodCondition ( f . ClientSet , f . Namespace . Name , name , "success or failure" , timeout ,
func ( pod * v1 . Pod ) ( bool , error ) {
switch pod . Status . Phase {
case v1 . PodFailed :
return true , nil
case v1 . PodSucceeded :
return true , nil
default :
return false , nil
}
} ,
) ) . To ( gomega . Succeed ( ) , "wait for pod %q to finish running" , name )
}
2019-03-29 17:55:19 +00:00
// WaitForErrorEventOrSuccess waits for pod to succeed or an error event for that pod.
2017-02-23 09:46:50 +00:00
func ( c * PodClient ) WaitForErrorEventOrSuccess ( pod * v1 . Pod ) ( * v1 . Event , error ) {
var ev * v1 . Event
err := wait . Poll ( Poll , PodStartTimeout , func ( ) ( bool , error ) {
2019-04-02 14:08:55 +00:00
evnts , err := c . f . ClientSet . CoreV1 ( ) . Events ( pod . Namespace ) . Search ( scheme . Scheme , pod )
2017-02-23 09:46:50 +00:00
if err != nil {
return false , fmt . Errorf ( "error in listing events: %s" , err )
}
for _ , e := range evnts . Items {
switch e . Reason {
case events . KillingContainer , events . FailedToCreateContainer , sysctl . UnsupportedReason , sysctl . ForbiddenReason :
ev = & e
return true , nil
case events . StartedContainer :
return true , nil
default :
// ignore all other errors
}
}
return false , nil
} )
return ev , err
}
2017-02-27 05:40:15 +00:00
// MatchContainerOutput gets output of a container and match expected regexp in the output.
2016-11-18 18:29:37 +00:00
func ( c * PodClient ) MatchContainerOutput ( name string , containerName string , expectedRegexp string ) error {
f := c . f
output , err := GetPodLogs ( f . ClientSet , f . Namespace . Name , name , containerName )
if err != nil {
return fmt . Errorf ( "failed to get output for container %q of pod %q" , containerName , name )
}
regex , err := regexp . Compile ( expectedRegexp )
if err != nil {
return fmt . Errorf ( "failed to compile regexp %q: %v" , expectedRegexp , err )
}
if ! regex . MatchString ( output ) {
return fmt . Errorf ( "failed to match regexp %q in output %q" , expectedRegexp , output )
}
return nil
}
2018-08-14 22:36:07 +00:00
2019-03-29 17:55:19 +00:00
// PodIsReady returns true if the specified pod is ready. Otherwise false.
2018-08-14 22:36:07 +00:00
func ( c * PodClient ) PodIsReady ( name string ) bool {
pod , err := c . Get ( name , metav1 . GetOptions { } )
ExpectNoError ( err )
return podutil . IsPodReady ( pod )
}