@@ -222,6 +222,14 @@ func (gcs *GCS) applyEncryption(obj *storage.ObjectHandle) *storage.ObjectHandle
222222 return obj
223223}
224224
225+ // isNotEncryptedError checks if the error is "ResourceNotEncryptedWithCustomerEncryptionKey"
226+ func (gcs * GCS ) isNotEncryptedError (err error ) bool {
227+ if err == nil {
228+ return false
229+ }
230+ return strings .Contains (err .Error (), "ResourceNotEncryptedWithCustomerEncryptionKey" )
231+ }
232+
225233func (gcs * GCS ) Walk (ctx context.Context , gcsPath string , recursive bool , process func (ctx context.Context , r RemoteFile ) error ) error {
226234 rootPath := path .Join (gcs .Config .Path , gcsPath )
227235 return gcs .WalkAbsolute (ctx , rootPath , recursive , process )
@@ -278,13 +286,28 @@ func (gcs *GCS) GetFileReaderAbsolute(ctx context.Context, key string) (io.ReadC
278286 return nil , err
279287 }
280288 pClient := pClientObj .(* clientObject ).Client
281- obj := gcs .applyEncryption (pClient .Bucket (gcs .Config .Bucket ).Object (key ))
289+ obj := pClient .Bucket (gcs .Config .Bucket ).Object (key )
290+ // Do NOT apply encryption for object_disks files - they are not encrypted
291+ // because ClickHouse needs to read them directly without encryption key
292+ isObjectDiskPath := gcs .Config .ObjectDiskPath != "" && strings .HasPrefix (key , gcs .Config .ObjectDiskPath )
293+ if ! isObjectDiskPath {
294+ obj = gcs .applyEncryption (obj )
295+ }
282296 reader , err := obj .NewReader (ctx )
283297 if err != nil {
284- if pErr := gcs .clientPool .InvalidateObject (ctx , pClientObj ); pErr != nil {
285- log .Warn ().Msgf ("gcs.GetFileReader: gcs.clientPool.InvalidateObject error: %v " , pErr )
298+ // If the object is not encrypted but we tried to read it with encryption key,
299+ // retry without encryption (for backward compatibility with old backups)
300+ if ! isObjectDiskPath && gcs .isNotEncryptedError (err ) && gcs .encryptionKey != nil {
301+ log .Debug ().Msgf ("gcs.GetFileReader: object %s not encrypted, retrying without encryption key" , key )
302+ obj = pClient .Bucket (gcs .Config .Bucket ).Object (key )
303+ reader , err = obj .NewReader (ctx )
304+ }
305+ if err != nil {
306+ if pErr := gcs .clientPool .InvalidateObject (ctx , pClientObj ); pErr != nil {
307+ log .Warn ().Msgf ("gcs.GetFileReader: gcs.clientPool.InvalidateObject error: %v " , pErr )
308+ }
309+ return nil , err
286310 }
287- return nil , err
288311 }
289312 if pErr := gcs .clientPool .ReturnObject (ctx , pClientObj ); pErr != nil {
290313 log .Warn ().Msgf ("gcs.GetFileReader: gcs.clientPool.ReturnObject error: %v " , pErr )
@@ -307,7 +330,12 @@ func (gcs *GCS) PutFileAbsolute(ctx context.Context, key string, r io.ReadCloser
307330 return err
308331 }
309332 pClient := pClientObj .(* clientObject ).Client
310- obj := gcs .applyEncryption (pClient .Bucket (gcs .Config .Bucket ).Object (key ))
333+ obj := pClient .Bucket (gcs .Config .Bucket ).Object (key )
334+ // Do NOT apply encryption for object_disks files - they must be readable by ClickHouse
335+ // which doesn't have access to the encryption key
336+ if gcs .Config .ObjectDiskPath == "" || ! strings .HasPrefix (key , gcs .Config .ObjectDiskPath ) {
337+ obj = gcs .applyEncryption (obj )
338+ }
311339 // always retry transient errors to mitigate retry logic bugs.
312340 obj = obj .Retryer (storage .WithPolicy (storage .RetryAlways ))
313341 writer := obj .NewWriter (ctx )
@@ -340,13 +368,28 @@ func (gcs *GCS) StatFile(ctx context.Context, key string) (RemoteFile, error) {
340368}
341369
342370func (gcs * GCS ) StatFileAbsolute (ctx context.Context , key string ) (RemoteFile , error ) {
343- obj := gcs .applyEncryption (gcs .client .Bucket (gcs .Config .Bucket ).Object (key ))
371+ obj := gcs .client .Bucket (gcs .Config .Bucket ).Object (key )
372+ // Do NOT apply encryption for object_disks files - they are not encrypted
373+ // because ClickHouse needs to read them directly without encryption key
374+ isObjectDiskPath := gcs .Config .ObjectDiskPath != "" && strings .HasPrefix (key , gcs .Config .ObjectDiskPath )
375+ if ! isObjectDiskPath {
376+ obj = gcs .applyEncryption (obj )
377+ }
344378 objAttr , err := obj .Attrs (ctx )
345379 if err != nil {
346- if errors .Is (err , storage .ErrObjectNotExist ) {
347- return nil , ErrNotFound
380+ // If the object is not encrypted but we tried to read it with encryption key,
381+ // retry without encryption (for backward compatibility with old backups)
382+ if ! isObjectDiskPath && gcs .isNotEncryptedError (err ) && gcs .encryptionKey != nil {
383+ log .Debug ().Msgf ("gcs.StatFile: object %s not encrypted, retrying without encryption key" , key )
384+ obj = gcs .client .Bucket (gcs .Config .Bucket ).Object (key )
385+ objAttr , err = obj .Attrs (ctx )
386+ }
387+ if err != nil {
388+ if errors .Is (err , storage .ErrObjectNotExist ) {
389+ return nil , ErrNotFound
390+ }
391+ return nil , err
348392 }
349- return nil , err
350393 }
351394 return & gcsFile {
352395 size : objAttr .Size ,
@@ -396,7 +439,9 @@ func (gcs *GCS) CopyObject(ctx context.Context, srcSize int64, srcBucket, srcKey
396439 }
397440 pClient := pClientObj .(* clientObject ).Client
398441 src := pClient .Bucket (srcBucket ).Object (srcKey )
399- dst := gcs .applyEncryption (pClient .Bucket (gcs .Config .Bucket ).Object (dstKey ))
442+ // Do NOT apply encryption for object_disks files - they must be readable by ClickHouse
443+ // which doesn't have access to the encryption key
444+ dst := pClient .Bucket (gcs .Config .Bucket ).Object (dstKey )
400445 // always retry transient errors to mitigate retry logic bugs.
401446 dst = dst .Retryer (storage .WithPolicy (storage .RetryAlways ))
402447 attrs , err := src .Attrs (ctx )
@@ -407,8 +452,8 @@ func (gcs *GCS) CopyObject(ctx context.Context, srcSize int64, srcBucket, srcKey
407452 return 0 , err
408453 }
409454 copier := dst .CopierFrom (src )
410- // If encryption is enabled, the destination will be encrypted
411- // Note: source objects from object disks are not encrypted by clickhouse-backup
455+ // Note: source and destination objects for object disks are not encrypted
456+ // because ClickHouse needs to read them directly without encryption key
412457 if _ , err = copier .Run (ctx ); err != nil {
413458 if pErr := gcs .clientPool .InvalidateObject (ctx , pClientObj ); pErr != nil {
414459 log .Warn ().Msgf ("gcs.CopyObject: gcs.clientPool.InvalidateObject error: %+v" , pErr )
0 commit comments