github.com/arunkumar7540/cli@v6.45.0+incompatible/api/cloudcontroller/ccv3/droplet.go (about) 1 package ccv3 2 3 import ( 4 "bytes" 5 "code.cloudfoundry.org/cli/api/cloudcontroller" 6 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal" 9 "code.cloudfoundry.org/cli/api/cloudcontroller/uploads" 10 "encoding/json" 11 "io" 12 ) 13 14 // Droplet represents a Cloud Controller droplet's metadata. A droplet is a set of 15 // compiled bits for a given application. 16 type Droplet struct { 17 //Buildpacks are the detected buildpacks from the staging process. 18 Buildpacks []DropletBuildpack `json:"buildpacks,omitempty"` 19 // CreatedAt is the timestamp that the Cloud Controller created the droplet. 20 CreatedAt string `json:"created_at"` 21 // GUID is the unique droplet identifier. 22 GUID string `json:"guid"` 23 // Image is the Docker image name. 24 Image string `json:"image"` 25 // Stack is the root filesystem to use with the buildpack. 26 Stack string `json:"stack,omitempty"` 27 // State is the current state of the droplet. 28 State constant.DropletState `json:"state"` 29 } 30 31 // DropletBuildpack is the name and output of a buildpack used to create a 32 // droplet. 33 type DropletBuildpack struct { 34 // Name is the buildpack name. 35 Name string `json:"name"` 36 //DetectOutput is the output during buildpack detect process. 37 DetectOutput string `json:"detect_output"` 38 } 39 40 type DropletCreateRequest struct { 41 Relationships Relationships `json:"relationships"` 42 } 43 44 // CreateDroplet creates a new droplet without a package for the app with 45 // the given guid. 46 func (client *Client) CreateDroplet(appGUID string) (Droplet, Warnings, error) { 47 requestBody := DropletCreateRequest{ 48 Relationships: Relationships{ 49 constant.RelationshipTypeApplication: Relationship{GUID: appGUID}, 50 }, 51 } 52 53 body, marshalErr := json.Marshal(requestBody) 54 if marshalErr != nil { 55 return Droplet{}, nil, marshalErr 56 } 57 58 request, createRequestErr := client.newHTTPRequest(requestOptions{ 59 RequestName: internal.PostDropletRequest, 60 Body: bytes.NewReader(body), 61 }) 62 if createRequestErr != nil { 63 return Droplet{}, nil, createRequestErr 64 } 65 66 var responseDroplet Droplet 67 response := cloudcontroller.Response{ 68 DecodeJSONResponseInto: &responseDroplet, 69 } 70 err := client.connection.Make(request, &response) 71 72 return responseDroplet, response.Warnings, err 73 } 74 75 // GetApplicationDropletCurrent returns the current droplet for a given 76 // application. 77 func (client *Client) GetApplicationDropletCurrent(appGUID string) (Droplet, Warnings, error) { 78 request, err := client.newHTTPRequest(requestOptions{ 79 RequestName: internal.GetApplicationDropletCurrentRequest, 80 URIParams: map[string]string{"app_guid": appGUID}, 81 }) 82 if err != nil { 83 return Droplet{}, nil, err 84 } 85 86 var responseDroplet Droplet 87 response := cloudcontroller.Response{ 88 DecodeJSONResponseInto: &responseDroplet, 89 } 90 err = client.connection.Make(request, &response) 91 return responseDroplet, response.Warnings, err 92 } 93 94 // GetDroplet returns a droplet with the given GUID. 95 func (client *Client) GetDroplet(dropletGUID string) (Droplet, Warnings, error) { 96 request, err := client.newHTTPRequest(requestOptions{ 97 RequestName: internal.GetDropletRequest, 98 URIParams: map[string]string{"droplet_guid": dropletGUID}, 99 }) 100 if err != nil { 101 return Droplet{}, nil, err 102 } 103 104 var responseDroplet Droplet 105 response := cloudcontroller.Response{ 106 DecodeJSONResponseInto: &responseDroplet, 107 } 108 err = client.connection.Make(request, &response) 109 110 return responseDroplet, response.Warnings, err 111 } 112 113 // GetDroplets lists droplets with optional filters. 114 func (client *Client) GetDroplets(query ...Query) ([]Droplet, Warnings, error) { 115 request, err := client.newHTTPRequest(requestOptions{ 116 RequestName: internal.GetDropletsRequest, 117 Query: query, 118 }) 119 if err != nil { 120 return nil, nil, err 121 } 122 123 var responseDroplets []Droplet 124 warnings, err := client.paginate(request, Droplet{}, func(item interface{}) error { 125 if droplet, ok := item.(Droplet); ok { 126 responseDroplets = append(responseDroplets, droplet) 127 } else { 128 return ccerror.UnknownObjectInListError{ 129 Expected: Droplet{}, 130 Unexpected: item, 131 } 132 } 133 return nil 134 }) 135 136 return responseDroplets, warnings, err 137 } 138 139 // UploadDropletBits asynchronously uploads bits from a .tgz file located at dropletPath to the 140 // droplet with guid dropletGUID. It returns a job URL pointing to the asynchronous upload job. 141 func (client *Client) UploadDropletBits(dropletGUID string, dropletPath string, droplet io.Reader, dropletLength int64) (JobURL, Warnings, error) { 142 contentLength, err := uploads.CalculateRequestSize(dropletLength, dropletPath, "bits") 143 if err != nil { 144 return "", nil, err 145 } 146 147 contentType, body, writeErrors := uploads.CreateMultipartBodyAndHeader(droplet, dropletPath, "bits") 148 149 request, err := client.newHTTPRequest(requestOptions{ 150 RequestName: internal.PostDropletBitsRequest, 151 URIParams: internal.Params{"droplet_guid": dropletGUID}, 152 Body: body, 153 }) 154 if err != nil { 155 return "", nil, err 156 } 157 158 request.ContentLength = contentLength 159 request.Header.Set("Content-Type", contentType) 160 161 jobURL, warnings, err := client.uploadDropletAsynchronously(request, writeErrors) 162 if err != nil { 163 return "", warnings, err 164 } 165 166 return jobURL, warnings, nil 167 } 168 169 func (client *Client) uploadDropletAsynchronously(request *cloudcontroller.Request, writeErrors <-chan error) (JobURL, Warnings, error) { 170 var droplet Droplet 171 response := cloudcontroller.Response{ 172 DecodeJSONResponseInto: &droplet, 173 } 174 175 httpErrors := make(chan error) 176 177 go func() { 178 defer close(httpErrors) 179 180 err := client.connection.Make(request, &response) 181 if err != nil { 182 httpErrors <- err 183 } 184 }() 185 186 // The following section makes the following assumptions: 187 // 1) If an error occurs during file reading, an EOF is sent to the request 188 // object. Thus ending the request transfer. 189 // 2) If an error occurs during request transfer, an EOF is sent to the pipe. 190 // Thus ending the writing routine. 191 var firstError error 192 var writeClosed, httpClosed bool 193 194 for { 195 select { 196 case writeErr, ok := <-writeErrors: 197 if !ok { 198 writeClosed = true 199 break // for select 200 } 201 if firstError == nil { 202 firstError = writeErr 203 } 204 case httpErr, ok := <-httpErrors: 205 if !ok { 206 httpClosed = true 207 break // for select 208 } 209 if firstError == nil { 210 firstError = httpErr 211 } 212 } 213 214 if writeClosed && httpClosed { 215 break // for for 216 } 217 } 218 219 return JobURL(response.ResourceLocationURL), response.Warnings, firstError 220 }