2017-02-01 00:45:59 +00:00
/ *
Copyright 2016 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 framework
import (
"fmt"
"regexp"
"sync"
"time"
"k8s.io/apimachinery/pkg/api/errors"
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"
"k8s.io/kubernetes/pkg/api/v1"
v1core "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
// 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
// Convenience method for getting a pod client interface in the framework's namespace,
// possibly applying test-suite specific transformations to the pod spec, e.g. for
// node e2e pod scheduling.
func ( f * Framework ) PodClient ( ) * PodClient {
return & PodClient {
f : f ,
PodInterface : f . ClientSet . Core ( ) . Pods ( f . Namespace . Name ) ,
}
}
type PodClient struct {
f * Framework
v1core . PodInterface
}
// Create creates a new pod according to the framework specifications (don't wait for it to start).
func ( c * PodClient ) Create ( pod * v1 . Pod ) * v1 . Pod {
c . mungeSpec ( pod )
p , err := c . PodInterface . Create ( pod )
ExpectNoError ( err , "Error creating Pod" )
return p
}
// 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 {
p := c . Create ( pod )
ExpectNoError ( c . f . WaitForPodRunning ( p . Name ) )
// Get the newest pod after it becomes running, some status may change after pod created, such as pod ip.
p , err := c . Get ( p . Name , metav1 . GetOptions { } )
ExpectNoError ( err )
return p
}
// CreateBatch create a batch of pods. All pods are created before waiting.
func ( c * PodClient ) CreateBatch ( pods [ ] * v1 . Pod ) [ ] * v1 . Pod {
ps := make ( [ ] * v1 . Pod , len ( pods ) )
var wg sync . WaitGroup
for i , pod := range pods {
wg . Add ( 1 )
go func ( i int , pod * v1 . Pod ) {
defer wg . Done ( )
defer GinkgoRecover ( )
ps [ i ] = c . CreateSync ( pod )
} ( i , pod )
}
wg . Wait ( )
return ps
}
// Update updates the pod object. It retries if there is a conflict, throw out error if
// there is any other errors. name is the pod name, updateFn is the function updating the
// pod object.
func ( c * PodClient ) Update ( name string , updateFn func ( pod * v1 . Pod ) ) {
ExpectNoError ( wait . Poll ( time . Millisecond * 500 , time . Second * 30 , func ( ) ( bool , error ) {
pod , err := c . PodInterface . Get ( name , metav1 . GetOptions { } )
if err != nil {
return false , fmt . Errorf ( "failed to get pod %q: %v" , name , err )
}
updateFn ( pod )
_ , err = c . PodInterface . Update ( pod )
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 )
} ) )
}
// 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-02-03 13:41:32 +00:00
func ( c * PodClient ) DeleteSync ( name string , options * metav1 . DeleteOptions , timeout time . Duration ) {
2017-02-01 00:45:59 +00:00
err := c . Delete ( name , options )
if err != nil && ! errors . IsNotFound ( err ) {
Failf ( "Failed to delete pod %q: %v" , name , err )
}
Expect ( WaitForPodToDisappear ( c . f . ClientSet , c . f . Namespace . Name , name , labels . Everything ( ) ,
2 * time . Second , timeout ) ) . To ( Succeed ( ) , "wait for pod %q to disappear" , name )
}
// mungeSpec apply test-suite specific transformations to the pod spec.
func ( c * PodClient ) mungeSpec ( pod * v1 . Pod ) {
if ! TestContext . NodeE2E {
return
}
Expect ( pod . Spec . NodeName ) . To ( Or ( BeZero ( ) , Equal ( TestContext . NodeName ) ) , "Test misconfigured" )
pod . Spec . NodeName = TestContext . NodeName
// Node e2e does not support the default DNSClusterFirst policy. Set
// the policy to DNSDefault, which is configured per node.
pod . Spec . DNSPolicy = v1 . DNSDefault
// 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 ]
if c . ImagePullPolicy == v1 . PullAlways {
// 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
}
// If the image policy is not PullAlways, the image must be in the white list and
// pre-pulled.
Expect ( ImageWhiteList . Has ( c . Image ) ) . To ( 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 )
// Do not pull images during the tests because the images in white list should have
// been prepulled.
c . ImagePullPolicy = v1 . PullNever
}
}
// TODO(random-liu): Move pod wait function into this file
// WaitForSuccess waits for pod to success.
func ( c * PodClient ) WaitForSuccess ( name string , timeout time . Duration ) {
f := c . f
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 , fmt . Errorf ( "pod %q failed with reason: %q, message: %q" , name , pod . Status . Reason , pod . Status . Message )
case v1 . PodSucceeded :
return true , nil
default :
return false , nil
}
} ,
) ) . To ( Succeed ( ) , "wait for pod %q to success" , name )
}
// MatchContainerOutput gest output of a container and match expected regexp in the output.
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
}