github.com/technosophos/deis@v1.7.1-0.20150915173815-f9005256004b/tests/utils/itutils.go (about)

     1  package utils
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"log"
     9  	"math/rand"
    10  	"net/http"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"strings"
    15  	"testing"
    16  	"text/template"
    17  	"time"
    18  )
    19  
    20  // Deis points to the CLI used to run tests.
    21  var Deis = os.Getenv("DEIS_BINARY") + " "
    22  
    23  func init() {
    24  	if Deis == " " {
    25  		Deis = "deis "
    26  	}
    27  }
    28  
    29  // DeisTestConfig allows tests to be repeated against different
    30  // targets, with different example apps, using specific credentials, and so on.
    31  type DeisTestConfig struct {
    32  	AuthKey            string
    33  	Hosts              string
    34  	Domain             string
    35  	SSHKey             string
    36  	ClusterName        string
    37  	UserName           string
    38  	Password           string
    39  	NewPassword        string
    40  	NewOwner           string
    41  	Email              string
    42  	ExampleApp         string
    43  	AppDomain          string
    44  	AppName            string
    45  	ProcessNum         string
    46  	ImageID            string
    47  	Version            string
    48  	AppUser            string
    49  	SSLCertificatePath string
    50  	SSLKeyPath         string
    51  }
    52  
    53  // randomApp is used for the test run if DEIS_TEST_APP isn't set
    54  var randomApp = GetRandomApp()
    55  
    56  // GetGlobalConfig returns a test configuration object.
    57  func GetGlobalConfig() *DeisTestConfig {
    58  	authKey := os.Getenv("DEIS_TEST_AUTH_KEY")
    59  	if authKey == "" {
    60  		authKey = "deis"
    61  	}
    62  	hosts := os.Getenv("DEIS_TEST_HOSTS")
    63  	if hosts == "" {
    64  		hosts = "172.17.8.100"
    65  	}
    66  	domain := os.Getenv("DEIS_TEST_DOMAIN")
    67  	if domain == "" {
    68  		domain = "local3.deisapp.com"
    69  	}
    70  	sshKey := os.Getenv("DEIS_TEST_SSH_KEY")
    71  	if sshKey == "" {
    72  		sshKey = "~/.vagrant.d/insecure_private_key"
    73  	}
    74  	exampleApp := os.Getenv("DEIS_TEST_APP")
    75  	if exampleApp == "" {
    76  		exampleApp = randomApp
    77  	}
    78  	appDomain := os.Getenv("DEIS_TEST_APP_DOMAIN")
    79  	if appDomain == "" {
    80  		appDomain = fmt.Sprintf("test.%s", domain)
    81  	}
    82  
    83  	// generate a self-signed certifcate for the app domain
    84  	keyOut, err := filepath.Abs(appDomain + ".key")
    85  	if err != nil {
    86  		log.Fatal(err)
    87  	}
    88  	certOut, err := filepath.Abs(appDomain + ".cert")
    89  	if err != nil {
    90  		log.Fatal(err)
    91  	}
    92  	cmd := exec.Command("openssl", "req", "-new", "-newkey", "rsa:4096", "-nodes", "-x509",
    93  		"-days", "1",
    94  		"-subj", fmt.Sprintf("/C=US/ST=Colorado/L=Boulder/CN=%s", appDomain),
    95  		"-keyout", keyOut,
    96  		"-out", certOut)
    97  	if err := cmd.Start(); err != nil {
    98  		log.Fatal(err)
    99  	}
   100  	if err := cmd.Wait(); err != nil {
   101  		log.Fatal(err)
   102  	}
   103  
   104  	var envCfg = DeisTestConfig{
   105  		AuthKey:            authKey,
   106  		Hosts:              hosts,
   107  		Domain:             domain,
   108  		SSHKey:             sshKey,
   109  		ClusterName:        "dev",
   110  		UserName:           "test",
   111  		Password:           "asdf1234",
   112  		Email:              "test@test.co.nz",
   113  		ExampleApp:         exampleApp,
   114  		AppDomain:          appDomain,
   115  		AppName:            "sample",
   116  		ProcessNum:         "2",
   117  		ImageID:            "buildtest",
   118  		Version:            "2",
   119  		AppUser:            "test1",
   120  		SSLCertificatePath: certOut,
   121  		SSLKeyPath:         keyOut,
   122  	}
   123  	return &envCfg
   124  }
   125  
   126  // HTTPClient returns a client for use with the integration tests.
   127  func HTTPClient() *http.Client {
   128  	// disable security check for self-signed certificates
   129  	tr := &http.Transport{
   130  		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
   131  	}
   132  	return &http.Client{Transport: tr}
   133  }
   134  
   135  func doCurl(url string) ([]byte, error) {
   136  	client := HTTPClient()
   137  	response, err := client.Get(url)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	defer response.Body.Close()
   142  	body, err := ioutil.ReadAll(response.Body)
   143  
   144  	if !strings.Contains(string(body), "Powered by") {
   145  		return nil, fmt.Errorf("App not started (%d)\nBody: (%s)", response.StatusCode, string(body))
   146  	}
   147  
   148  	return body, nil
   149  }
   150  
   151  // Curl connects to an endpoint to see if the endpoint is responding.
   152  func Curl(t *testing.T, url string) {
   153  	CurlWithFail(t, url, false, "")
   154  }
   155  
   156  // CurlApp is a convenience function to see if the example app is running.
   157  func CurlApp(t *testing.T, cfg DeisTestConfig) {
   158  	CurlWithFail(t, fmt.Sprintf("http://%s.%s", cfg.AppName, cfg.Domain), false, "")
   159  }
   160  
   161  // CurlWithFail connects to a Deis endpoint to see if the example app is running.
   162  func CurlWithFail(t *testing.T, url string, failFlag bool, expect string) {
   163  	// FIXME: try the curl a few times
   164  	for i := 0; i < 20; i++ {
   165  		body, err := doCurl(url)
   166  		if err == nil {
   167  			fmt.Println(string(body))
   168  			return
   169  		}
   170  		time.Sleep(1 * time.Second)
   171  	}
   172  
   173  	// once more to fail with an error
   174  	body, err := doCurl(url)
   175  
   176  	switch failFlag {
   177  	case true:
   178  		if err != nil {
   179  			if strings.Contains(string(err.Error()), expect) {
   180  				fmt.Println("(Error expected...ok) " + expect)
   181  			} else {
   182  				t.Fatal(err)
   183  			}
   184  		} else {
   185  			if strings.Contains(string(body), expect) {
   186  				fmt.Println("(Error expected...ok) " + expect)
   187  			} else {
   188  				t.Fatal(err)
   189  			}
   190  		}
   191  	case false:
   192  		if err != nil {
   193  			t.Fatal(err)
   194  		} else {
   195  			fmt.Println(string(body))
   196  		}
   197  	}
   198  }
   199  
   200  // CheckList executes a command and optionally tests whether its output does
   201  // or does not contain a given string.
   202  func CheckList(
   203  	t *testing.T, cmd string, params interface{}, contain string, notflag bool) {
   204  	var cmdBuf bytes.Buffer
   205  	tmpl := template.Must(template.New("cmd").Parse(cmd))
   206  	if err := tmpl.Execute(&cmdBuf, params); err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	cmdString := cmdBuf.String()
   210  	fmt.Println(cmdString)
   211  	var cmdl *exec.Cmd
   212  	if strings.Contains(cmd, "cat") {
   213  		cmdl = exec.Command("sh", "-c", cmdString)
   214  	} else {
   215  		cmdl = exec.Command("sh", "-c", Deis+cmdString)
   216  	}
   217  	stdout, _, err := RunCommandWithStdoutStderr(cmdl)
   218  	if err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	if notflag && strings.Contains(stdout.String(), contain) {
   222  		t.Fatalf("Didn't expect '%s' in command output:\n%v", contain, stdout)
   223  	}
   224  	if !notflag && !strings.Contains(stdout.String(), contain) {
   225  		t.Fatalf("Expected '%s' in command output:\n%v", contain, stdout)
   226  	}
   227  }
   228  
   229  // Execute takes command string and parameters required to execute the command,
   230  // a failflag to check whether the command is expected to fail, and an expect
   231  // string to check whether the command has failed according to failflag.
   232  //
   233  // If failflag is true and the command failed, check the stdout and stderr for
   234  // the expect string.
   235  func Execute(t *testing.T, cmd string, params interface{}, failFlag bool, expect string) {
   236  	var cmdBuf bytes.Buffer
   237  	tmpl := template.Must(template.New("cmd").Parse(cmd))
   238  	if err := tmpl.Execute(&cmdBuf, params); err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	cmdString := cmdBuf.String()
   242  	fmt.Println(cmdString)
   243  	var cmdl *exec.Cmd
   244  	if strings.Contains(cmd, "git ") {
   245  		cmdl = exec.Command("sh", "-c", cmdString)
   246  	} else {
   247  		cmdl = exec.Command("sh", "-c", Deis+cmdString)
   248  	}
   249  
   250  	switch failFlag {
   251  	case true:
   252  		if stdout, stderr, err := RunCommandWithStdoutStderr(cmdl); err != nil {
   253  			if strings.Contains(stdout.String(), expect) || strings.Contains(stderr.String(), expect) {
   254  				fmt.Println("(Error expected...ok)")
   255  			} else {
   256  				t.Fatal(err)
   257  			}
   258  		} else {
   259  			if strings.Contains(stdout.String(), expect) || strings.Contains(stderr.String(), expect) {
   260  				fmt.Println("(Error expected...ok)" + expect)
   261  			} else {
   262  				t.Fatal(err)
   263  			}
   264  		}
   265  	case false:
   266  		stdout, stderr, err := RunCommandWithStdoutStderr(cmdl)
   267  		if err != nil {
   268  			t.Fatal(err)
   269  		}
   270  
   271  		if containsWarning(stdout.String()) || containsWarning(stderr.String()) {
   272  			t.Fatal("Warning found in output, aborting")
   273  		}
   274  
   275  		fmt.Println("ok")
   276  	}
   277  }
   278  
   279  // AppsDestroyTest destroys a Deis app and checks that it was successful.
   280  func AppsDestroyTest(t *testing.T, params *DeisTestConfig) {
   281  	fmt.Printf("destroying app %s...\n", params.ExampleApp)
   282  	cmd := "apps:destroy --app={{.AppName}} --confirm={{.AppName}}"
   283  	if err := Chdir(params.ExampleApp); err != nil {
   284  		t.Fatal(err)
   285  	}
   286  	Execute(t, cmd, params, false, "")
   287  	if err := Chdir(".."); err != nil {
   288  		t.Fatal(err)
   289  	}
   290  	if err := Rmdir(params.ExampleApp); err != nil {
   291  		t.Fatal(err)
   292  	}
   293  }
   294  
   295  // GetRandomApp returns a known working example app at random for testing.
   296  func GetRandomApp() string {
   297  	rand.Seed(int64(time.Now().Unix()))
   298  	apps := []string{
   299  		"example-clojure-ring",
   300  		// "example-dart",
   301  		"example-dockerfile-python",
   302  		"example-go",
   303  		"example-java-jetty",
   304  		"example-nodejs-express",
   305  		// "example-php",
   306  		"example-play",
   307  		"example-python-django",
   308  		"example-python-flask",
   309  		"example-ruby-sinatra",
   310  		"example-scala",
   311  		"example-dockerfile-http",
   312  	}
   313  	return apps[rand.Intn(len(apps))]
   314  }
   315  
   316  func containsWarning(out string) bool {
   317  	if strings.Contains(out, "WARNING") {
   318  		return true
   319  	}
   320  
   321  	return false
   322  }