github.com/buildpack/pack@v0.5.0/app/run_test.go (about)

     1  package app_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"math/rand"
     9  	"net/http"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/docker/docker/client"
    15  	"github.com/heroku/color"
    16  	"github.com/sclevine/spec"
    17  	"github.com/sclevine/spec/report"
    18  
    19  	"github.com/buildpack/pack/app"
    20  	"github.com/buildpack/pack/internal/fakes"
    21  	h "github.com/buildpack/pack/testhelpers"
    22  )
    23  
    24  func TestApp(t *testing.T) {
    25  	h.RequireDocker(t)
    26  	color.Disable(true)
    27  	defer func() { color.Disable(false) }()
    28  	rand.Seed(time.Now().UTC().UnixNano())
    29  	spec.Run(t, "app", testApp, spec.Sequential(), spec.Report(report.Terminal{}))
    30  }
    31  
    32  func testApp(t *testing.T, when spec.G, it spec.S) {
    33  	when("#Run", func() {
    34  		var (
    35  			subject *app.Image
    36  			docker  *client.Client
    37  			err     error
    38  			errBuf  bytes.Buffer
    39  			repo    string
    40  		)
    41  
    42  		it.Before(func() {
    43  			docker, err = client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.38"))
    44  			h.AssertNil(t, err)
    45  
    46  			repo = "some-org/" + h.RandString(10)
    47  
    48  			logger := fakes.NewFakeLogger(&errBuf)
    49  
    50  			subject = &app.Image{
    51  				RepoName: repo,
    52  				Logger:   logger,
    53  			}
    54  		})
    55  
    56  		it.After(func() {
    57  			h.AssertNil(t, h.DockerRmi(docker, repo, "hashicorp/http-echo"))
    58  		})
    59  
    60  		when("there is no exposed or provided ports", func() {
    61  			it.Before(func() {
    62  				h.CreateImageOnLocal(
    63  					t,
    64  					docker,
    65  					repo,
    66  					"FROM hashicorp/http-echo\nCMD [\"-text=hello world\"]",
    67  				)
    68  			})
    69  
    70  			it("runs an image", func() {
    71  				assertOnRunningContainer(t, subject, nil, &errBuf, docker, func() bool {
    72  					return strings.Contains(errBuf.String(), "listening")
    73  				})
    74  			})
    75  		})
    76  
    77  		when("a port is exposed", func() {
    78  			var containerPort string
    79  
    80  			it.Before(func() {
    81  				containerPort, err = h.GetFreePort()
    82  				h.AssertNil(t, err)
    83  				h.CreateImageOnLocal(
    84  					t,
    85  					docker,
    86  					repo,
    87  					fmt.Sprintf(
    88  						"FROM hashicorp/http-echo\nEXPOSE %s\nCMD [\"-listen=:%s\",\"-text=hello world\"]",
    89  						containerPort,
    90  						containerPort,
    91  					),
    92  				)
    93  			})
    94  
    95  			it("gets exposed ports from the image", func() {
    96  				assertOnRunningContainer(t, subject, nil, &errBuf, docker, func() bool {
    97  					resp, err := http.Get("http://localhost:" + containerPort)
    98  					if err != nil {
    99  						t.Log(err)
   100  						return false
   101  					}
   102  					defer resp.Body.Close()
   103  
   104  					body, err := ioutil.ReadAll(resp.Body)
   105  					if err != nil {
   106  						t.Log(err)
   107  						return false
   108  					}
   109  					if !strings.Contains(string(body), "hello world") {
   110  						t.Log("got response body:", string(body))
   111  						return false
   112  					}
   113  					return true
   114  				})
   115  			})
   116  		})
   117  
   118  		when("custom ports bindings are defined", func() {
   119  			var (
   120  				containerPort string
   121  				err           error
   122  			)
   123  
   124  			it.Before(func() {
   125  				containerPort, err = h.GetFreePort()
   126  				h.AssertNil(t, err)
   127  				h.CreateImageOnLocal(
   128  					t,
   129  					docker,
   130  					repo,
   131  					fmt.Sprintf("FROM hashicorp/http-echo\nCMD [\"-listen=:%s\",\"-text=hello world\"]", containerPort),
   132  				)
   133  			})
   134  
   135  			it("binds simple ports from localhost to the container on the same port", func() {
   136  				assertOnRunningContainer(t, subject, []string{containerPort}, &errBuf, docker, func() bool {
   137  					resp, err := http.Get("http://localhost:" + containerPort)
   138  					if err != nil {
   139  						t.Log(err)
   140  						return false
   141  					}
   142  					defer resp.Body.Close()
   143  
   144  					body, err := ioutil.ReadAll(resp.Body)
   145  					if err != nil {
   146  						t.Log(err)
   147  						return false
   148  					}
   149  					if !strings.Contains(string(body), "hello world") {
   150  						t.Log("got response body:", string(body))
   151  						return false
   152  					}
   153  					return true
   154  				})
   155  			})
   156  
   157  			it("binds each port to the container", func() {
   158  				hostPort, err := h.GetFreePort()
   159  				h.AssertNil(t, err)
   160  
   161  				assertOnRunningContainer(
   162  					t,
   163  					subject,
   164  					[]string{fmt.Sprintf("127.0.0.1:%s:%s/tcp", hostPort, containerPort)},
   165  					&errBuf,
   166  					docker,
   167  					func() bool {
   168  						resp, err := http.Get("http://localhost:" + hostPort)
   169  						if err != nil {
   170  							t.Log(err)
   171  							return false
   172  						}
   173  						defer resp.Body.Close()
   174  
   175  						body, err := ioutil.ReadAll(resp.Body)
   176  						if err != nil {
   177  							t.Log(err)
   178  							return false
   179  						}
   180  						if !strings.Contains(string(body), "hello world") {
   181  							t.Log("got response body:", string(body))
   182  							return false
   183  						}
   184  						return true
   185  					})
   186  			})
   187  		})
   188  	})
   189  }
   190  
   191  func assertOnRunningContainer(t *testing.T, subject *app.Image, port []string, errBuf *bytes.Buffer, docker *client.Client, testFunc func() bool) {
   192  	ctx, cancel := context.WithCancel(context.Background())
   193  
   194  	done := make(chan error)
   195  	go func() {
   196  		done <- subject.Run(ctx, docker, port)
   197  	}()
   198  
   199  	ticker := time.NewTicker(time.Second)
   200  	defer ticker.Stop()
   201  	timer := time.NewTimer(time.Second * 5)
   202  	defer timer.Stop()
   203  
   204  loop:
   205  	for {
   206  		select {
   207  		case <-timer.C:
   208  			cancel()
   209  			t.Fatalf("timed out: %s", errBuf.String())
   210  		case <-ticker.C:
   211  			if testFunc() {
   212  				break loop
   213  			}
   214  		}
   215  	}
   216  	cancel()
   217  	if err := <-done; !strings.Contains(err.Error(), context.Canceled.Error()) {
   218  		t.Fatalf("expected canceled context, failed with a different error: %s", err)
   219  	}
   220  }