2016-02-22 18:52:20 +00:00
/ *
Copyright 2016 The Kubernetes Authors 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 e2e_node
import (
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
2016-05-23 20:16:47 +00:00
"path"
2016-02-22 18:52:20 +00:00
"strings"
"time"
"github.com/golang/glog"
)
2016-03-04 22:07:35 +00:00
var serverStartTimeout = flag . Duration ( "server-start-timeout" , time . Second * 120 , "Time to wait for each server to become healthy." )
2016-05-23 20:16:47 +00:00
var reportDir = flag . String ( "report-dir" , "" , "Path to the directory where the JUnit XML reports should be saved. Default is empty, which doesn't generate these reports." )
2016-02-22 18:52:20 +00:00
type e2eService struct {
2016-05-23 20:16:47 +00:00
etcdCmd * exec . Cmd
etcdDataDir string
apiServerCmd * exec . Cmd
kubeletCmd * exec . Cmd
kubeletStaticPodDir string
nodeName string
2016-02-22 18:52:20 +00:00
}
2016-02-24 21:12:42 +00:00
func newE2eService ( nodeName string ) * e2eService {
return & e2eService { nodeName : nodeName }
2016-02-22 18:52:20 +00:00
}
func ( es * e2eService ) start ( ) error {
if _ , err := getK8sBin ( "kubelet" ) ; err != nil {
return err
}
if _ , err := getK8sBin ( "kube-apiserver" ) ; err != nil {
return err
}
cmd , err := es . startEtcd ( )
if err != nil {
return err
}
es . etcdCmd = cmd
cmd , err = es . startApiServer ( )
if err != nil {
return err
}
es . apiServerCmd = cmd
cmd , err = es . startKubeletServer ( )
if err != nil {
return err
}
es . kubeletCmd = cmd
return nil
}
func ( es * e2eService ) stop ( ) {
if es . kubeletCmd != nil {
err := es . kubeletCmd . Process . Kill ( )
if err != nil {
glog . Errorf ( "Failed to stop kubelet.\n%v" , err )
}
}
2016-04-18 05:00:59 +00:00
if es . kubeletStaticPodDir != "" {
err := os . RemoveAll ( es . kubeletStaticPodDir )
if err != nil {
glog . Errorf ( "Failed to delete kubelet static pod directory %s.\n%v" , es . kubeletStaticPodDir , err )
}
}
2016-02-22 18:52:20 +00:00
if es . apiServerCmd != nil {
err := es . apiServerCmd . Process . Kill ( )
if err != nil {
2016-04-18 20:56:07 +00:00
glog . Errorf ( "Failed to stop kube-apiserver.\n%v" , err )
2016-02-22 18:52:20 +00:00
}
}
if es . etcdCmd != nil {
err := es . etcdCmd . Process . Kill ( )
if err != nil {
glog . Errorf ( "Failed to stop etcd.\n%v" , err )
}
}
if es . etcdDataDir != "" {
err := os . RemoveAll ( es . etcdDataDir )
if err != nil {
glog . Errorf ( "Failed to delete etcd data directory %s.\n%v" , es . etcdDataDir , err )
}
}
}
func ( es * e2eService ) startEtcd ( ) ( * exec . Cmd , error ) {
dataDir , err := ioutil . TempDir ( "" , "node-e2e" )
if err != nil {
return nil , err
}
es . etcdDataDir = dataDir
2016-02-26 23:06:25 +00:00
cmd := exec . Command ( "etcd" )
// Execute etcd in the data directory instead of using --data-dir because the flag sometimes requires additional
// configuration (e.g. --name in version 0.4.9)
cmd . Dir = es . etcdDataDir
hcc := newHealthCheckCommand (
"http://127.0.0.1:4001/v2/keys/" , // Trailing slash is required,
cmd ,
2016-05-23 20:16:47 +00:00
"etcd.log" )
2016-02-26 23:06:25 +00:00
return cmd , es . startServer ( hcc )
2016-02-22 18:52:20 +00:00
}
func ( es * e2eService ) startApiServer ( ) ( * exec . Cmd , error ) {
2016-02-26 23:06:25 +00:00
cmd := exec . Command ( "sudo" , getApiServerBin ( ) ,
"--etcd-servers" , "http://127.0.0.1:4001" ,
"--insecure-bind-address" , "0.0.0.0" ,
"--service-cluster-ip-range" , "10.0.0.1/24" ,
2016-04-07 00:11:13 +00:00
"--kubelet-port" , "10250" ,
"--allow-privileged" , "true" ,
2016-05-23 20:16:47 +00:00
"--v" , "8" , "--logtostderr" ,
2016-04-07 00:11:13 +00:00
)
2016-02-26 23:06:25 +00:00
hcc := newHealthCheckCommand (
"http://127.0.0.1:8080/healthz" ,
cmd ,
2016-05-23 20:16:47 +00:00
"kube-apiserver.log" )
2016-02-26 23:06:25 +00:00
return cmd , es . startServer ( hcc )
2016-02-22 18:52:20 +00:00
}
func ( es * e2eService ) startKubeletServer ( ) ( * exec . Cmd , error ) {
2016-04-18 05:00:59 +00:00
dataDir , err := ioutil . TempDir ( "" , "node-e2e-pod" )
if err != nil {
return nil , err
}
es . kubeletStaticPodDir = dataDir
2016-02-26 23:06:25 +00:00
cmd := exec . Command ( "sudo" , getKubeletServerBin ( ) ,
"--api-servers" , "http://127.0.0.1:8080" ,
"--address" , "0.0.0.0" ,
"--port" , "10250" ,
2016-03-14 18:18:27 +00:00
"--hostname-override" , es . nodeName , // Required because hostname is inconsistent across hosts
"--volume-stats-agg-period" , "10s" , // Aggregate volumes frequently so tests don't need to wait as long
2016-04-07 00:11:13 +00:00
"--allow-privileged" , "true" ,
2016-04-21 22:34:28 +00:00
"--serialize-image-pulls" , "false" ,
2016-04-18 05:00:59 +00:00
"--config" , es . kubeletStaticPodDir ,
"--file-check-frequency" , "10s" , // Check file frequently so tests won't wait too long
2016-05-23 20:16:47 +00:00
"--v" , "8" , "--logtostderr" ,
2016-03-14 18:18:27 +00:00
)
2016-02-26 23:06:25 +00:00
hcc := newHealthCheckCommand (
"http://127.0.0.1:10255/healthz" ,
cmd ,
2016-05-23 20:16:47 +00:00
"kubelet.log" )
2016-02-26 23:06:25 +00:00
return cmd , es . startServer ( hcc )
2016-02-22 18:52:20 +00:00
}
2016-02-26 23:06:25 +00:00
func ( es * e2eService ) startServer ( cmd * healthCheckCommand ) error {
2016-02-22 18:52:20 +00:00
cmdErrorChan := make ( chan error )
go func ( ) {
2016-05-23 20:16:47 +00:00
defer close ( cmdErrorChan )
// Create the output filename
outPath := path . Join ( * reportDir , cmd . outputFilename )
outfile , err := os . Create ( outPath )
if err != nil {
cmdErrorChan <- fmt . Errorf ( "Failed to create file %s for `%s` %v." , outPath , cmd , err )
return
}
defer outfile . Close ( )
defer outfile . Sync ( )
// Set the command to write the output file
cmd . Cmd . Stdout = outfile
cmd . Cmd . Stderr = outfile
// Run the command
err = cmd . Run ( )
2016-02-22 18:52:20 +00:00
if err != nil {
2016-05-23 20:16:47 +00:00
cmdErrorChan <- fmt . Errorf ( "%s Failed with error \"%v\". Output written to: %s" , cmd , err , outPath )
return
2016-02-22 18:52:20 +00:00
}
} ( )
endTime := time . Now ( ) . Add ( * serverStartTimeout )
for endTime . After ( time . Now ( ) ) {
select {
case err := <- cmdErrorChan :
2016-02-26 23:06:25 +00:00
return err
2016-02-22 18:52:20 +00:00
case <- time . After ( time . Second ) :
2016-02-26 23:06:25 +00:00
resp , err := http . Get ( cmd . HealthCheckUrl )
2016-02-22 18:52:20 +00:00
if err == nil && resp . StatusCode == http . StatusOK {
2016-02-26 23:06:25 +00:00
return nil
2016-02-22 18:52:20 +00:00
}
}
}
2016-02-26 23:06:25 +00:00
return fmt . Errorf ( "Timeout waiting for service %s" , cmd )
2016-02-22 18:52:20 +00:00
}
type healthCheckCommand struct {
2016-02-26 23:06:25 +00:00
* exec . Cmd
HealthCheckUrl string
2016-05-23 20:16:47 +00:00
outputFilename string
2016-02-26 23:06:25 +00:00
}
2016-05-23 20:16:47 +00:00
func newHealthCheckCommand ( healthCheckUrl string , cmd * exec . Cmd , filename string ) * healthCheckCommand {
2016-02-26 23:06:25 +00:00
return & healthCheckCommand {
HealthCheckUrl : healthCheckUrl ,
Cmd : cmd ,
2016-05-23 20:16:47 +00:00
outputFilename : filename ,
2016-02-26 23:06:25 +00:00
}
2016-02-22 18:52:20 +00:00
}
func ( hcc * healthCheckCommand ) String ( ) string {
2016-02-26 23:06:25 +00:00
return fmt . Sprintf ( "`%s %s` health-check: %s" , hcc . Path , strings . Join ( hcc . Args , " " ) , hcc . HealthCheckUrl )
2016-02-22 18:52:20 +00:00
}