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