github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/api/cloudcontroller/ccv3/buildpack.go (about) 1 package ccv3 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 8 "code.cloudfoundry.org/cli/api/cloudcontroller" 9 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/uploads" 12 "code.cloudfoundry.org/cli/types" 13 ) 14 15 // Buildpack represents a Cloud Controller V3 buildpack. 16 type Buildpack struct { 17 // Enabled is true when the buildpack can be used for staging. 18 Enabled types.NullBool 19 // Filename is the uploaded filename of the buildpack. 20 Filename string 21 // GUID is the unique identifier for the buildpack. 22 GUID string 23 // Locked is true when the buildpack cannot be updated. 24 Locked types.NullBool 25 // Name is the name of the buildpack. To be used by app buildpack field. 26 // (only alphanumeric characters) 27 Name string 28 // Position is the order in which the buildpacks are checked during buildpack 29 // auto-detection. 30 Position types.NullInt 31 // Stack is the name of the stack that the buildpack will use. 32 Stack string 33 // State is the current state of the buildpack. 34 State string 35 // Links are links to related resources. 36 Links APILinks 37 // Metadata is used for custom tagging of API resources 38 Metadata *Metadata 39 } 40 41 // MarshalJSON converts a Package into a Cloud Controller Package. 42 func (buildpack Buildpack) MarshalJSON() ([]byte, error) { 43 ccBuildpack := struct { 44 Name string `json:"name,omitempty"` 45 Stack string `json:"stack,omitempty"` 46 Position *int `json:"position,omitempty"` 47 Enabled *bool `json:"enabled,omitempty"` 48 Locked *bool `json:"locked,omitempty"` 49 Metadata *Metadata `json:"metadata,omitempty"` 50 }{ 51 Name: buildpack.Name, 52 Stack: buildpack.Stack, 53 } 54 55 if buildpack.Position.IsSet { 56 ccBuildpack.Position = &buildpack.Position.Value 57 } 58 if buildpack.Enabled.IsSet { 59 ccBuildpack.Enabled = &buildpack.Enabled.Value 60 } 61 if buildpack.Locked.IsSet { 62 ccBuildpack.Locked = &buildpack.Locked.Value 63 } 64 65 return json.Marshal(ccBuildpack) 66 } 67 68 func (buildpack *Buildpack) UnmarshalJSON(data []byte) error { 69 var ccBuildpack struct { 70 GUID string `json:"guid,omitempty"` 71 Links APILinks `json:"links,omitempty"` 72 Name string `json:"name,omitempty"` 73 Filename string `json:"filename,omitempty"` 74 Stack string `json:"stack,omitempty"` 75 State string `json:"state,omitempty"` 76 Enabled types.NullBool `json:"enabled"` 77 Locked types.NullBool `json:"locked"` 78 Position types.NullInt `json:"position"` 79 Metadata *Metadata `json:"metadata"` 80 } 81 82 err := cloudcontroller.DecodeJSON(data, &ccBuildpack) 83 if err != nil { 84 return err 85 } 86 87 buildpack.Enabled = ccBuildpack.Enabled 88 buildpack.Filename = ccBuildpack.Filename 89 buildpack.GUID = ccBuildpack.GUID 90 buildpack.Locked = ccBuildpack.Locked 91 buildpack.Name = ccBuildpack.Name 92 buildpack.Position = ccBuildpack.Position 93 buildpack.Stack = ccBuildpack.Stack 94 buildpack.State = ccBuildpack.State 95 buildpack.Links = ccBuildpack.Links 96 buildpack.Metadata = ccBuildpack.Metadata 97 98 return nil 99 } 100 101 // CreateBuildpack creates a buildpack with the given settings, Type and the 102 // ApplicationRelationship must be set. 103 func (client *Client) CreateBuildpack(bp Buildpack) (Buildpack, Warnings, error) { 104 bodyBytes, err := json.Marshal(bp) 105 if err != nil { 106 return Buildpack{}, nil, err 107 } 108 109 request, err := client.newHTTPRequest(requestOptions{ 110 RequestName: internal.PostBuildpackRequest, 111 Body: bytes.NewReader(bodyBytes), 112 }) 113 if err != nil { 114 return Buildpack{}, nil, err 115 } 116 117 var responseBuildpack Buildpack 118 response := cloudcontroller.Response{ 119 DecodeJSONResponseInto: &responseBuildpack, 120 } 121 err = client.connection.Make(request, &response) 122 123 return responseBuildpack, response.Warnings, err 124 } 125 126 // DeleteBuildpack deletes the buildpack with the provided guid. 127 func (client Client) DeleteBuildpack(buildpackGUID string) (JobURL, Warnings, error) { 128 request, err := client.newHTTPRequest(requestOptions{ 129 RequestName: internal.DeleteBuildpackRequest, 130 URIParams: map[string]string{ 131 "buildpack_guid": buildpackGUID, 132 }, 133 }) 134 if err != nil { 135 return "", nil, err 136 } 137 138 response := cloudcontroller.Response{} 139 err = client.connection.Make(request, &response) 140 141 return JobURL(response.ResourceLocationURL), response.Warnings, err 142 } 143 144 // GetBuildpacks lists buildpacks with optional filters. 145 func (client *Client) GetBuildpacks(query ...Query) ([]Buildpack, Warnings, error) { 146 request, err := client.newHTTPRequest(requestOptions{ 147 RequestName: internal.GetBuildpacksRequest, 148 Query: query, 149 }) 150 if err != nil { 151 return nil, nil, err 152 } 153 154 var fullBuildpacksList []Buildpack 155 warnings, err := client.paginate(request, Buildpack{}, func(item interface{}) error { 156 if buildpack, ok := item.(Buildpack); ok { 157 fullBuildpacksList = append(fullBuildpacksList, buildpack) 158 } else { 159 return ccerror.UnknownObjectInListError{ 160 Expected: Buildpack{}, 161 Unexpected: item, 162 } 163 } 164 return nil 165 }) 166 167 return fullBuildpacksList, warnings, err 168 } 169 170 func (client Client) UpdateBuildpack(buildpack Buildpack) (Buildpack, Warnings, error) { 171 bodyBytes, err := json.Marshal(buildpack) 172 if err != nil { 173 return Buildpack{}, nil, err 174 } 175 176 request, err := client.newHTTPRequest(requestOptions{ 177 RequestName: internal.PatchBuildpackRequest, 178 Body: bytes.NewReader(bodyBytes), 179 URIParams: map[string]string{"buildpack_guid": buildpack.GUID}, 180 }) 181 if err != nil { 182 return Buildpack{}, nil, err 183 } 184 185 var responseBuildpack Buildpack 186 response := cloudcontroller.Response{ 187 DecodeJSONResponseInto: &responseBuildpack, 188 } 189 err = client.connection.Make(request, &response) 190 191 return responseBuildpack, response.Warnings, err 192 } 193 194 // UploadBuildpack uploads the contents of a buildpack zip to the server. 195 func (client *Client) UploadBuildpack(buildpackGUID string, buildpackPath string, buildpack io.Reader, buildpackLength int64) (JobURL, Warnings, error) { 196 197 contentLength, err := uploads.CalculateRequestSize(buildpackLength, buildpackPath, "bits") 198 if err != nil { 199 return "", nil, err 200 } 201 202 contentType, body, writeErrors := uploads.CreateMultipartBodyAndHeader(buildpack, buildpackPath, "bits") 203 204 request, err := client.newHTTPRequest(requestOptions{ 205 RequestName: internal.PostBuildpackBitsRequest, 206 URIParams: internal.Params{"buildpack_guid": buildpackGUID}, 207 Body: body, 208 }) 209 210 if err != nil { 211 return "", nil, err 212 } 213 214 request.ContentLength = contentLength 215 request.Header.Set("Content-Type", contentType) 216 217 jobURL, warnings, err := client.uploadBuildpackAsynchronously(request, writeErrors) 218 if err != nil { 219 return "", warnings, err 220 } 221 return jobURL, warnings, nil 222 } 223 224 func (client *Client) uploadBuildpackAsynchronously(request *cloudcontroller.Request, writeErrors <-chan error) (JobURL, Warnings, error) { 225 226 var buildpack Buildpack 227 response := cloudcontroller.Response{ 228 DecodeJSONResponseInto: &buildpack, 229 } 230 231 httpErrors := make(chan error) 232 233 go func() { 234 defer close(httpErrors) 235 236 err := client.connection.Make(request, &response) 237 if err != nil { 238 httpErrors <- err 239 } 240 }() 241 242 // The following section makes the following assumptions: 243 // 1) If an error occurs during file reading, an EOF is sent to the request 244 // object. Thus ending the request transfer. 245 // 2) If an error occurs during request transfer, an EOF is sent to the pipe. 246 // Thus ending the writing routine. 247 var firstError error 248 var writeClosed, httpClosed bool 249 250 for { 251 select { 252 case writeErr, ok := <-writeErrors: 253 if !ok { 254 writeClosed = true 255 break // for select 256 } 257 if firstError == nil { 258 firstError = writeErr 259 } 260 case httpErr, ok := <-httpErrors: 261 if !ok { 262 httpClosed = true 263 break // for select 264 } 265 if firstError == nil { 266 firstError = httpErr 267 } 268 } 269 270 if writeClosed && httpClosed { 271 break // for for 272 } 273 } 274 return JobURL(response.ResourceLocationURL), response.Warnings, firstError 275 }