2014-10-14 19:28:45 +00:00
/ *
Copyright 2014 Google Inc . All rights reserved .
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 main
import (
"flag"
2014-11-23 09:13:02 +00:00
"fmt"
2014-10-14 19:28:45 +00:00
"io/ioutil"
"os"
2014-12-09 21:10:59 +00:00
"path/filepath"
goruntime "runtime"
2014-10-14 19:28:45 +00:00
"strconv"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
2014-11-11 23:23:09 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
2014-10-14 19:28:45 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
2014-12-09 21:10:59 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
2014-10-14 19:28:45 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/golang/glog"
)
var (
authConfig = flag . String ( "auth_config" , os . Getenv ( "HOME" ) + "/.kubernetes_auth" , "Path to the auth info file." )
2014-12-09 23:07:54 +00:00
certDir = flag . String ( "cert_dir" , "" , "Path to the directory containing the certs. Default is empty, which doesn't use certs." )
2014-10-14 19:28:45 +00:00
host = flag . String ( "host" , "" , "The host to connect to" )
2014-12-09 21:10:59 +00:00
repoRoot = flag . String ( "repo_root" , "./" , "Root directory of kubernetes repository, for finding test files. Default assumes working directory is repository root" )
2014-10-14 19:28:45 +00:00
)
func waitForPodRunning ( c * client . Client , id string ) {
for {
time . Sleep ( 5 * time . Second )
2014-10-21 21:14:35 +00:00
pod , err := c . Pods ( api . NamespaceDefault ) . Get ( id )
2014-10-14 19:28:45 +00:00
if err != nil {
glog . Warningf ( "Get pod failed: %v" , err )
continue
}
2014-11-21 19:04:32 +00:00
if pod . Status . Phase == api . PodRunning {
2014-10-14 19:28:45 +00:00
break
}
2014-11-23 09:13:02 +00:00
glog . Infof ( "Waiting for pod status to be %q (found %q)" , api . PodRunning , pod . Status . Phase )
2014-10-14 19:28:45 +00:00
}
}
2014-12-17 19:41:47 +00:00
// assetPath returns a path to the requested file; safe on all
// OSes. NOTE: If you use an asset in this test, you MUST add it to
// the KUBE_TEST_PORTABLE array in hack/lib/golang.sh.
2014-12-09 21:10:59 +00:00
func assetPath ( pathElements ... string ) string {
return filepath . Join ( * repoRoot , filepath . Join ( pathElements ... ) )
}
func loadObjectOrDie ( filePath string ) runtime . Object {
2014-10-14 19:28:45 +00:00
data , err := ioutil . ReadFile ( filePath )
if err != nil {
glog . Fatalf ( "Failed to read pod: %v" , err )
}
obj , err := latest . Codec . Decode ( data )
if err != nil {
glog . Fatalf ( "Failed to decode pod: %v" , err )
}
return obj
}
func loadPodOrDie ( filePath string ) * api . Pod {
obj := loadObjectOrDie ( filePath )
pod , ok := obj . ( * api . Pod )
if ! ok {
glog . Fatalf ( "Failed to load pod: %v" , obj )
}
return pod
}
func loadClientOrDie ( ) * client . Client {
config := client . Config {
Host : * host ,
}
2014-12-09 23:07:54 +00:00
info , err := clientauth . LoadFromFile ( * authConfig )
2014-10-14 19:28:45 +00:00
if err != nil {
glog . Fatalf ( "Error loading auth: %v" , err )
}
2014-12-09 23:07:54 +00:00
// If the certificate directory is provided, set the cert paths to be there.
if * certDir != "" {
glog . Infof ( "Expecting certs in %v." , * certDir )
info . CAFile = filepath . Join ( * certDir , "ca.crt" )
info . CertFile = filepath . Join ( * certDir , "kubecfg.crt" )
info . KeyFile = filepath . Join ( * certDir , "kubecfg.key" )
}
config , err = info . MergeWithConfig ( config )
2014-11-11 23:23:09 +00:00
if err != nil {
glog . Fatalf ( "Error creating client" )
2014-10-14 19:28:45 +00:00
}
c , err := client . New ( & config )
if err != nil {
glog . Fatalf ( "Error creating client" )
}
return c
}
2014-10-28 00:57:28 +00:00
func TestKubernetesROService ( c * client . Client ) bool {
svc := api . ServiceList { }
err := c . Get ( ) .
Namespace ( "default" ) .
AbsPath ( "/api/v1beta1/proxy/services/kubernetes-ro/api/v1beta1/services" ) .
Do ( ) .
Into ( & svc )
if err != nil {
glog . Errorf ( "unexpected error listing services using ro service: %v" , err )
return false
}
var foundRW , foundRO bool
for i := range svc . Items {
if svc . Items [ i ] . Name == "kubernetes" {
foundRW = true
}
if svc . Items [ i ] . Name == "kubernetes-ro" {
foundRO = true
}
}
if ! foundRW {
glog . Error ( "no RW service found" )
}
if ! foundRO {
glog . Error ( "no RO service found" )
}
if ! foundRW || ! foundRO {
return false
}
return true
}
2014-10-14 19:28:45 +00:00
func TestPodUpdate ( c * client . Client ) bool {
2014-10-21 21:14:35 +00:00
podClient := c . Pods ( api . NamespaceDefault )
2014-10-14 19:28:45 +00:00
2014-12-09 21:10:59 +00:00
pod := loadPodOrDie ( assetPath ( "api" , "examples" , "pod.json" ) )
2014-10-14 19:28:45 +00:00
value := strconv . Itoa ( time . Now ( ) . Nanosecond ( ) )
pod . Labels [ "time" ] = value
2014-10-21 21:14:35 +00:00
_ , err := podClient . Create ( pod )
2014-10-14 19:28:45 +00:00
if err != nil {
glog . Errorf ( "Failed to create pod: %v" , err )
return false
}
2014-10-21 21:14:35 +00:00
defer podClient . Delete ( pod . Name )
2014-10-22 17:02:02 +00:00
waitForPodRunning ( c , pod . Name )
2014-10-21 21:14:35 +00:00
pods , err := podClient . List ( labels . SelectorFromSet ( labels . Set ( map [ string ] string { "time" : value } ) ) )
2014-10-14 19:28:45 +00:00
if len ( pods . Items ) != 1 {
glog . Errorf ( "Failed to find the correct pod" )
return false
}
2014-10-21 21:14:35 +00:00
podOut , err := podClient . Get ( pod . Name )
2014-10-14 19:28:45 +00:00
if err != nil {
glog . Errorf ( "Failed to get pod: %v" , err )
return false
}
value = "time" + value
pod . Labels [ "time" ] = value
pod . ResourceVersion = podOut . ResourceVersion
2014-11-13 15:52:13 +00:00
pod . UID = podOut . UID
2014-10-21 21:14:35 +00:00
pod , err = podClient . Update ( pod )
2014-10-14 19:28:45 +00:00
if err != nil {
glog . Errorf ( "Failed to update pod: %v" , err )
return false
}
2014-10-22 17:02:02 +00:00
waitForPodRunning ( c , pod . Name )
2014-10-21 21:14:35 +00:00
pods , err = podClient . List ( labels . SelectorFromSet ( labels . Set ( map [ string ] string { "time" : value } ) ) )
2014-10-14 19:28:45 +00:00
if len ( pods . Items ) != 1 {
glog . Errorf ( "Failed to find the correct pod after update." )
return false
}
glog . Infof ( "pod update OK" )
return true
}
2014-11-25 19:30:15 +00:00
// TestImportantURLs validates that URLs that people depend on haven't moved.
// ***IMPORTANT*** Do *not* fix this test just by changing the path. If you moved a URL
// you can break upstream dependencies.
func TestImportantURLs ( c * client . Client ) bool {
tests := [ ] struct {
path string
2014-11-25 23:41:52 +00:00
} {
{ path : "/validate" } ,
{ path : "/healthz" } ,
// TODO: test proxy links here
}
2014-11-25 19:30:15 +00:00
ok := true
for _ , test := range tests {
glog . Infof ( "testing: %s" , test . path )
data , err := c . RESTClient . Get ( ) .
AbsPath ( test . path ) .
Do ( ) .
Raw ( )
2014-11-25 23:41:52 +00:00
if err != nil {
glog . Errorf ( "Failed: %v\nBody: %s" , err , string ( data ) )
ok = false
}
2014-11-25 19:30:15 +00:00
}
return ok
}
2014-11-13 23:45:34 +00:00
// TestKubeletSendsEvent checks that kubelets and scheduler send events about pods scheduling and running.
func TestKubeletSendsEvent ( c * client . Client ) bool {
2014-11-18 08:22:01 +00:00
provider := os . Getenv ( "KUBERNETES_PROVIDER" )
2014-12-09 23:07:54 +00:00
if len ( provider ) > 0 && provider != "gce" && provider != "gke" {
2014-11-18 08:22:01 +00:00
glog . Infof ( "skipping TestKubeletSendsEvent on cloud provider %s" , provider )
return true
}
2014-11-18 18:13:45 +00:00
if provider == "" {
2014-12-09 23:07:54 +00:00
glog . Info ( "KUBERNETES_PROVIDER is unset; assuming \"gce\"" )
2014-11-18 18:13:45 +00:00
}
2014-11-18 08:22:01 +00:00
2014-11-13 23:45:34 +00:00
podClient := c . Pods ( api . NamespaceDefault )
2014-12-09 21:10:59 +00:00
pod := loadPodOrDie ( assetPath ( "cmd" , "e2e" , "pod.json" ) )
2014-11-13 23:45:34 +00:00
value := strconv . Itoa ( time . Now ( ) . Nanosecond ( ) )
pod . Labels [ "time" ] = value
_ , err := podClient . Create ( pod )
if err != nil {
glog . Errorf ( "Failed to create pod: %v" , err )
return false
}
defer podClient . Delete ( pod . Name )
waitForPodRunning ( c , pod . Name )
pods , err := podClient . List ( labels . SelectorFromSet ( labels . Set ( map [ string ] string { "time" : value } ) ) )
if len ( pods . Items ) != 1 {
glog . Errorf ( "Failed to find the correct pod" )
return false
}
2014-12-02 19:01:24 +00:00
podWithUid , err := podClient . Get ( pod . Name )
2014-11-13 23:45:34 +00:00
if err != nil {
glog . Errorf ( "Failed to get pod: %v" , err )
return false
}
// Check for scheduler event about the pod.
2014-12-02 19:01:24 +00:00
glog . Infof ( "%+v" , podWithUid )
2014-11-13 23:45:34 +00:00
events , err := c . Events ( api . NamespaceDefault ) . List (
labels . Everything ( ) ,
labels . Set {
"involvedObject.kind" : "Pod" ,
2014-12-02 19:01:24 +00:00
"involvedObject.uid" : podWithUid . UID ,
2014-11-13 23:45:34 +00:00
"involvedObject.namespace" : api . NamespaceDefault ,
"source" : "scheduler" ,
} . AsSelector ( ) ,
)
if err != nil {
glog . Error ( "Error while listing events:" , err )
return false
}
if len ( events . Items ) == 0 {
glog . Error ( "Didn't see any scheduler events even though pod was running." )
return false
}
glog . Info ( "Saw scheduler event for our pod." )
// Check for kubelet event about the pod.
events , err = c . Events ( api . NamespaceDefault ) . List (
labels . Everything ( ) ,
labels . Set {
2014-12-02 19:01:24 +00:00
"involvedObject.uid" : podWithUid . UID ,
2014-11-13 23:45:34 +00:00
"involvedObject.kind" : "BoundPod" ,
"involvedObject.namespace" : api . NamespaceDefault ,
"source" : "kubelet" ,
} . AsSelector ( ) ,
)
if err != nil {
glog . Error ( "Error while listing events:" , err )
return false
}
if len ( events . Items ) == 0 {
glog . Error ( "Didn't see any kubelet events even though pod was running." )
return false
}
glog . Info ( "Saw kubelet event for our pod." )
return true
}
2014-12-09 21:10:59 +00:00
func TestNetwork ( c * client . Client ) bool {
ns := api . NamespaceDefault
svc , err := c . Services ( ns ) . Create ( loadObjectOrDie ( assetPath (
"contrib" , "for-tests" , "network-tester" , "service.json" ,
) ) . ( * api . Service ) )
if err != nil {
glog . Errorf ( "unable to create test service: %v" , err )
return false
}
// Clean up service
defer func ( ) {
if err = c . Services ( ns ) . Delete ( svc . Name ) ; err != nil {
glog . Errorf ( "unable to delete svc %v: %v" , svc . Name , err )
}
} ( )
rc , err := c . ReplicationControllers ( ns ) . Create ( loadObjectOrDie ( assetPath (
"contrib" , "for-tests" , "network-tester" , "rc.json" ,
) ) . ( * api . ReplicationController ) )
if err != nil {
glog . Errorf ( "unable to create test rc: %v" , err )
return false
}
// Clean up rc
defer func ( ) {
rc . Spec . Replicas = 0
rc , err = c . ReplicationControllers ( ns ) . Update ( rc )
if err != nil {
glog . Errorf ( "unable to modify replica count for rc %v: %v" , rc . Name , err )
return
}
if err = c . ReplicationControllers ( ns ) . Delete ( rc . Name ) ; err != nil {
glog . Errorf ( "unable to delete rc %v: %v" , rc . Name , err )
}
} ( )
const maxAttempts = 60
for i := 0 ; i < maxAttempts ; i ++ {
time . Sleep ( time . Second )
2014-12-26 20:06:25 +00:00
body , err := c . Get ( ) . Prefix ( "proxy" ) . Resource ( "services" ) . Name ( svc . Name ) . Suffix ( "status" ) . Do ( ) . Raw ( )
2014-12-09 21:10:59 +00:00
if err != nil {
glog . Infof ( "Attempt %v/%v: service/pod still starting. (error: '%v')" , i , maxAttempts , err )
continue
}
switch string ( body ) {
case "pass" :
glog . Infof ( "Passed on attempt %v. Cleaning up." , i )
return true
case "running" :
glog . Infof ( "Attempt %v/%v: test still running" , i , maxAttempts )
case "fail" :
2014-12-26 20:06:25 +00:00
if body , err := c . Get ( ) . Prefix ( "proxy" ) . Resource ( "services" ) . Name ( svc . Name ) . Suffix ( "read" ) . Do ( ) . Raw ( ) ; err != nil {
2014-12-09 21:10:59 +00:00
glog . Infof ( "Failed on attempt %v. Cleaning up. Error reading details: %v" , i , err )
} else {
glog . Infof ( "Failed on attempt %v. Cleaning up. Details:\n%v" , i , string ( body ) )
}
return false
}
}
2014-12-26 20:06:25 +00:00
if body , err := c . Get ( ) . Prefix ( "proxy" ) . Resource ( "services" ) . Name ( svc . Name ) . Suffix ( "read" ) . Do ( ) . Raw ( ) ; err != nil {
2014-12-09 21:10:59 +00:00
glog . Infof ( "Timed out. Cleaning up. Error reading details: %v" , err )
} else {
glog . Infof ( "Timed out. Cleaning up. Details:\n%v" , string ( body ) )
}
return false
}
2014-12-02 20:50:48 +00:00
type TestSpec struct {
// The test to run
test func ( c * client . Client ) bool
// The human readable name of this test
name string
// The id for this test. It should be constant for the life of the test.
id int
}
type TestInfo struct {
passed bool
spec TestSpec
}
// Output a summary in the TAP (test anything protocol) format for automated processing.
// See http://testanything.org/ for more info
func outputTAPSummary ( infoList [ ] TestInfo ) {
glog . Infof ( "1..%d" , len ( infoList ) )
for _ , info := range infoList {
if info . passed {
glog . Infof ( "ok %d - %s" , info . spec . id , info . spec . name )
} else {
glog . Infof ( "not ok %d - %s" , info . spec . id , info . spec . name )
}
}
}
2014-11-23 09:13:02 +00:00
// TestClusterDNS checks that cluster DNS works.
func TestClusterDNS ( c * client . Client ) bool {
podClient := c . Pods ( api . NamespaceDefault )
//TODO: Wait for skyDNS
// All the names we need to be able to resolve.
namesToResolve := [ ] string {
"kubernetes-ro" ,
"kubernetes-ro.default" ,
"kubernetes-ro.default.kubernetes.local" ,
"google.com" ,
}
probeCmd := "for i in `seq 1 600`; do "
for _ , name := range namesToResolve {
probeCmd += fmt . Sprintf ( "wget -O /dev/null %s && echo OK > /results/%s;" , name , name )
}
probeCmd += "sleep 1; done"
// Run a pod which probes DNS and exposes the results by HTTP.
pod := & api . Pod {
TypeMeta : api . TypeMeta {
Kind : "Pod" ,
APIVersion : "v1beta1" ,
} ,
ObjectMeta : api . ObjectMeta {
Name : "dns-test" ,
} ,
Spec : api . PodSpec {
Volumes : [ ] api . Volume {
{
Name : "results" ,
Source : & api . VolumeSource {
EmptyDir : & api . EmptyDir { } ,
} ,
} ,
} ,
Containers : [ ] api . Container {
{
Name : "webserver" ,
Image : "kubernetes/test-webserver" ,
VolumeMounts : [ ] api . VolumeMount {
{
Name : "results" ,
MountPath : "/results" ,
} ,
} ,
} ,
{
Name : "pinger" ,
Image : "busybox" ,
Command : [ ] string { "sh" , "-c" , probeCmd } ,
VolumeMounts : [ ] api . VolumeMount {
{
Name : "results" ,
MountPath : "/results" ,
} ,
} ,
} ,
} ,
} ,
}
_ , err := podClient . Create ( pod )
if err != nil {
glog . Errorf ( "Failed to create dns-test pod: %v" , err )
return false
}
defer podClient . Delete ( pod . Name )
waitForPodRunning ( c , pod . Name )
pod , err = podClient . Get ( pod . Name )
if err != nil {
glog . Errorf ( "Failed to get pod: %v" , err )
return false
}
// Try to find results for each expected name.
var failed [ ] string
for try := 1 ; try < 100 ; try ++ {
failed = [ ] string { }
for _ , name := range namesToResolve {
_ , err := c . Get ( ) .
2014-12-26 20:06:25 +00:00
Prefix ( "proxy" ) .
Resource ( "pods" ) .
2014-11-23 09:13:02 +00:00
Namespace ( "default" ) .
2014-12-26 20:06:25 +00:00
Name ( pod . Name ) .
Suffix ( "results" , name ) .
2014-11-23 09:13:02 +00:00
Do ( ) . Raw ( )
if err != nil {
failed = append ( failed , name )
}
}
if len ( failed ) == 0 {
break
}
time . Sleep ( 3 * time . Second )
}
if len ( failed ) != 0 {
glog . Errorf ( "DNS failed for: %v" , failed )
return false
}
// TODO: probe from the host, too.
glog . Info ( "DNS probes succeeded" )
return true
}
2014-10-14 19:28:45 +00:00
func main ( ) {
flag . Parse ( )
2014-12-09 21:10:59 +00:00
goruntime . GOMAXPROCS ( goruntime . NumCPU ( ) )
2014-10-14 19:28:45 +00:00
util . ReallyCrash = true
util . InitLogs ( )
defer util . FlushLogs ( )
go func ( ) {
defer util . FlushLogs ( )
2014-12-21 14:16:27 +00:00
time . Sleep ( 5 * time . Minute )
glog . Fatalf ( "This test has timed out. Cleanup not guaranteed." )
2014-10-14 19:28:45 +00:00
} ( )
c := loadClientOrDie ( )
2014-12-02 20:50:48 +00:00
// Define the tests. Important: for a clean test grid, please keep ids for a test constant.
tests := [ ] TestSpec {
{ TestKubernetesROService , "TestKubernetesROService" , 1 } ,
{ TestKubeletSendsEvent , "TestKubeletSendsEvent" , 2 } ,
{ TestImportantURLs , "TestImportantURLs" , 3 } ,
{ TestPodUpdate , "TestPodUpdate" , 4 } ,
{ TestNetwork , "TestNetwork" , 5 } ,
2014-11-23 09:13:02 +00:00
{ TestClusterDNS , "TestClusterDNS" , 6 } ,
2014-10-14 19:28:45 +00:00
}
2014-12-02 20:50:48 +00:00
info := [ ] TestInfo { }
2014-10-14 19:28:45 +00:00
passed := true
2014-12-09 23:07:54 +00:00
for i , test := range tests {
glog . Infof ( "Running test %d" , i + 1 )
2014-12-02 20:50:48 +00:00
testPassed := test . test ( c )
2014-10-14 19:28:45 +00:00
if ! testPassed {
2014-12-09 23:07:54 +00:00
glog . Infof ( " test %d failed" , i + 1 )
2014-10-14 19:28:45 +00:00
passed = false
2014-12-09 23:07:54 +00:00
} else {
glog . Infof ( " test %d passed" , i + 1 )
}
// TODO: clean up objects created during a test after the test, so cases
2014-11-18 08:22:01 +00:00
// are independent.
2014-12-02 20:50:48 +00:00
info = append ( info , TestInfo { testPassed , test } )
2014-10-14 19:28:45 +00:00
}
2014-12-02 20:50:48 +00:00
outputTAPSummary ( info )
2014-10-14 19:28:45 +00:00
if ! passed {
2014-12-09 23:07:54 +00:00
glog . Fatalf ( "At least one test failed" )
} else {
glog . Infof ( "All tests pass" )
2014-10-14 19:28:45 +00:00
}
}