github.com/chenbh/concourse/v6@v6.4.2/fly/integration/execute_with_outputs_test.go (about)

     1  package integration_test
     2  
     3  import (
     4  	"archive/tar"
     5  	"compress/gzip"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"time"
    14  
    15  	"github.com/chenbh/concourse/v6/atc"
    16  	"github.com/chenbh/concourse/v6/atc/event"
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  	"github.com/onsi/gomega/gbytes"
    20  	"github.com/onsi/gomega/gexec"
    21  	"github.com/onsi/gomega/ghttp"
    22  	"github.com/vito/go-sse/sse"
    23  )
    24  
    25  var _ = Describe("Fly CLI", func() {
    26  	var tmpdir string
    27  	var buildDir string
    28  	var taskConfigPath string
    29  
    30  	var streaming chan struct{}
    31  	var events chan atc.Event
    32  	var outputDir string
    33  
    34  	var expectedPlan atc.Plan
    35  	var workerArtifact = atc.WorkerArtifact{
    36  		ID:   125,
    37  		Name: "some-dir",
    38  	}
    39  
    40  	BeforeEach(func() {
    41  		var err error
    42  		tmpdir, err = ioutil.TempDir("", "fly-build-dir")
    43  		Expect(err).NotTo(HaveOccurred())
    44  
    45  		outputDir, err = ioutil.TempDir("", "fly-task-output")
    46  		Expect(err).NotTo(HaveOccurred())
    47  
    48  		buildDir = filepath.Join(tmpdir, "fixture")
    49  
    50  		err = os.Mkdir(buildDir, 0755)
    51  		Expect(err).NotTo(HaveOccurred())
    52  
    53  		taskConfigPath = filepath.Join(buildDir, "task.yml")
    54  
    55  		err = ioutil.WriteFile(
    56  			taskConfigPath,
    57  			[]byte(`---
    58  platform: some-platform
    59  
    60  image_resource:
    61    type: registry-image
    62    source:
    63      repository: ubuntu
    64  
    65  inputs:
    66  - name: fixture
    67  
    68  outputs:
    69  - name: some-dir
    70  
    71  params:
    72    FOO: bar
    73    BAZ: buzz
    74    X: 1
    75  
    76  run:
    77    path: /bin/sh
    78    args:
    79      - -c
    80      - echo some-content > some-dir/a-file
    81  
    82  `),
    83  			0644,
    84  		)
    85  		Expect(err).NotTo(HaveOccurred())
    86  
    87  		streaming = make(chan struct{})
    88  		events = make(chan atc.Event)
    89  
    90  		planFactory := atc.NewPlanFactory(0)
    91  
    92  		expectedPlan = planFactory.NewPlan(atc.EnsurePlan{
    93  			Step: planFactory.NewPlan(atc.DoPlan{
    94  				planFactory.NewPlan(atc.AggregatePlan{
    95  					planFactory.NewPlan(atc.ArtifactInputPlan{
    96  						Name:       filepath.Base(buildDir),
    97  						ArtifactID: 125,
    98  					}),
    99  				}),
   100  				planFactory.NewPlan(atc.TaskPlan{
   101  					Name: "one-off",
   102  					Config: &atc.TaskConfig{
   103  						Platform: "some-platform",
   104  						ImageResource: &atc.ImageResource{
   105  							Type: "registry-image",
   106  							Source: atc.Source{
   107  								"repository": "ubuntu",
   108  							},
   109  						},
   110  						Inputs: []atc.TaskInputConfig{
   111  							{Name: "fixture"},
   112  						},
   113  						Outputs: []atc.TaskOutputConfig{
   114  							{Name: "some-dir"},
   115  						},
   116  						Params: map[string]string{
   117  							"FOO": "bar",
   118  							"BAZ": "buzz",
   119  							"X":   "1",
   120  						},
   121  						Run: atc.TaskRunConfig{
   122  							Path: "/bin/sh",
   123  							Args: []string{"-c", "echo some-content > some-dir/a-file"},
   124  						},
   125  					},
   126  				}),
   127  			}),
   128  			Next: planFactory.NewPlan(atc.AggregatePlan{
   129  				planFactory.NewPlan(atc.ArtifactOutputPlan{
   130  					Name: "some-dir",
   131  				}),
   132  			}),
   133  		})
   134  	})
   135  
   136  	AfterEach(func() {
   137  		err := os.RemoveAll(tmpdir)
   138  		Expect(err).NotTo(HaveOccurred())
   139  
   140  		err = os.RemoveAll(outputDir)
   141  		Expect(err).NotTo(HaveOccurred())
   142  	})
   143  
   144  	JustBeforeEach(func() {
   145  		atcServer.RouteToHandler("POST", "/api/v1/teams/main/artifacts",
   146  			ghttp.CombineHandlers(
   147  				func(w http.ResponseWriter, req *http.Request) {
   148  					Expect(req.FormValue("platform")).To(Equal("some-platform"))
   149  
   150  					gr, err := gzip.NewReader(req.Body)
   151  					Expect(err).NotTo(HaveOccurred())
   152  
   153  					tr := tar.NewReader(gr)
   154  
   155  					hdr, err := tr.Next()
   156  					Expect(err).NotTo(HaveOccurred())
   157  
   158  					Expect(hdr.Name).To(Equal("./"))
   159  
   160  					hdr, err = tr.Next()
   161  					Expect(err).NotTo(HaveOccurred())
   162  
   163  					Expect(hdr.Name).To(MatchRegexp("(./)?task.yml$"))
   164  				},
   165  				ghttp.RespondWithJSONEncoded(201, workerArtifact),
   166  			),
   167  		)
   168  		atcServer.RouteToHandler("POST", "/api/v1/teams/main/builds",
   169  			ghttp.CombineHandlers(
   170  				ghttp.VerifyRequest("POST", "/api/v1/teams/main/builds"),
   171  				VerifyPlan(expectedPlan),
   172  				func(w http.ResponseWriter, r *http.Request) {
   173  					http.SetCookie(w, &http.Cookie{
   174  						Name:    "Some-Cookie",
   175  						Value:   "some-cookie-data",
   176  						Path:    "/",
   177  						Expires: time.Now().Add(1 * time.Minute),
   178  					})
   179  				},
   180  				ghttp.RespondWith(201, `{"id":128}`),
   181  			),
   182  		)
   183  		atcServer.RouteToHandler("GET", "/api/v1/builds/128/events",
   184  			ghttp.CombineHandlers(
   185  				ghttp.VerifyRequest("GET", "/api/v1/builds/128/events"),
   186  				func(w http.ResponseWriter, r *http.Request) {
   187  					flusher := w.(http.Flusher)
   188  
   189  					w.Header().Add("Content-Type", "text/event-stream; charset=utf-8")
   190  					w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
   191  					w.Header().Add("Connection", "keep-alive")
   192  
   193  					w.WriteHeader(http.StatusOK)
   194  
   195  					flusher.Flush()
   196  
   197  					close(streaming)
   198  
   199  					id := 0
   200  
   201  					for e := range events {
   202  						payload, err := json.Marshal(event.Message{Event: e})
   203  						Expect(err).NotTo(HaveOccurred())
   204  
   205  						event := sse.Event{
   206  							ID:   fmt.Sprintf("%d", id),
   207  							Name: "event",
   208  							Data: payload,
   209  						}
   210  
   211  						err = event.Write(w)
   212  						Expect(err).NotTo(HaveOccurred())
   213  
   214  						flusher.Flush()
   215  
   216  						id++
   217  					}
   218  
   219  					err := sse.Event{
   220  						Name: "end",
   221  					}.Write(w)
   222  					Expect(err).NotTo(HaveOccurred())
   223  				},
   224  			),
   225  		)
   226  		atcServer.RouteToHandler("GET", "/api/v1/builds/128/artifacts",
   227  			ghttp.RespondWithJSONEncoded(200, []atc.WorkerArtifact{workerArtifact}),
   228  		)
   229  
   230  		atcServer.RouteToHandler("GET", "/api/v1/teams/main/artifacts/125", tarHandler)
   231  	})
   232  
   233  	Context("when running with --output", func() {
   234  		Context("when the task specifies those outputs", func() {
   235  			It("downloads the tasks output to the directory provided", func() {
   236  				flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "--output", "some-dir="+outputDir)
   237  				flyCmd.Dir = buildDir
   238  
   239  				sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
   240  				Expect(err).NotTo(HaveOccurred())
   241  
   242  				// sync with after create
   243  				Eventually(streaming).Should(BeClosed())
   244  
   245  				close(events)
   246  
   247  				<-sess.Exited
   248  				Expect(sess.ExitCode()).To(Equal(0))
   249  
   250  				outputFiles, err := ioutil.ReadDir(outputDir)
   251  				Expect(err).NotTo(HaveOccurred())
   252  
   253  				Expect(outputFiles).To(HaveLen(1))
   254  				Expect(outputFiles[0].Name()).To(Equal("some-file"))
   255  
   256  				data, err := ioutil.ReadFile(filepath.Join(outputDir, outputFiles[0].Name()))
   257  				Expect(err).NotTo(HaveOccurred())
   258  				Expect(data).To(Equal([]byte("tar-contents")))
   259  			})
   260  		})
   261  
   262  		Context("when the task does not specify those outputs", func() {
   263  			It("exits 1", func() {
   264  				flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-o", "wrong-output=wrong-path")
   265  				flyCmd.Dir = buildDir
   266  
   267  				sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
   268  				Expect(err).NotTo(HaveOccurred())
   269  
   270  				Eventually(sess.Err).Should(gbytes.Say("error: unknown output 'wrong-output'"))
   271  
   272  				<-sess.Exited
   273  				Expect(sess.ExitCode()).To(Equal(1))
   274  			})
   275  		})
   276  	})
   277  })
   278  
   279  func tarHandler(w http.ResponseWriter, req *http.Request) {
   280  	gw := gzip.NewWriter(w)
   281  	tw := tar.NewWriter(gw)
   282  
   283  	tarContents := []byte("tar-contents")
   284  
   285  	err := tw.WriteHeader(&tar.Header{
   286  		Name: "some-file",
   287  		Mode: 0644,
   288  		Size: int64(len(tarContents)),
   289  	})
   290  	Expect(err).NotTo(HaveOccurred())
   291  
   292  	_, err = tw.Write(tarContents)
   293  	Expect(err).NotTo(HaveOccurred())
   294  
   295  	err = tw.Close()
   296  	Expect(err).NotTo(HaveOccurred())
   297  
   298  	err = gw.Close()
   299  	Expect(err).NotTo(HaveOccurred())
   300  }