github.com/bshelton229/agent@v3.5.4+incompatible/agent/form_uploader.go (about)

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	_ "crypto/sha512" // import sha512 to make sha512 ssl certs work
     6  	"fmt"
     7  	"io"
     8  	"mime/multipart"
     9  	"net/http"
    10  	"net/http/httputil"
    11  	"regexp"
    12  	// "net/http/httputil"
    13  	"errors"
    14  	"net/url"
    15  	"os"
    16  
    17  	"github.com/buildkite/agent/api"
    18  	"github.com/buildkite/agent/logger"
    19  )
    20  
    21  var ArtifactPathVariableRegex = regexp.MustCompile("\\$\\{artifact\\:path\\}")
    22  
    23  type FormUploader struct {
    24  	// Whether or not HTTP calls shoud be debugged
    25  	DebugHTTP bool
    26  }
    27  
    28  func (u *FormUploader) Setup(destination string, debugHTTP bool) error {
    29  	u.DebugHTTP = debugHTTP
    30  
    31  	return nil
    32  }
    33  
    34  // The FormUploader doens't specify a URL, as one is provided by Buildkite
    35  // after uploading
    36  func (u *FormUploader) URL(artifact *api.Artifact) string {
    37  	return ""
    38  }
    39  
    40  func (u *FormUploader) Upload(artifact *api.Artifact) error {
    41  	// Create a HTTP request for uploading the file
    42  	request, err := createUploadRequest(artifact)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	// Create the client
    48  	client := &http.Client{}
    49  
    50  	// Perform the request
    51  	logger.Debug("%s %s", request.Method, request.URL)
    52  	response, err := client.Do(request)
    53  
    54  	// Check for errors
    55  	if err != nil {
    56  		return err
    57  	} else {
    58  		// Be sure to close the response body at the end of
    59  		// this function
    60  		defer response.Body.Close()
    61  
    62  		if u.DebugHTTP {
    63  			responseDump, err := httputil.DumpResponse(response, true)
    64  			logger.Debug("\nERR: %s\n%s", err, string(responseDump))
    65  		}
    66  
    67  		if response.StatusCode/100 != 2 {
    68  			body := &bytes.Buffer{}
    69  			_, err := body.ReadFrom(response.Body)
    70  			if err != nil {
    71  				return err
    72  			}
    73  
    74  			// Return a custom error with the response body from the page
    75  			message := fmt.Sprintf("%s (%d)", body, response.StatusCode)
    76  			return errors.New(message)
    77  		}
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  // Creates a new file upload http request with optional extra params
    84  func createUploadRequest(artifact *api.Artifact) (*http.Request, error) {
    85  	file, err := os.Open(artifact.AbsolutePath)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	defer file.Close()
    90  
    91  	body := &bytes.Buffer{}
    92  	writer := multipart.NewWriter(body)
    93  
    94  	// Set the post data for the request
    95  	for key, val := range artifact.UploadInstructions.Data {
    96  		// Replace the magical ${artifact:path} variable with the
    97  		// artifact's path
    98  		newVal := ArtifactPathVariableRegex.ReplaceAllLiteralString(val, artifact.Path)
    99  
   100  		// Write the new value to the form
   101  		err = writer.WriteField(key, newVal)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	}
   106  
   107  	// It's important that we add the form field last because when
   108  	// uploading to an S3 form, they are really nit-picky about the field
   109  	// order, and the file needs to be the last one other it doesn't work.
   110  	part, err := writer.CreateFormFile(artifact.UploadInstructions.Action.FileInput, artifact.Path)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	_, err = io.Copy(part, file)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	err = writer.Close()
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	// Create the URL that we'll send data to
   126  	uri, err := url.Parse(artifact.UploadInstructions.Action.URL)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	uri.Path = artifact.UploadInstructions.Action.Path
   132  
   133  	// Create the request
   134  	req, err := http.NewRequest(artifact.UploadInstructions.Action.Method, uri.String(), body)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	// Finally add the multipart content type to the request
   140  	req.Header.Add("Content-Type", writer.FormDataContentType())
   141  
   142  	return req, nil
   143  }