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 }