44 "bytes"
55 "context"
66 "fmt"
7+ "strings"
78 "sync"
89 "time"
910
@@ -222,6 +223,7 @@ type CreatePageBlobInput struct {
222223 BlobURL string
223224 ImageURL string
224225 StorageAccountName string
226+ BootstrapIgnData []byte
225227 ImageLength int64
226228 StorageAccountKeys []armstorage.AccountKey
227229 CloudConfiguration cloud.Configuration
@@ -234,13 +236,13 @@ type CreatePageBlobOutput struct {
234236}
235237
236238// CreatePageBlob creates a blob and uploads a file from a URL to it.
237- func CreatePageBlob (ctx context.Context , in * CreatePageBlobInput ) (* CreatePageBlobOutput , error ) {
239+ func CreatePageBlob (ctx context.Context , in * CreatePageBlobInput ) (string , error ) {
238240 logrus .Debugf ("Getting page blob credentials" )
239241
240242 // XXX: Should try all of them until one is successful
241243 sharedKeyCredential , err := azblob .NewSharedKeyCredential (in .StorageAccountName , * in .StorageAccountKeys [0 ].Value )
242244 if err != nil {
243- return nil , fmt .Errorf ("failed to get shared credentials for storage account: %w" , err )
245+ return "" , fmt .Errorf ("failed to get shared credentials for storage account: %w" , err )
244246 }
245247
246248 logrus .Debugf ("Getting page blob client" )
@@ -254,30 +256,88 @@ func CreatePageBlob(ctx context.Context, in *CreatePageBlobInput) (*CreatePageBl
254256 },
255257 )
256258 if err != nil {
257- return nil , fmt .Errorf ("failed to get page blob client: %w" , err )
259+ return "" , fmt .Errorf ("failed to get page blob client: %w" , err )
258260 }
259261
260- // This is used in terraform, not sure if it matters
261- metadata := make (map [string ]* string , 1 )
262- metadata ["source_uri" ] = to .Ptr (in .ImageURL )
262+ logrus .Debugf ("Creating Page blob and uploading image to it" )
263+ if in .ImageURL == "" {
264+ _ , err = pageBlobClient .Create (ctx , in .ImageLength , nil )
265+ if err != nil {
266+ return "" , fmt .Errorf ("failed to create page blob with image contents: %w" , err )
267+ }
268+ // This image (example: ignition shim) needs to be uploaded from a local file.
269+ err = doUploadPages (ctx , pageBlobClient , in .BootstrapIgnData , in .ImageLength )
270+ if err != nil {
271+ return "" , fmt .Errorf ("failed to upload page blob image contents: %w" , err )
272+ }
273+ } else {
274+ // This is used in terraform, not sure if it matters
275+ metadata := map [string ]* string {
276+ "source_uri" : to .Ptr (in .ImageURL ),
277+ }
278+
279+ _ , err = pageBlobClient .Create (ctx , in .ImageLength , & pageblob.CreateOptions {
280+ Metadata : metadata ,
281+ })
282+ if err != nil {
283+ return "" , fmt .Errorf ("failed to create page blob with image URL: %w" , err )
284+ }
263285
264- logrus .Debugf ("Creating blob" )
265- _ , err = pageBlobClient .Create (ctx , in .ImageLength , & pageblob.CreateOptions {
266- Metadata : metadata ,
267- })
268- if err != nil {
269- return nil , fmt .Errorf ("failed to create blob: %w" , err )
286+ err = doUploadPagesFromURL (ctx , pageBlobClient , in .ImageURL , in .ImageLength )
287+ if err != nil {
288+ return "" , fmt .Errorf ("failed to upload page blob image from URL %s: %w" , in .ImageURL , err )
289+ }
270290 }
271291
272- logrus . Debugf ( "Uploading to blob" )
273- err = doUploadPagesFromURL ( ctx , pageBlobClient , in . ImageURL , in . ImageLength )
292+ // Is this addition OK for when CreatePageBlob() is called from InfraReady( )
293+ sasURL , err := pageBlobClient . GetSASURL (sas. BlobPermissions { Read : true }, time . Now (). Add ( time . Minute * 60 ), & blob. GetSASURLOptions {} )
274294 if err != nil {
275- return nil , fmt .Errorf ("failed to upload blob image %s : %w" , in . ImageURL , err )
295+ return "" , fmt .Errorf ("failed to get Page Blob SAS URL : %w" , err )
276296 }
277- return & CreatePageBlobOutput {
278- PageBlobClient : pageBlobClient ,
279- SharedKeyCredential : sharedKeyCredential ,
280- }, nil
297+ return sasURL , nil
298+ }
299+
300+ func doUploadPages (ctx context.Context , pageBlobClient * pageblob.Client , imageData []byte , imageLength int64 ) error {
301+ logrus .Debugf ("Uploading to Page Blob with Image of length :%d" , imageLength )
302+
303+ // Page blobs file size must be a multiple of 512, hence a little padding is needed to push the file.
304+ // imageLength has already been adjusted to the next highest size divisible by 512.
305+ // So, here we are padding the image to match this size.
306+ // Bootstrap Ignition is a json file. For parsing of this file to succeed with the padding, the
307+ // file needs to end with a }.
308+ logrus .Debugf ("Original Image length: %d" , int64 (len (imageData )))
309+ padding := imageLength - int64 (len (imageData ))
310+ paddingString := strings .Repeat (" " , int (padding )) + string (imageData [len (imageData )- 1 ])
311+ imageData = append (imageData [0 :len (imageData )- 1 ], paddingString ... )
312+ logrus .Debugf ("New Image length (after padding): %d" , int64 (len (imageData )))
313+
314+ pageSize := int64 (1024 * 1024 * 4 )
315+ newOffset := int64 (0 )
316+ remainingImageLength := imageLength
317+
318+ for remainingImageLength > 0 {
319+ if remainingImageLength < pageSize {
320+ pageSize = remainingImageLength
321+ }
322+
323+ logrus .Debugf ("Uploading pages with Offset :%d and Count :%d" , newOffset , pageSize )
324+
325+ _ , err := pageBlobClient .UploadPages (
326+ ctx ,
327+ streaming .NopCloser (bytes .NewReader (imageData )),
328+ blob.HTTPRange {
329+ Offset : newOffset ,
330+ Count : pageSize ,
331+ },
332+ nil )
333+ if err != nil {
334+ return fmt .Errorf ("failed uploading Image to page blob: %w" , err )
335+ }
336+ newOffset += pageSize
337+ remainingImageLength -= pageSize
338+ logrus .Debugf ("newOffset :%d and remainingImageLength :%d" , newOffset , remainingImageLength )
339+ }
340+ return nil
281341}
282342
283343func doUploadPagesFromURL (ctx context.Context , pageBlobClient * pageblob.Client , imageURL string , imageLength int64 ) error {
0 commit comments