2014-10-22 20:49:40 -04:00
/ *
2015-05-01 12:19:44 -04:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2014-10-22 20:49:40 -04: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 .
* /
// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main
import (
"flag"
"fmt"
2014-12-15 19:03:11 -05:00
"io"
2014-10-22 20:49:40 -04:00
"log"
2014-12-15 19:03:11 -05:00
"net/http"
2014-10-22 20:49:40 -04:00
"os"
"os/exec"
"path"
"path/filepath"
2015-01-04 10:56:20 -05:00
"strconv"
2014-10-22 20:49:40 -04:00
"strings"
2015-09-15 18:03:34 -04:00
"time"
2014-10-22 20:49:40 -04:00
)
var (
2014-11-10 20:46:11 -05:00
isup = flag . Bool ( "isup" , false , "Check to see if the e2e cluster is up, then exit." )
build = flag . Bool ( "build" , false , "If true, build a new release. Otherwise, use whatever is there." )
2014-12-15 19:03:11 -05:00
version = flag . String ( "version" , "" , "The version to be tested (including the leading 'v'). An empty string defaults to the local build, but it can be set to any release (e.g. v0.4.4, v0.6.0)." )
2014-11-10 20:46:11 -05:00
up = flag . Bool ( "up" , false , "If true, start the the e2e cluster. If cluster is already up, recreate it." )
push = flag . Bool ( "push" , false , "If true, push to e2e cluster. Has no effect if -up is true." )
2014-12-08 17:55:47 -05:00
pushup = flag . Bool ( "pushup" , false , "If true, push to e2e cluster if it's up, otherwise start the e2e cluster." )
2014-11-10 20:46:11 -05:00
down = flag . Bool ( "down" , false , "If true, tear down the cluster before exiting." )
2015-02-04 19:57:53 -05:00
test = flag . Bool ( "test" , false , "Run Ginkgo tests." )
2015-02-17 20:39:44 -05:00
testArgs = flag . String ( "test_args" , "" , "Space-separated list of arguments to pass to Ginkgo test runner." )
2014-11-10 20:46:11 -05:00
root = flag . String ( "root" , absOrDie ( filepath . Clean ( filepath . Join ( path . Base ( os . Args [ 0 ] ) , ".." ) ) ) , "Root directory of kubernetes repository." )
verbose = flag . Bool ( "v" , false , "If true, print all command output." )
checkVersionSkew = flag . Bool ( "check_version_skew" , true , "" +
"By default, verify that client and server have exact version match. " +
"You can explicitly set to false if you're, e.g., testing client changes " +
"for which the server version doesn't make a difference." )
2014-11-07 19:14:29 -05:00
ctlCmd = flag . String ( "ctl" , "" , "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)" )
2014-10-22 20:49:40 -04:00
)
2014-12-15 19:03:11 -05:00
const (
serverTarName = "kubernetes-server-linux-amd64.tar.gz"
saltTarName = "kubernetes-salt.tar.gz"
downloadDirName = "_output/downloads"
tarDirName = "server"
tempDirName = "upgrade-e2e-temp-dir"
2015-11-30 17:31:32 -05:00
minNodeCount = 2
2014-12-15 19:03:11 -05:00
)
var (
// Root directory of the specified cluster version, rather than of where
// this script is being run from.
versionRoot = * root
)
2014-10-22 20:49:40 -04:00
func absOrDie ( path string ) string {
out , err := filepath . Abs ( path )
if err != nil {
panic ( err )
}
return out
}
2014-12-08 13:13:37 -05:00
type TestResult struct {
Pass int
Fail int
}
type ResultsByTest map [ string ] TestResult
2014-10-22 20:49:40 -04:00
func main ( ) {
2015-09-15 18:03:34 -04:00
log . SetFlags ( log . LstdFlags | log . Lshortfile )
2014-10-22 20:49:40 -04:00
flag . Parse ( )
2014-10-23 17:47:30 -04:00
if * isup {
status := 1
2014-12-07 18:29:30 -05:00
if IsUp ( ) {
2014-10-23 17:47:30 -04:00
status = 0
log . Printf ( "Cluster is UP" )
} else {
log . Printf ( "Cluster is DOWN" )
}
os . Exit ( status )
}
2014-10-22 20:49:40 -04:00
if * build {
2015-02-06 17:33:57 -05:00
// The build-release script needs stdin to ask the user whether
// it's OK to download the docker image.
cmd := exec . Command ( path . Join ( * root , "hack/e2e-internal/build-release.sh" ) )
cmd . Stdin = os . Stdin
if ! finishRunning ( "build-release" , cmd ) {
2014-10-22 20:49:40 -04:00
log . Fatal ( "Error building. Aborting." )
}
}
2014-12-15 19:03:11 -05:00
if * version != "" {
// If the desired version isn't available already, do whatever's needed
// to make it available. Once done, update the root directory for client
// tools to be the root of the release directory so that the given
// release's tools will be used. We can't use this new root for
// everything because it likely doesn't have the hack/ directory in it.
if newVersionRoot , err := PrepareVersion ( * version ) ; err != nil {
log . Fatalf ( "Error preparing a binary of version %s: %s. Aborting." , * version , err )
} else {
versionRoot = newVersionRoot
2015-02-05 03:00:28 -05:00
os . Setenv ( "KUBE_VERSION_ROOT" , newVersionRoot )
2014-12-15 19:03:11 -05:00
}
}
2015-02-05 03:00:28 -05:00
os . Setenv ( "KUBECTL" , versionRoot + ` /cluster/kubectl.sh ` + kubectlArgs ( ) )
2016-02-17 19:49:07 -05:00
os . Setenv ( "KUBE_TEST_DEBUG" , "y" )
2015-02-05 03:00:28 -05:00
2014-12-08 17:55:47 -05:00
if * pushup {
if IsUp ( ) {
log . Printf ( "e2e cluster is up, pushing." )
* up = false
* push = true
} else {
log . Printf ( "e2e cluster is down, creating." )
* up = true
* push = false
}
}
2014-10-22 20:49:40 -04:00
if * up {
if ! Up ( ) {
log . Fatal ( "Error starting e2e cluster. Aborting." )
}
} else if * push {
2015-02-06 17:33:57 -05:00
if ! finishRunning ( "push" , exec . Command ( path . Join ( * root , "hack/e2e-internal/e2e-push.sh" ) ) ) {
2014-10-22 20:49:40 -04:00
log . Fatal ( "Error pushing e2e cluster. Aborting." )
}
}
2015-02-11 16:11:03 -05:00
success := true
2014-11-07 19:14:29 -05:00
switch {
case * ctlCmd != "" :
2015-02-06 17:33:57 -05:00
ctlArgs := strings . Fields ( * ctlCmd )
2015-02-06 17:27:59 -05:00
os . Setenv ( "KUBE_CONFIG_FILE" , "config-test.sh" )
2015-02-11 16:11:03 -05:00
success = finishRunning ( "'kubectl " + * ctlCmd + "'" , exec . Command ( path . Join ( versionRoot , "cluster/kubectl.sh" ) , ctlArgs ... ) )
2015-02-04 19:57:53 -05:00
case * test :
2015-02-11 16:11:03 -05:00
success = Test ( )
2014-10-22 20:49:40 -04:00
}
if * down {
TearDown ( )
}
2015-02-11 16:11:03 -05:00
if ! success {
2014-10-22 20:49:40 -04:00
os . Exit ( 1 )
}
}
2015-01-14 18:57:28 -05:00
func TearDown ( ) bool {
2015-02-06 17:33:57 -05:00
return finishRunning ( "teardown" , exec . Command ( path . Join ( * root , "hack/e2e-internal/e2e-down.sh" ) ) )
2014-10-22 20:49:40 -04:00
}
2015-01-14 18:57:28 -05:00
// Up brings an e2e cluster up, recreating it if one is already running.
2014-10-22 20:49:40 -04:00
func Up ( ) bool {
2015-01-14 18:57:28 -05:00
if IsUp ( ) {
log . Printf ( "e2e cluster already running; will teardown" )
if res := TearDown ( ) ; ! res {
return false
}
2014-10-22 20:49:40 -04:00
}
2015-02-06 17:33:57 -05:00
return finishRunning ( "up" , exec . Command ( path . Join ( * root , "hack/e2e-internal/e2e-up.sh" ) ) )
2014-10-22 20:49:40 -04:00
}
2015-01-15 19:43:03 -05:00
// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize ( ) {
2015-11-30 17:31:32 -05:00
// Check that there are at least minNodeCount nodes running
2015-02-11 17:57:34 -05:00
cmd := exec . Command ( path . Join ( * root , "hack/e2e-internal/e2e-cluster-size.sh" ) )
if * verbose {
cmd . Stderr = os . Stderr
}
stdout , err := cmd . Output ( )
if err != nil {
2015-05-10 00:08:55 -04:00
log . Fatalf ( "Could not get nodes to validate cluster size (%s)" , err )
2015-01-15 19:43:03 -05:00
}
2015-02-11 17:57:34 -05:00
numNodes , err := strconv . Atoi ( strings . TrimSpace ( string ( stdout ) ) )
2015-01-15 19:43:03 -05:00
if err != nil {
log . Fatalf ( "Could not count number of nodes to validate cluster size (%s)" , err )
}
2015-11-30 17:31:32 -05:00
if numNodes < minNodeCount {
log . Fatalf ( "Cluster size (%d) is too small to run e2e tests. %d Nodes are required." , numNodes , minNodeCount )
2015-01-15 19:43:03 -05:00
}
}
2014-12-07 18:29:30 -05:00
// Is the e2e cluster up?
func IsUp ( ) bool {
2015-02-06 17:33:57 -05:00
return finishRunning ( "get status" , exec . Command ( path . Join ( * root , "hack/e2e-internal/e2e-status.sh" ) ) )
2014-12-07 18:29:30 -05:00
}
2014-12-15 19:03:11 -05:00
// PrepareVersion makes sure that the specified release version is locally
// available and ready to be used by kube-up or kube-push. Returns the director
// path of the release.
func PrepareVersion ( version string ) ( string , error ) {
if version == "" {
// Assume that the build flag already handled building a local binary.
return * root , nil
}
// If the version isn't a local build, try fetching the release from Google
// Cloud Storage.
downloadDir := filepath . Join ( * root , downloadDirName )
if err := os . MkdirAll ( downloadDir , 0755 ) ; err != nil {
return "" , err
}
localReleaseDir := filepath . Join ( downloadDir , version )
if err := os . MkdirAll ( localReleaseDir , 0755 ) ; err != nil {
return "" , err
}
remoteReleaseTar := fmt . Sprintf ( "https://storage.googleapis.com/kubernetes-release/release/%s/kubernetes.tar.gz" , version )
localReleaseTar := filepath . Join ( downloadDir , fmt . Sprintf ( "kubernetes-%s.tar.gz" , version ) )
if _ , err := os . Stat ( localReleaseTar ) ; os . IsNotExist ( err ) {
out , err := os . Create ( localReleaseTar )
if err != nil {
return "" , err
}
resp , err := http . Get ( remoteReleaseTar )
if err != nil {
out . Close ( )
return "" , err
}
defer resp . Body . Close ( )
io . Copy ( out , resp . Body )
if err != nil {
out . Close ( )
return "" , err
}
out . Close ( )
}
2015-02-06 17:33:57 -05:00
if ! finishRunning ( "untarRelease" , exec . Command ( "tar" , "-C" , localReleaseDir , "-zxf" , localReleaseTar , "--strip-components=1" ) ) {
2014-12-15 19:03:11 -05:00
log . Fatal ( "Failed to untar release. Aborting." )
}
// Now that we have the binaries saved locally, use the path to the untarred
// directory as the "root" path for future operations.
return localReleaseDir , nil
2014-10-22 20:49:40 -04:00
}
2015-02-04 19:57:53 -05:00
func Test ( ) bool {
2014-12-07 18:29:30 -05:00
if ! IsUp ( ) {
log . Fatal ( "Testing requested, but e2e cluster not up!" )
}
2015-01-15 19:43:03 -05:00
ValidateClusterSize ( )
2015-02-17 20:39:44 -05:00
return finishRunning ( "Ginkgo tests" , exec . Command ( filepath . Join ( * root , "hack/ginkgo-e2e.sh" ) , strings . Fields ( * testArgs ) ... ) )
2014-10-22 20:49:40 -04:00
}
2015-02-13 16:53:27 -05:00
func finishRunning ( stepName string , cmd * exec . Cmd ) bool {
2014-10-22 20:49:40 -04:00
if * verbose {
2015-02-13 16:53:27 -05:00
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
2014-10-22 20:49:40 -04:00
}
2015-02-13 16:53:27 -05:00
log . Printf ( "Running: %v" , stepName )
2015-09-15 18:03:34 -04:00
defer func ( start time . Time ) {
log . Printf ( "Step '%s' finished in %s" , stepName , time . Since ( start ) )
} ( time . Now ( ) )
2014-10-22 20:49:40 -04:00
if err := cmd . Run ( ) ; err != nil {
log . Printf ( "Error running %v: %v" , stepName , err )
2015-02-13 16:53:27 -05:00
return false
2014-10-22 20:49:40 -04:00
}
2015-02-13 16:53:27 -05:00
return true
2015-02-06 17:33:57 -05:00
}
2014-11-12 16:30:53 -05:00
// returns either "", or a list of args intended for appending with the
2015-08-08 17:29:57 -04:00
// kubectl command (beginning with a space).
2014-11-12 16:30:53 -05:00
func kubectlArgs ( ) string {
if * checkVersionSkew {
2015-01-14 16:10:45 -05:00
return " --match-server-version"
2014-11-12 16:30:53 -05:00
}
2015-01-14 16:10:45 -05:00
return ""
2014-11-12 16:30:53 -05:00
}