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