github.com/asifdxtreme/cli@v6.1.3-0.20150123051144-9ead8700b4ae+incompatible/cf/api/application_bits/application_bits.go (about)

     1  package application_bits
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"mime/multipart"
     9  	"net/textproto"
    10  	"os"
    11  	"time"
    12  
    13  	"github.com/cloudfoundry/cli/cf/api/resources"
    14  	"github.com/cloudfoundry/cli/cf/configuration/core_config"
    15  	"github.com/cloudfoundry/cli/cf/errors"
    16  	. "github.com/cloudfoundry/cli/cf/i18n"
    17  	"github.com/cloudfoundry/cli/cf/net"
    18  	"github.com/cloudfoundry/gofileutils/fileutils"
    19  )
    20  
    21  const (
    22  	DefaultAppUploadBitsTimeout = 15 * time.Minute
    23  )
    24  
    25  type ApplicationBitsRepository interface {
    26  	GetApplicationFiles(appFilesRequest []resources.AppFileResource) ([]resources.AppFileResource, error)
    27  	UploadBits(appGuid string, zipFile *os.File, presentFiles []resources.AppFileResource) (apiErr error)
    28  }
    29  
    30  type CloudControllerApplicationBitsRepository struct {
    31  	config  core_config.Reader
    32  	gateway net.Gateway
    33  }
    34  
    35  func NewCloudControllerApplicationBitsRepository(config core_config.Reader, gateway net.Gateway) (repo CloudControllerApplicationBitsRepository) {
    36  	repo.config = config
    37  	repo.gateway = gateway
    38  	return
    39  }
    40  
    41  func (repo CloudControllerApplicationBitsRepository) UploadBits(appGuid string, zipFile *os.File, presentFiles []resources.AppFileResource) (apiErr error) {
    42  	apiUrl := fmt.Sprintf("/v2/apps/%s/bits", appGuid)
    43  	fileutils.TempFile("requests", func(requestFile *os.File, err error) {
    44  		if err != nil {
    45  			apiErr = errors.NewWithError(T("Error creating tmp file: {{.Err}}", map[string]interface{}{"Err": err}), err)
    46  			return
    47  		}
    48  
    49  		// json.Marshal represents a nil value as "null" instead of an empty slice "[]"
    50  		if presentFiles == nil {
    51  			presentFiles = []resources.AppFileResource{}
    52  		}
    53  
    54  		presentFilesJSON, err := json.Marshal(presentFiles)
    55  		if err != nil {
    56  			apiErr = errors.NewWithError(T("Error marshaling JSON"), err)
    57  			return
    58  		}
    59  
    60  		boundary, err := repo.writeUploadBody(zipFile, requestFile, presentFilesJSON)
    61  		if err != nil {
    62  			apiErr = errors.NewWithError(T("Error writing to tmp file: {{.Err}}", map[string]interface{}{"Err": err}), err)
    63  			return
    64  		}
    65  
    66  		var request *net.Request
    67  		request, apiErr = repo.gateway.NewRequestForFile("PUT", repo.config.ApiEndpoint()+apiUrl, repo.config.AccessToken(), requestFile)
    68  		if apiErr != nil {
    69  			return
    70  		}
    71  
    72  		contentType := fmt.Sprintf("multipart/form-data; boundary=%s", boundary)
    73  		request.HttpReq.Header.Set("Content-Type", contentType)
    74  
    75  		response := &resources.Resource{}
    76  		_, apiErr = repo.gateway.PerformPollingRequestForJSONResponse(repo.config.ApiEndpoint(), request, response, DefaultAppUploadBitsTimeout)
    77  		if apiErr != nil {
    78  			return
    79  		}
    80  	})
    81  
    82  	return
    83  }
    84  
    85  func (repo CloudControllerApplicationBitsRepository) GetApplicationFiles(appFilesToCheck []resources.AppFileResource) ([]resources.AppFileResource, error) {
    86  	allAppFilesJson, err := json.Marshal(appFilesToCheck)
    87  	if err != nil {
    88  		apiErr := errors.NewWithError(T("Failed to create json for resource_match request"), err)
    89  		return nil, apiErr
    90  	}
    91  
    92  	presentFiles := []resources.AppFileResource{}
    93  	apiErr := repo.gateway.UpdateResourceSync(
    94  		repo.config.ApiEndpoint(),
    95  		"/v2/resource_match",
    96  		bytes.NewReader(allAppFilesJson),
    97  		&presentFiles)
    98  
    99  	if apiErr != nil {
   100  		return nil, apiErr
   101  	}
   102  
   103  	return presentFiles, nil
   104  }
   105  
   106  func (repo CloudControllerApplicationBitsRepository) writeUploadBody(zipFile *os.File, body *os.File, presentResourcesJson []byte) (boundary string, err error) {
   107  	writer := multipart.NewWriter(body)
   108  	defer writer.Close()
   109  
   110  	boundary = writer.Boundary()
   111  
   112  	part, err := writer.CreateFormField("resources")
   113  	if err != nil {
   114  		return
   115  	}
   116  
   117  	_, err = io.Copy(part, bytes.NewBuffer(presentResourcesJson))
   118  	if err != nil {
   119  		return
   120  	}
   121  
   122  	if zipFile != nil {
   123  		zipStats, zipErr := zipFile.Stat()
   124  		if zipErr != nil {
   125  			return
   126  		}
   127  
   128  		if zipStats.Size() == 0 {
   129  			return
   130  		}
   131  
   132  		part, zipErr = createZipPartWriter(zipStats, writer)
   133  		if zipErr != nil {
   134  			return
   135  		}
   136  
   137  		_, zipErr = io.Copy(part, zipFile)
   138  		if zipErr != nil {
   139  			return
   140  		}
   141  	}
   142  
   143  	return
   144  }
   145  
   146  func createZipPartWriter(zipStats os.FileInfo, writer *multipart.Writer) (io.Writer, error) {
   147  	h := make(textproto.MIMEHeader)
   148  	h.Set("Content-Disposition", `form-data; name="application"; filename="application.zip"`)
   149  	h.Set("Content-Type", "application/zip")
   150  	h.Set("Content-Length", fmt.Sprintf("%d", zipStats.Size()))
   151  	h.Set("Content-Transfer-Encoding", "binary")
   152  	return writer.CreatePart(h)
   153  }